ContentEncodingHeader.java

/*
 * The contents of this file are subject to the terms of the Common Development and
 * Distribution License (the License). You may not use this file except in compliance with the
 * License.
 *
 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
 * specific language governing permission and limitations under the License.
 *
 * When distributing Covered Software, include this CDDL Header Notice in each file and include
 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2010–2011 ApexIdentity Inc.
 * Portions Copyright 2011-2015 ForgeRock AS.
 */

package org.forgerock.http.header;

import static java.util.Collections.*;
import static org.forgerock.http.header.HeaderUtil.*;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;

import org.forgerock.http.decoder.Decoder;
import org.forgerock.http.protocol.Header;
import org.forgerock.http.protocol.Message;

/**
 * Processes the <strong>{@code Content-Encoding}</strong> message header. For
 * more information, see <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC
 * 2616</a> §14.11.
 */
public class ContentEncodingHeader extends Header {
    /** The name of this header. */
    public static final String NAME = "Content-Encoding";

    /** The content coding, in the order they are applied to the entity. */
    private final List<String> codings;

    /**
     * Constructs a new empty header.
     */
    public ContentEncodingHeader() {
        this(new ArrayList<String>(1));
    }

    /**
     * Constructs a new header with the provided content encodings.
     *
     * @param codings
     *            The content encodings.
     */
    public ContentEncodingHeader(final List<String> codings) {
        this.codings = codings;
    }

    /**
     * Constructs a new header, initialized from the specified message.
     *
     * @param message
     *            The message to initialize the header from.
     * @return The parsed header.
     */
    public static ContentEncodingHeader valueOf(final Message message) {
        return new ContentEncodingHeader(parseMultiValuedHeader(message, NAME));
    }

    /**
     * Constructs a new header, initialized from the specified string value.
     *
     * @param string
     *            The value to initialize the header from.
     * @return The parsed header.
     */
    public static ContentEncodingHeader valueOf(final String string) {
        return new ContentEncodingHeader(parseMultiValuedHeader(string));
    }

    /**
     * Returns an input stream that decodes the specified input stream, given
     * the content-codings that are specified in the {@code codings} list.
     *
     * @param in
     *            the input stream to decode.
     * @return an input stream that provides the decoded content.
     * @throws IOException
     *             if an I/O exception occurs.
     * @throws UnsupportedEncodingException
     *             if an unsupported content-encoding is specified.
     */
    public InputStream decode(InputStream in) throws IOException {
        // decode in the reverse order that encoding was applied
        for (ListIterator<String> i = codings.listIterator(codings.size()); i.hasPrevious();) {
            String name = i.previous();
            Decoder decoder = Decoder.SERVICES.get(name);
            if (decoder == null) {
                throw new UnsupportedEncodingException(name);
            }
            in = decoder.decode(in);
        }
        return in;
    }

    /**
     * Returns the list of content codings.
     *
     * @return The list of content codings.
     */
    public List<String> getCodings() {
        return codings;
    }

    @Override
    public String getName() {
        return NAME;
    }

    @Override
    public List<String> getValues() {
        // will return null if empty
        final String joined = HeaderUtil.join(codings, ',');
        return joined == null ? Collections.<String>emptyList() : singletonList(joined);
    }

    static class Factory extends HeaderFactory<ContentEncodingHeader> {

        @Override
        public ContentEncodingHeader parse(String value) {
            return valueOf(value);
        }

        @Override
        public ContentEncodingHeader parse(List<String> values) {
            return valueOf(join(values, ','));
        }
    }
}