001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2015 ForgeRock AS. 015 */ 016 017package org.forgerock.http.header; 018 019import static java.util.Collections.*; 020 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026import java.util.TreeMap; 027 028import org.forgerock.http.protocol.Header; 029 030/** 031 * Creates instances of {@link Header} classes from String representation. 032 * @param <H> The type of {@link Header} produced by the factory. 033 */ 034public abstract class HeaderFactory<H extends Header> { 035 /** 036 * A map of {@link Header} types to the names of the headers they 037 * implement. 038 */ 039 public static final Map<Class<? extends Header>, String> HEADER_NAMES = unmodifiableMap( 040 new HashMap<Class<? extends Header>, String>() { 041 { 042 put(AcceptApiVersionHeader.class, AcceptApiVersionHeader.NAME); 043 put(AcceptLanguageHeader.class, AcceptLanguageHeader.NAME); 044 put(ConnectionHeader.class, ConnectionHeader.NAME); 045 put(ContentApiVersionHeader.class, ContentApiVersionHeader.NAME); 046 put(ContentEncodingHeader.class, ContentEncodingHeader.NAME); 047 put(ContentLengthHeader.class, ContentLengthHeader.NAME); 048 put(ContentTypeHeader.class, ContentTypeHeader.NAME); 049 put(CookieHeader.class, CookieHeader.NAME); 050 put(LocationHeader.class, LocationHeader.NAME); 051 put(SetCookieHeader.class, SetCookieHeader.NAME); 052 put(TransactionIdHeader.class, TransactionIdHeader.NAME); 053 put(WarningHeader.class, WarningHeader.NAME); 054 } 055 }); 056 057 /** 058 * A map of header names to known {@code HeaderFactory} implementations. 059 */ 060 public static final Map<String, HeaderFactory<?>> FACTORIES = unmodifiableMap( 061 new TreeMap<String, HeaderFactory<?>>(String.CASE_INSENSITIVE_ORDER) { 062 { 063 put(AcceptApiVersionHeader.NAME, new AcceptApiVersionHeader.Factory()); 064 put(AcceptLanguageHeader.NAME, new AcceptLanguageHeader.Factory()); 065 put(ConnectionHeader.NAME, new ConnectionHeader.Factory()); 066 put(ContentApiVersionHeader.NAME, new ContentApiVersionHeader.Factory()); 067 put(ContentEncodingHeader.NAME, new ContentEncodingHeader.Factory()); 068 put(ContentLengthHeader.NAME, new ContentLengthHeader.Factory()); 069 put(ContentTypeHeader.NAME, new ContentTypeHeader.Factory()); 070 put(CookieHeader.NAME, new CookieHeader.Factory()); 071 put(LocationHeader.NAME, new LocationHeader.Factory()); 072 put(SetCookieHeader.NAME, new SetCookieHeader.Factory()); 073 put(TransactionIdHeader.NAME, new TransactionIdHeader.Factory()); 074 put(WarningHeader.NAME, new WarningHeader.Factory()); 075 if (size() != HEADER_NAMES.size()) { 076 throw new IllegalStateException("Misconfigured maps"); 077 } 078 } 079 080 @Override 081 public HeaderFactory<?> put(String key, HeaderFactory<?> value) { 082 if (!HEADER_NAMES.containsValue(key)) { 083 throw new IllegalStateException("Misconfigured maps"); 084 } 085 return super.put(key, value); 086 } 087 }); 088 /** 089 * Create a header instance from String representation, which may contain 090 * multiple values if the header supports them. 091 * 092 * @param value The string representation. 093 * @return The parsed header. 094 * @throws MalformedHeaderException When the value cannot be parsed. 095 */ 096 protected abstract H parse(String value) throws MalformedHeaderException; 097 098 /** 099 * Create a header instance from a list of String representations, each of 100 * which may in turn contain multiple values if the header supports them. 101 * @param values The string representations. 102 * @return The parsed header. 103 * @throws MalformedHeaderException When the value cannot be parsed. 104 */ 105 protected abstract H parse(List<String> values) throws MalformedHeaderException; 106 107 /** 108 * Create a header instance from a provided object representation. 109 * <p> 110 * Subclasses may wish to override this method in order to support other 111 * types of value. 112 * 113 * @param value An object representation - may be a string, a collection 114 * of strings, an array of strings, an instance of {@code Header}, 115 * or some other object type supported by the subclass. 116 * @return The parsed header. 117 * @throws MalformedHeaderException When the value cannot be parsed. 118 */ 119 public H parse(Object value) throws MalformedHeaderException { 120 try { 121 if (value instanceof Header) { 122 return parse(new ArrayList<>(((Header) value).getValues())); 123 } else if (value instanceof String) { 124 final String s = (String) value; 125 return s.isEmpty() ? null : parse(s); 126 } else if (value instanceof List) { 127 return parse((List<String>) value); 128 } else if (value.getClass().isArray()) { 129 return parse(Arrays.asList((String[]) value)); 130 } 131 } catch (RuntimeException e) { 132 throw new MalformedHeaderException("Could not parse header", e); 133 } 134 throw new IllegalArgumentException("Cannot parse header value from type: " + value); 135 } 136}