]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/util/Fields.java
updating jetty to jetty-9.2.16.v2016040
[gigi.git] / lib / jetty / org / eclipse / jetty / util / Fields.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4 //  ------------------------------------------------------------------------
5 //  All rights reserved. This program and the accompanying materials
6 //  are made available under the terms of the Eclipse Public License v1.0
7 //  and Apache License v2.0 which accompanies this distribution.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
16 //  ========================================================================
17 //
18
19 package org.eclipse.jetty.util;
20
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.Iterator;
25 import java.util.LinkedHashMap;
26 import java.util.LinkedHashSet;
27 import java.util.List;
28 import java.util.Locale;
29 import java.util.Map;
30 import java.util.Set;
31
32 /**
33  * <p>A container for name/value pairs, known as fields.</p>
34  * <p>A {@link Field} is composed of a name string that can be case-sensitive
35  * or case-insensitive (by specifying the option at the constructor) and
36  * of a case-sensitive set of value strings.</p>
37  * <p>The implementation of this class is not thread safe.</p>
38  */
39 public class Fields implements Iterable<Fields.Field>
40 {
41     private final boolean caseSensitive;
42     private final Map<String, Field> fields;
43
44     /**
45      * <p>Creates an empty, modifiable, case insensitive {@link Fields} instance.</p>
46      * @see #Fields(Fields, boolean)
47      */
48     public Fields()
49     {
50         this(false);
51     }
52
53     /**
54      * <p>Creates an empty, modifiable, case insensitive {@link Fields} instance.</p>
55      * @param caseSensitive whether this {@link Fields} instance must be case sensitive
56      * @see #Fields(Fields, boolean)
57      */
58     public Fields(boolean caseSensitive)
59     {
60         this.caseSensitive = caseSensitive;
61         fields = new LinkedHashMap<>();
62     }
63
64     /**
65      * <p>Creates a {@link Fields} instance by copying the fields from the given
66      * {@link Fields} and making it (im)mutable depending on the given {@code immutable} parameter</p>
67      *
68      * @param original the {@link Fields} to copy fields from
69      * @param immutable whether this instance is immutable
70      */
71     public Fields(Fields original, boolean immutable)
72     {
73         this.caseSensitive = original.caseSensitive;
74         Map<String, Field> copy = new LinkedHashMap<>();
75         copy.putAll(original.fields);
76         fields = immutable ? Collections.unmodifiableMap(copy) : copy;
77     }
78
79     @Override
80     public boolean equals(Object obj)
81     {
82         if (this == obj)
83             return true;
84         if (obj == null || getClass() != obj.getClass())
85             return false;
86         Fields that = (Fields)obj;
87         if (getSize() != that.getSize())
88             return false;
89         if (caseSensitive != that.caseSensitive)
90             return false;
91         for (Map.Entry<String, Field> entry : fields.entrySet())
92         {
93             String name = entry.getKey();
94             Field value = entry.getValue();
95             if (!value.equals(that.get(name), caseSensitive))
96                 return false;
97         }
98         return true;
99     }
100
101     @Override
102     public int hashCode()
103     {
104         return fields.hashCode();
105     }
106
107     /**
108      * @return a set of field names
109      */
110     public Set<String> getNames()
111     {
112         Set<String> result = new LinkedHashSet<>();
113         for (Field field : fields.values())
114             result.add(field.getName());
115         return result;
116     }
117
118     private String normalizeName(String name)
119     {
120         return caseSensitive ? name : name.toLowerCase(Locale.ENGLISH);
121     }
122
123     /**
124      * @param name the field name
125      * @return the {@link Field} with the given name, or null if no such field exists
126      */
127     public Field get(String name)
128     {
129         return fields.get(normalizeName(name));
130     }
131
132     /**
133      * <p>Inserts or replaces the given name/value pair as a single-valued {@link Field}.</p>
134      *
135      * @param name the field name
136      * @param value the field value
137      */
138     public void put(String name, String value)
139     {
140         // Preserve the case for the field name
141         Field field = new Field(name, value);
142         fields.put(normalizeName(name), field);
143     }
144
145     /**
146      * <p>Inserts or replaces the given {@link Field}, mapped to the {@link Field#getName() field's name}</p>
147      *
148      * @param field the field to put
149      */
150     public void put(Field field)
151     {
152         if (field != null)
153             fields.put(normalizeName(field.getName()), field);
154     }
155
156     /**
157      * <p>Adds the given value to a field with the given name,
158      * creating a {@link Field} is none exists for the given name.</p>
159      *
160      * @param name the field name
161      * @param value the field value to add
162      */
163     public void add(String name, String value)
164     {
165         String key = normalizeName(name);
166         Field field = fields.get(key);
167         if (field == null)
168         {
169             // Preserve the case for the field name
170             field = new Field(name, value);
171             fields.put(key, field);
172         }
173         else
174         {
175             field = new Field(field.getName(), field.getValues(), value);
176             fields.put(key, field);
177         }
178     }
179
180     /**
181      * <p>Removes the {@link Field} with the given name</p>
182      *
183      * @param name the name of the field to remove
184      * @return the removed field, or null if no such field existed
185      */
186     public Field remove(String name)
187     {
188         return fields.remove(normalizeName(name));
189     }
190
191     /**
192      * <p>Empties this {@link Fields} instance from all fields</p>
193      * @see #isEmpty()
194      */
195     public void clear()
196     {
197         fields.clear();
198     }
199
200     /**
201      * @return whether this {@link Fields} instance is empty
202      */
203     public boolean isEmpty()
204     {
205         return fields.isEmpty();
206     }
207
208     /**
209      * @return the number of fields
210      */
211     public int getSize()
212     {
213         return fields.size();
214     }
215
216     /**
217      * @return an iterator over the {@link Field}s present in this instance
218      */
219     @Override
220     public Iterator<Field> iterator()
221     {
222         return fields.values().iterator();
223     }
224
225     @Override
226     public String toString()
227     {
228         return fields.toString();
229     }
230
231     /**
232      * <p>A named list of string values.</p>
233      * <p>The name is case-sensitive and there must be at least one value.</p>
234      */
235     public static class Field
236     {
237         private final String name;
238         private final List<String> values;
239
240         public Field(String name, String value)
241         {
242             this(name, Collections.singletonList(value));
243         }
244
245         private Field(String name, List<String> values, String... moreValues)
246         {
247             this.name = name;
248             List<String> list = new ArrayList<>(values.size() + moreValues.length);
249             list.addAll(values);
250             list.addAll(Arrays.asList(moreValues));
251             this.values = Collections.unmodifiableList(list);
252         }
253
254         public boolean equals(Field that, boolean caseSensitive)
255         {
256             if (this == that)
257                 return true;
258             if (that == null)
259                 return false;
260             if (caseSensitive)
261                 return equals(that);
262             return name.equalsIgnoreCase(that.name) && values.equals(that.values);
263         }
264
265         @Override
266         public boolean equals(Object obj)
267         {
268             if (this == obj)
269                 return true;
270             if (obj == null || getClass() != obj.getClass())
271                 return false;
272             Field that = (Field)obj;
273             return name.equals(that.name) && values.equals(that.values);
274         }
275
276         @Override
277         public int hashCode()
278         {
279             int result = name.hashCode();
280             result = 31 * result + values.hashCode();
281             return result;
282         }
283
284         /**
285          * @return the field's name
286          */
287         public String getName()
288         {
289             return name;
290         }
291
292         /**
293          * @return the first field's value
294          */
295         public String getValue()
296         {
297             return values.get(0);
298         }
299
300         /**
301          * <p>Attempts to convert the result of {@link #getValue()} to an integer,
302          * returning it if the conversion is successful; returns null if the
303          * result of {@link #getValue()} is null.</p>
304          *
305          * @return the result of {@link #getValue()} converted to an integer, or null
306          * @throws NumberFormatException if the conversion fails
307          */
308         public Integer getValueAsInt()
309         {
310             final String value = getValue();
311             return value == null ? null : Integer.valueOf(value);
312         }
313
314         /**
315          * @return the field's values
316          */
317         public List<String> getValues()
318         {
319             return values;
320         }
321
322         /**
323          * @return whether the field has multiple values
324          */
325         public boolean hasMultipleValues()
326         {
327             return values.size() > 1;
328         }
329
330         @Override
331         public String toString()
332         {
333             return String.format("%s=%s", name, values);
334         }
335     }
336 }