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-2014 ForgeRock AS.
016 */
017
018package org.forgerock.openig.header;
019
020import java.io.IOException;
021import java.io.InputStream;
022import java.io.UnsupportedEncodingException;
023import java.util.ArrayList;
024import java.util.List;
025import java.util.ListIterator;
026
027import org.forgerock.openig.decoder.Decoder;
028import org.forgerock.openig.http.Message;
029
030/**
031 * Processes the <strong>{@code Content-Encoding}</strong> message header. For more information, see
032 * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</a> §14.11.
033 */
034public class ContentEncodingHeader implements Header {
035
036    /** The name of the header that this object represents. */
037    public static final String NAME = "Content-Encoding";
038
039    /** The content-coding(s), in the order they are applied to the entity. */
040    private final List<String> codings = new ArrayList<String>();
041
042    /**
043     * Constructs a new empty header.
044     */
045    public ContentEncodingHeader() {
046    }
047
048    /**
049     * Constructs a new header, initialized from the specified message.
050     *
051     * @param message the message to initialize the header from.
052     */
053    public ContentEncodingHeader(Message<?> message) {
054        fromMessage(message);
055    }
056
057    /**
058     * Constructs a new header, initialized from the specified string value.
059     *
060     * @param string the value to initialize the header from.
061     */
062    public ContentEncodingHeader(String string) {
063        fromString(string);
064    }
065
066    /**
067     * Returns an input stream that decodes the specified input stream, given the
068     * content-codings that are specified in the {@code codings} list.
069     *
070     * @param in the input stream to decode.
071     * @return an input stream that provides the decoded content.
072     * @throws IOException if an I/O exception occurs.
073     * @throws UnsupportedEncodingException if an unsupported content-encoding is specified.
074     */
075    public InputStream decode(InputStream in) throws IOException, UnsupportedEncodingException {
076        // decode in the reverse order that encoding was applied
077        for (ListIterator<String> i = codings.listIterator(codings.size()); i.hasPrevious();) {
078            String name = i.previous();
079            Decoder decoder = Decoder.SERVICES.get(name);
080            if (decoder == null) {
081                throw new UnsupportedEncodingException(name);
082            }
083            in = decoder.decode(in);
084        }
085        return in;
086    }
087
088    private void clear() {
089        codings.clear();
090    }
091
092    /**
093     * Returns the content-coding(s).
094     *
095     * @return The list of the content-coding(s).
096     */
097    public List<String> getCodings() {
098        return codings;
099    }
100
101    @Override
102    public String getKey() {
103        return NAME;
104    }
105
106    @Override
107    public void fromMessage(Message<?> message) {
108        if (message != null && message.getHeaders() != null) {
109            fromString(HeaderUtil.join(message.getHeaders().get(NAME), ','));
110        }
111    }
112
113    @Override
114    public void fromString(String string) {
115        clear();
116        if (string != null) {
117            codings.addAll(HeaderUtil.split(string, ','));
118        }
119    }
120
121    @Override
122    public void toMessage(Message<?> message) {
123        String value = toString();
124        if (value != null) {
125            message.getHeaders().putSingle(NAME, value);
126        }
127    }
128
129    @Override
130    public String toString() {
131        // will return null if empty
132        return HeaderUtil.join(codings, ',');
133    }
134
135    @Override
136    public boolean equals(Object o) {
137        return o == this
138                || (o instanceof ContentEncodingHeader
139                        && codings.equals(((ContentEncodingHeader) o).codings));
140    }
141
142    @Override
143    public int hashCode() {
144        return codings.hashCode();
145    }
146}