CookieMap.java

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.chain.web.javax.internal;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;

import javax.servlet.http.Cookie;

import org.apache.commons.chain.web.MapEntry;
import org.apache.commons.chain.web.ParameterMap;

/**
 * Implementation of {@code Map} for cookies with
 * a parameter-provider.
 *
 * @param <P> the type of the parameter-provider
 *
 * @author Graff Stefan
 * @since Chain 1.3
 */
public class CookieMap<P> extends ParameterMap<P, Cookie> {

    /**
     * Supplier to return the {@link Cookie}-Array in this object.
     */
    private final Supplier<Cookie[]> cookiesSupplier;

    /**
     * The constructor for the {@code Map} for cookies.
     *
     * @param request         the request with the cookies
     * @param cookiesSupplier Supplier to return the {@link Cookie}-Array in this
     *                        object
     */
    public CookieMap(final P request, final Supplier<Cookie[]> cookiesSupplier) {
        super(request, null, null);
        this.cookiesSupplier = cookiesSupplier;
    }

    /**
     * Returns {@code true} if this cookie-map contains a mapping
     * for the specified cookie-name.
     *
     * @param key The key whose presence in this cookie-map is to
     *            be tested
     *
     * @return {@code true} if this cookie-map contains a mapping
     *         for the specified cookie-name.
     */
    @Override
    public boolean containsKey(Object key) {
        return get(key) != null;
    }

    /**
     * Returns {@code true} if this cookie-map maps one or more keys
     * to the specified cookie.
     *
     * @param value cookie whose presence in this cookie-map is to be
     *        tested
     *
     * @return {@code true} if this cookie-map maps one or more keys
     *         to the specified cookie
     */
    @Override
    public boolean containsValue(Object value) {
        for (Cookie cookie : values()) {
            if (cookie.equals(value)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns a {@link Set} view of the mappings contained in this
     * cookie-map. The set is not backed by the cookie-map, so
     * changes to the cookie-map are not reflected in the set,
     * and vice-versa.
     *
     * @return a set view of the mappings contained in this cookie-map
     */
    @Override
    public Set<Map.Entry<String, Cookie>> entrySet() {
        final Set<Map.Entry<String, Cookie>> set = new HashSet<>();
        for (Cookie cookie : values()) {
            set.add(new MapEntry<>(cookie.getName(), cookie, false));
        }
        return set;
    }

    /**
     * Returns the cookie to which the specified cookie-name is mapped,
     * or {@code null} if this cookie-map contains no mapping for the
     * cookie-name.
     *
     * @param key the cookie-name whose associated value is to be
     *            returned
     *
     * @return the value to which the specified key is mapped, or
     *         {@code null} if this cookie-map contains no mapping for
     *         the cookie-name
     *
     * @see #put(Object, Object)
     */
    @Override
    public Cookie get(Object key) {
        final Collection<Cookie> cookies = values();
        if (!cookies.isEmpty()) {
            final String skey = key(key);
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(skey)) {
                    return cookie;
                }
            }
        }
        return null;
    }

    /**
     * Returns {@code true} if this cookie-map contains no cookies.
     *
     * @return {@code true} if this cookie-map contains no cookies
     */
    @Override
    public boolean isEmpty() {
        final Cookie[] cookies = cookiesSupplier.get();
        return cookies == null || cookies.length == 0;
    }

    /**
     * Returns a {@link Set} view of the cookies contained in this
     * cookie-map. The set is not backed by the cookie-map, so
     * changes to the cookie-map are not reflected in the set, and
     * vice-versa.
     *
     * @return a set view of the cookies contained in this cookie-map
     */
    @Override
    public Set<String> keySet() {
        final Collection<Cookie> cookies = values();
        final Set<String> set = new HashSet<String>(Math.max((int) (cookies.size() / .75f) + 1, 16));
        for (Cookie cookie : cookies) {
            set.add(cookie.getName());
        }
        return set;
    }

    /**
     * Returns the number of cookies in this cookie-map.
     *
     * @return the number of cookies in this cookie-map
     */
    @Override
    public int size() {
        final Cookie[] cookies = cookiesSupplier.get();
        return cookies == null ? 0 : cookies.length;
    }

    /**
     * Returns a {@link Collection} view of the cookies contained in
     * this cookie-map. The collection is not backed by the
     * cookie-map, so changes to the cookie-map are not
     * reflected in the collection, and vice-versa.
     *
     * @return a view of the cookies contained in this cookie-map
     */
    @Override
    public Collection<Cookie> values() {
        final Cookie[] cookies = cookiesSupplier.get();
        return cookies == null ? Collections.emptyList() : Arrays.asList(cookies);
    }

    /**
     * Returns the hash code value for this cookie-map. The
     * hash code of a cookie-map is defined to be the sum of
     * the hash codes of each entry in the cookie-map's
     * {@code entrySet()} view. This ensures that {@code m1.equals(m2)}
     * implies that {@code m1.hashCode()==m2.hashCode()} for any two
     * parameter-maps {@code m1} and {@code m2}, as required by the
     * general contract of {@link Object#hashCode}.
     *
     * @implSpec
     * This implementation calls the {@code hashCode()} from the
     * parameter-map.
     *
     * @return the hash code value for this cookie-map
     */
    @Override
    public int hashCode() {
        return super.hashCode();
    }

    /**
     * Compares the specified object with this cookie-map for equality.
     * Returns {@code true} if the given object is also a cookie-map
     * and the two cookie-maps represent the same mappings. More formally,
     * two cookie-maps {@code m1} and {@code m2} represent the same
     * mappings if {@code m1.entrySet().equals(m2.entrySet())}.
     *
     * @param obj object to be compared for equality with this
     *        cookie-map
     *
     * @return {@code true} if the specified object is equal to this
     *         cookie-map
     */
    @Override
    public boolean equals(Object obj) {
        return super.equals(obj);
    }
}