com.tinspx.util.net.Cookies.java Source code

Java tutorial

Introduction

Here is the source code for com.tinspx.util.net.Cookies.java

Source

/* Copyright (C) 2013-2014 Ian Teune <ian.teune@gmail.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package com.tinspx.util.net;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.net.HttpHeaders;
import com.tinspx.util.collect.CollectUtils;
import java.io.IOException;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

/**
 * Cookie utility methods.
 * <p>
 * Important note: {@link CookieManager#get(URI, Map)} will always return a Map
 * with a "Cookie" key. If there were no cookies available, the value will be an
 * empty List.
 * 
 * @author Ian
 */
public class Cookies {

    private Cookies() {
    }

    /**
     * The String "Cookie2" (without quotes) used as the Cookie2 header key
     */
    public static final String COOKIE2 = "Cookie2";

    private static final ImmutableMap<String, List<String>> EMPTY_GET_MAP = ImmutableMap.of(HttpHeaders.COOKIE,
            Collections.<String>emptyList());

    private static final CookieHandler EMPTY_HANDLER = new CookieHandler() {
        @Override
        public ImmutableMap<String, List<String>> get(URI uri, Map<String, List<String>> requestHeaders)
                throws IOException {
            return EMPTY_GET_MAP;
        }

        @Override
        public void put(URI uri, Map<String, List<String>> responseHeaders) throws IOException {
        }
    };

    /**
     * Returns a singleton empty {@link CookieHandler}.
     * {@link CookieHandler#put(URI, Map) put} has no effect and
     * {@link CookieHandler#get(URI, Map) get} always returns the empty Map.
     *
     * @return singleton empty {@link CookieHandler}
     */
    public static CookieHandler emptyCookieHandler() {
        return EMPTY_HANDLER;
    }

    private static final CookieStore EMPTY_STORE = new CookieStore() {
        @Override
        public void add(URI uri, HttpCookie cookie) {
        }

        @Override
        public List<HttpCookie> get(URI uri) {
            return Collections.emptyList();
        }

        @Override
        public List<HttpCookie> getCookies() {
            return Collections.emptyList();
        }

        @Override
        public List<URI> getURIs() {
            return Collections.emptyList();
        }

        @Override
        public boolean remove(URI uri, HttpCookie cookie) {
            return false;
        }

        @Override
        public boolean removeAll() {
            return false;
        }
    };

    private static final CookieManager EMPTY_MANAGER = new CookieManager(EMPTY_STORE, CookiePolicy.ACCEPT_NONE);

    /**
     * Returns a singleton empty/blackhole {@link CookieStore} implementation.
     * Adding and removing cookies has no effect and all get methods return an
     * empty list.
     *
     * @return a singleton empty and unmodifiable {@link CookieStore}
     */
    public static CookieStore emptyCookieStore() {
        return EMPTY_STORE;
    }

    /**
     * Returns a singleton empty {@link CookieManager} that uses
     * {@link #emptyCookieStore()} as the cookie store and
     * {@link CookiePolicy#ACCEPT_NONE} as the cookie policy.
     *
     * @return singleton empty and unmodifiable {@link CookieManager}
     */
    public static CookieManager emptyCookieManager() {
        return EMPTY_MANAGER;
    }

    /**
     * Determines if the given CookieStore is null or equal to the singleton
     * empty CookiStore instance returned by {@link #emptyCookieStore()}
     * 
     * @param store the CookieStore to check if null or empty
     * @return true if the CookieStore is null or equal to the CookieStore
     * returned from {@link #emptyCookieStore()}
     */
    public static boolean isEmptyCookieStore(@Nullable CookieStore store) {
        return store == null || store == EMPTY_STORE;
    }

    /**
     * Returns true if the given CookieManager is null, equal to the singleton
     * CookieManager instance returned from {@link #emptyCookieManager()}, or
     * the manager's CookieStore is equal to the singleton empty CookiStore
     * instance returned by {@link #emptyCookieStore()}
     *
     * @param manager the CookieManager to check
     * @return true if the manager is null or represents an empty CookieManager
     */
    public static boolean isEmptyCookieManager(@Nullable CookieManager manager) {
        return manager == null || manager == EMPTY_MANAGER || isEmptyCookieStore(manager.getCookieStore());
    }

    /**
     * Adds cookie headers to {@code headers}. {@code header} is the name of the
     * header and should be "Cookie" or "Cookie2", but that is not a
     * requirement. {@code values} are the header values associated with
     * {@code header}. These values are added to {@code headers} as a
     * <i>single</i> entry. If there are multiple values in {@code values}, they
     * are combined into a single String delimited by the String "; " (semicolon
     * followed by space). Any values associated with the key {@code header} in
     * {@code headers} are replaced.
     * <p>
     * {@code values} may be null or empty. If this is the case, no changes are
     * made to {@code headers} and false is returned. If changes were made to
     * {@code headers} true is returned.
     *
     * @see #toSingleCookieList(java.util.List)
     * @param headers the headers ListMultimap to update
     * @param header the name of the header, will be the key used to update
     * {@code headers}
     * @param values the String values associated with {@code header} that will
     * be combined into a single entry
     * @return true if {@code headers} was modified
     */
    public static boolean addFixed(ListMultimap<String, String> headers, String header,
            @Nullable List<String> values) {
        if (values == null || values.isEmpty()) {
            return false;
        }
        headers.replaceValues(header, CollectUtils.iterable(toSingleCookieString(values)));
        return true;
    }

