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