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 2010–2011 ApexIdentity Inc.
015 * Portions Copyright 2011-2015 ForgeRock AS.
016 */
017
018package org.forgerock.http.header;
019
020import static org.forgerock.http.header.HeaderUtil.*;
021
022import java.nio.charset.Charset;
023import java.util.Collections;
024import java.util.List;
025import java.util.Map;
026
027import org.forgerock.http.protocol.Header;
028import org.forgerock.http.protocol.Message;
029
030/**
031 * Processes the <strong>{@code Content-Type}</strong> message header. For more
032 * information, see <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</a>
033 * §14.17.
034 */
035public class ContentTypeHeader extends Header {
036
037    /**
038     * Constructs a new header, initialized from the specified message.
039     *
040     * @param message
041     *            The message to initialize the header from.
042     * @return The parsed header.
043     */
044    public static ContentTypeHeader valueOf(final Message message) {
045        return valueOf(parseSingleValuedHeader(message, NAME));
046    }
047
048    /**
049     * Constructs a new header, initialized from the specified string value.
050     *
051     * @param string
052     *            The value to initialize the header from.
053     * @return The parsed header.
054     */
055    public static ContentTypeHeader valueOf(final String string) {
056        List<String> parts = HeaderUtil.split(string, ';');
057        if (parts.size() > 0) {
058            String type = parts.get(0);
059            final Map<String, String> parameters = HeaderUtil.parseParameters(parts);
060            String charset = parameters.get("charset");
061            String boundary = parameters.get("boundary");
062            return new ContentTypeHeader(type, charset, boundary);
063        } else {
064            return new ContentTypeHeader(null, null, null);
065        }
066    }
067
068    /** The name of this header. */
069    public static final String NAME = "Content-Type";
070
071    /** The type/sub-type of the message. */
072    private final String type;
073
074    /** The character set used in encoding the message. */
075    private final String charset;
076
077    /** The boundary value provided in multipart messages. */
078    private final String boundary;
079
080    /**
081     * Constructs a new empty header whose type, charset, and boundary are all
082     * {@code null}.
083     */
084    public ContentTypeHeader() {
085        this(null, null, null);
086    }
087
088    /**
089     * Constructs a new header with the provided parameters.
090     *
091     * @param type
092     *            The type/sub-type of the message.
093     * @param charset
094     *            The character set used in encoding the message.
095     * @param boundary
096     *            The boundary value provided in multipart messages.
097     */
098    public ContentTypeHeader(String type, String charset, String boundary) {
099        this.type = type;
100        this.charset = charset;
101        this.boundary = boundary;
102    }
103
104    /**
105     * Returns the media type of the underlying data or {@code null} if none
106     * specified.
107     *
108     * @return The media type of the underlying data or {@code null} if none
109     *         specified.
110     */
111    public String getType() {
112        return type;
113    }
114
115    /**
116     * Returns the character set encoding used to encode the message, or
117     * {@code null} if no character set was specified.
118     *
119     * @return The character set encoding used to encode the message or
120     *         {@code null} if empty.
121     * @throws java.nio.charset.IllegalCharsetNameException
122     *             if the given charset name is illegal.
123     * @throws java.nio.charset.UnsupportedCharsetException
124     *             if no support for the named charset is available.
125     */
126    public Charset getCharset() {
127        return charset != null ? Charset.forName(charset) : null;
128    }
129
130    /**
131     * Returns the encapsulation boundary or {@code null} if none specified.
132     *
133     * @return The encapsulation boundary or {@code null} if none specified.
134     */
135    public String getBoundary() {
136        return boundary;
137    }
138
139    @Override
140    public String getName() {
141        return NAME;
142    }
143
144    @Override
145    public List<String> getValues() {
146        if (type == null || type.isEmpty()) {
147            return null;
148        }
149        StringBuilder sb = new StringBuilder();
150        sb.append(type);
151        if (charset != null) {
152            sb.append("; charset=").append(charset);
153        }
154        if (boundary != null) {
155            sb.append("; boundary=").append(boundary);
156        }
157        return sb.length() > 0 ? Collections.singletonList(sb.toString()) : Collections.<String>emptyList();
158    }
159
160    static class Factory extends AbstractSingleValuedHeaderFactory<ContentTypeHeader> {
161
162        @Override
163        public ContentTypeHeader parse(String value) {
164            return valueOf(value);
165        }
166    }
167}