001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.chain.web.jakarta.internal;
018
019import java.util.Arrays;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.HashSet;
023import java.util.Map;
024import java.util.Set;
025import java.util.function.Supplier;
026
027import org.apache.commons.chain.web.MapEntry;
028import org.apache.commons.chain.web.ParameterMap;
029
030import jakarta.servlet.http.Cookie;
031
032/**
033 * Implementation of {@code Map} for cookies with
034 * a parameter-provider.
035 *
036 * @param <P> the type of the parameter-provider
037 *
038 * @author Graff Stefan
039 * @since Chain 1.3
040 */
041public class CookieMap<P> extends ParameterMap<P, Cookie> {
042
043    /**
044     * Supplier to return the {@link Cookie}-Array in this object.
045     */
046    private final Supplier<Cookie[]> cookiesSupplier;
047
048    /**
049     * The constructor for the {@code Map} for cookies.
050     *
051     * @param request         the request with the cookies
052     * @param cookiesSupplier Supplier to return the {@link Cookie}-Array in this
053     *                        object
054     */
055    public CookieMap(final P request, final Supplier<Cookie[]> cookiesSupplier) {
056        super(request, null, null);
057        this.cookiesSupplier = cookiesSupplier;
058    }
059
060    /**
061     * Returns {@code true} if this cookie-map contains a mapping
062     * for the specified cookie-name.
063     *
064     * @param key The key whose presence in this cookie-map is to
065     *            be tested
066     *
067     * @return {@code true} if this cookie-map contains a mapping
068     *         for the specified cookie-name.
069     */
070    @Override
071    public boolean containsKey(Object key) {
072        return get(key) != null;
073    }
074
075    /**
076     * Returns {@code true} if this cookie-map maps one or more keys
077     * to the specified cookie.
078     *
079     * @param value cookie whose presence in this cookie-map is to be
080     *        tested
081     *
082     * @return {@code true} if this cookie-map maps one or more keys
083     *         to the specified cookie
084     */
085    @Override
086    public boolean containsValue(Object value) {
087        for (Cookie cookie : values()) {
088            if (cookie.equals(value)) {
089                return true;
090            }
091        }
092        return false;
093    }
094
095    /**
096     * Returns a {@link Set} view of the mappings contained in this
097     * cookie-map. The set is not backed by the cookie-map, so
098     * changes to the cookie-map are not reflected in the set,
099     * and vice-versa.
100     *
101     * @return a set view of the mappings contained in this cookie-map
102     */
103    @Override
104    public Set<Map.Entry<String, Cookie>> entrySet() {
105        final Set<Map.Entry<String, Cookie>> set = new HashSet<>();
106        for (Cookie cookie : values()) {
107            set.add(new MapEntry<>(cookie.getName(), cookie, false));
108        }
109        return set;
110    }
111
112    /**
113     * Returns the cookie to which the specified cookie-name is mapped,
114     * or {@code null} if this cookie-map contains no mapping for the
115     * cookie-name.
116     *
117     * @param key the cookie-name whose associated value is to be
118     *            returned
119     *
120     * @return the value to which the specified key is mapped, or
121     *         {@code null} if this cookie-map contains no mapping for
122     *         the cookie-name
123     *
124     * @see #put(Object, Object)
125     */
126    @Override
127    public Cookie get(Object key) {
128        final Collection<Cookie> cookies = values();
129        if (!cookies.isEmpty()) {
130            final String skey = key(key);
131            for (Cookie cookie : cookies) {
132                if (cookie.getName().equals(skey)) {
133                    return cookie;
134                }
135            }
136        }
137        return null;
138    }
139
140    /**
141     * Returns {@code true} if this cookie-map contains no cookies.
142     *
143     * @return {@code true} if this cookie-map contains no cookies
144     */
145    @Override
146    public boolean isEmpty() {
147        final Cookie[] cookies = cookiesSupplier.get();
148        return cookies == null || cookies.length == 0;
149    }
150
151    /**
152     * Returns a {@link Set} view of the cookies contained in this
153     * cookie-map. The set is not backed by the cookie-map, so
154     * changes to the cookie-map are not reflected in the set, and
155     * vice-versa.
156     *
157     * @return a set view of the cookies contained in this cookie-map
158     */
159    @Override
160    public Set<String> keySet() {
161        final Collection<Cookie> cookies = values();
162        final Set<String> set = new HashSet<String>(Math.max((int) (cookies.size() / .75f) + 1, 16));
163        for (Cookie cookie : cookies) {
164            set.add(cookie.getName());
165        }
166        return set;
167    }
168
169    /**
170     * Returns the number of cookies in this cookie-map.
171     *
172     * @return the number of cookies in this cookie-map
173     */
174    @Override
175    public int size() {
176        final Cookie[] cookies = cookiesSupplier.get();
177        return cookies == null ? 0 : cookies.length;
178    }
179
180    /**
181     * Returns a {@link Collection} view of the cookies contained in
182     * this cookie-map. The collection is not backed by the
183     * cookie-map, so changes to the cookie-map are not
184     * reflected in the collection, and vice-versa.
185     *
186     * @return a view of the cookies contained in this cookie-map
187     */
188    @Override
189    public Collection<Cookie> values() {
190        final Cookie[] cookies = cookiesSupplier.get();
191        return cookies == null ? Collections.emptyList() : Arrays.asList(cookies);
192    }
193
194    /**
195     * Returns the hash code value for this cookie-map. The
196     * hash code of a cookie-map is defined to be the sum of
197     * the hash codes of each entry in the cookie-map's
198     * {@code entrySet()} view. This ensures that {@code m1.equals(m2)}
199     * implies that {@code m1.hashCode()==m2.hashCode()} for any two
200     * parameter-maps {@code m1} and {@code m2}, as required by the
201     * general contract of {@link Object#hashCode}.
202     *
203     * @implSpec
204     * This implementation calls the {@code hashCode()} from the
205     * parameter-map.
206     *
207     * @return the hash code value for this cookie-map
208     */
209    @Override
210    public int hashCode() {
211        return super.hashCode();
212    }
213
214    /**
215     * Compares the specified object with this cookie-map for equality.
216     * Returns {@code true} if the given object is also a cookie-map
217     * and the two cookie-maps represent the same mappings. More formally,
218     * two cookie-maps {@code m1} and {@code m2} represent the same
219     * mappings if {@code m1.entrySet().equals(m2.entrySet())}.
220     *
221     * @param obj object to be compared for equality with this
222     *        cookie-map
223     *
224     * @return {@code true} if the specified object is equal to this
225     *         cookie-map
226     */
227    @Override
228    public boolean equals(Object obj) {
229        return super.equals(obj);
230    }
231}