1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.chain.web.jakarta.internal;
18
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.HashSet;
23 import java.util.Map;
24 import java.util.Set;
25 import java.util.function.Supplier;
26
27 import org.apache.commons.chain.web.MapEntry;
28 import org.apache.commons.chain.web.ParameterMap;
29
30 import jakarta.servlet.http.Cookie;
31
32 /**
33 * Implementation of {@code Map} for cookies with
34 * a parameter-provider.
35 *
36 * @param <P> the type of the parameter-provider
37 *
38 * @author Graff Stefan
39 * @since Chain 1.3
40 */
41 public class CookieMap<P> extends ParameterMap<P, Cookie> {
42
43 /**
44 * Supplier to return the {@link Cookie}-Array in this object.
45 */
46 private final Supplier<Cookie[]> cookiesSupplier;
47
48 /**
49 * The constructor for the {@code Map} for cookies.
50 *
51 * @param request the request with the cookies
52 * @param cookiesSupplier Supplier to return the {@link Cookie}-Array in this
53 * object
54 */
55 public CookieMap(final P request, final Supplier<Cookie[]> cookiesSupplier) {
56 super(request, null, null);
57 this.cookiesSupplier = cookiesSupplier;
58 }
59
60 /**
61 * Returns {@code true} if this cookie-map contains a mapping
62 * for the specified cookie-name.
63 *
64 * @param key The key whose presence in this cookie-map is to
65 * be tested
66 *
67 * @return {@code true} if this cookie-map contains a mapping
68 * for the specified cookie-name.
69 */
70 @Override
71 public boolean containsKey(Object key) {
72 return get(key) != null;
73 }
74
75 /**
76 * Returns {@code true} if this cookie-map maps one or more keys
77 * to the specified cookie.
78 *
79 * @param value cookie whose presence in this cookie-map is to be
80 * tested
81 *
82 * @return {@code true} if this cookie-map maps one or more keys
83 * to the specified cookie
84 */
85 @Override
86 public boolean containsValue(Object value) {
87 for (Cookie cookie : values()) {
88 if (cookie.equals(value)) {
89 return true;
90 }
91 }
92 return false;
93 }
94
95 /**
96 * Returns a {@link Set} view of the mappings contained in this
97 * cookie-map. The set is not backed by the cookie-map, so
98 * changes to the cookie-map are not reflected in the set,
99 * 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 }