    /**
     * Adds cookie headers to {@code headers}. {@code header} is the name of the
     * header and should be "Cookie" or "Cookie2", but that is not a
     * requirement. {@code values} are the header values associated with
     * {@code header}. These values are added to {@code headers} as a
     * single element List. If there are multiple values in {@code values}, they
     * are combined into a single String delimited by the String "; " (semicolon
     * followed by space). Any values associated with the key {@code header} in
     * {@code headers} are replaced.
     * <p>
     * {@code values} may be null or empty. If this is the case, no changes are
     * made to {@code headers} and false is returned. If changes were made to
     * {@code headers} true is returned.
     *
     * @see #toSingleCookieList(java.util.List)
     * @param headers the headers Map to update
     * @param header the name of the header, will be the key used to update
     * {@code headers}
     * @param values the String values associated with {@code header} that will
     * be combined into a single entry
     * @return true if {@code headers} was modified
     */
    public static boolean addFixed(Map<String, List<String>> headers, String header,
            @Nullable List<String> values) {
        if (values == null || values.isEmpty()) {
            return false;
        }
        headers.put(header, Lists.newArrayList(toSingleCookieString(values)));
        return true;
    }

    /**
     * Fixes the Cookie and Cookie2 headers to ensure there is only one mapping
     * for each cookie header. Changes are made directly in the provided map.
     * <p>
     * If there are multiple mappings to either the Cookie or Cookie2 header,
     * they are combined into a single String delimited by the String "; "
     * (semicolon followed by space).
     *
     * @param headers the existing headers to fix
     * @return true if {@code headers} was modified
     */
    public static boolean fixCookieHeaders(ListMultimap<String, String> headers) {
        boolean modified = false;
        List<String> h = headers.get(HttpHeaders.COOKIE);
        if (h.size() > 1) {
            headers.replaceValues(HttpHeaders.COOKIE, toSingleCookieList(h));
            modified = true;
        }
        h = headers.get(COOKIE2);
        if (h.size() > 1) {
            headers.replaceValues(COOKIE2, toSingleCookieList(h));
            modified = true;
        }
        return modified;
    }

    /**
     * Fixes the Cookie and Cookie2 headers to ensure there is only one mapping
     * for each cookie header. Changes are made directly in the provided map.
     * <p>
     * If there are multiple mappings to either the Cookie or Cookie2 header,
     * they are combined into a single String delimited by the String "; "
     * (semicolon followed by space).
     *
     * @param headers the existing headers to fix
     * @return true if {@code headers} was modified
     */
    public static boolean fixCookieHeaders(Map<String, List<String>> headers) {
        boolean modified = false;
        List<String> h = headers.get(HttpHeaders.COOKIE);
        if (h != null && h.size() > 1) {
            headers.put(HttpHeaders.COOKIE, toSingleCookieList(h));
            modified = true;
        }
        h = headers.get(COOKIE2);
        if (h != null && h.size() > 1) {
            headers.put(COOKIE2, toSingleCookieList(h));
            modified = true;
        }
        return modified;
    }

    /**
     * "fixes" the list of cookies that are mapped to either Cookie or Cookie2
     * headers. If {@code cookies} has more than one value, they are combined
     * into one String via {@link #toSingleCookieString(java.util.List)} and
     * and this combined string is set as the sole element of {@code cookies}. 
     * 
     * @param cookies the List of cookies to "fix", guaranteed to have at most
     *  one element (could be empty} when this method returns
     * @return true if {@code cookies} was modified
     */
    public static boolean fixCookieList(List<String> cookies) {
        if (cookies.size() > 1) {
            String cookie = toSingleCookieString(cookies);
            cookies.clear();
            cookies.add(cookie);
            return true;
        }
        return false;
    }

    /**
     * Same behavior as {@link #fixCookieList(List)}, except a new modifiable
     * List instance is always created and returned, regardless if any changes
     * need to be made to {@code cookies}. The returned List will have at most
     * one element. If {@code cookies} is empty, a new empty List is returned.
     * If {@code cookies} has a single element, a new single element List is
     * returned with the same contents as {@code cookies}. If {@code cookies}
     * has more than one element, a new single element List is returned whose
     * sole element is the combinations of all the cookie Strings in
     * {@code cookies} delimited by "; " via
     * {@link #toSingleCookieString(java.util.List)}. The returned List is
     * always modifiable.
     *
     * @param cookies the list of cookies to fix and make a copy of, no
     * modifications are made to {@code cookies}
     * @return a new modifiable List that will at most one element, which will
     * be the combined cookie string
     */
    public static List<String> toSingleCookieList(List<String> cookies) {
        if (cookies.size() <= 1) {
            return Lists.newArrayList(cookies);
        } else {
            return Lists.newArrayList(toSingleCookieString(cookies));
        }
    }

    /**
     * Combines all Strings in {@code cookies} into single Cookie or Cookie2
     * header value by combining them into a single String delimited by the
     * String "; " (semicolon followed by space).
     * <p>
     * If {@code cookies} is empty, the empty String is returned. If
     * {@code cookies} has a single element, the sole element is returned as is.
     * If {@code cookies} has more than one element, the Strings are appended in
     * the order they appear in the List, delimited by "; " (semicolon followed
     * by space).
     *
     *
     * @param cookies the List of cookies to combine
     * @return the combined cookie String, or the empty String if
     * {@code cookies} is empty
     */
    public static String toSingleCookieString(List<String> cookies) {
        if (cookies.isEmpty()) {
            return "";
        } else if (cookies.size() == 1) {
            return cookies.get(0);
        }
        int size = 0;
        for (String c : cookies) {
            if (c != null) {
                size += c.length() + 2;
            }
        }
        StringBuilder s = new StringBuilder(size);
        for (String c : cookies) {
            if (c != null) {
                if (s.length() > 0) {
                    s.append("; ");
                }
                s.append(c);
            }
        }
        return s.toString();
    }
}