2 // ========================================================================
3 // Copyright (c) 1995-2014 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.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.util;
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;
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>
39 public class Fields implements Iterable<Fields.Field>
41 private final boolean caseSensitive;
42 private final Map<String, Field> fields;
45 * <p>Creates an empty, modifiable, case insensitive {@link Fields} instance.</p>
46 * @see #Fields(Fields, boolean)
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)
58 public Fields(boolean caseSensitive)
60 this.caseSensitive = caseSensitive;
61 fields = new LinkedHashMap<>();
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>
68 * @param original the {@link Fields} to copy fields from
69 * @param immutable whether this instance is immutable
71 public Fields(Fields original, boolean immutable)
73 this.caseSensitive = original.caseSensitive;
74 Map<String, Field> copy = new LinkedHashMap<>();
75 copy.putAll(original.fields);
76 fields = immutable ? Collections.unmodifiableMap(copy) : copy;
80 public boolean equals(Object obj)
84 if (obj == null || getClass() != obj.getClass())
86 Fields that = (Fields)obj;
87 if (getSize() != that.getSize())
89 if (caseSensitive != that.caseSensitive)
91 for (Map.Entry<String, Field> entry : fields.entrySet())
93 String name = entry.getKey();
94 Field value = entry.getValue();
95 if (!value.equals(that.get(name), caseSensitive))
102 public int hashCode()
104 return fields.hashCode();
108 * @return a set of field names
110 public Set<String> getNames()
112 Set<String> result = new LinkedHashSet<>();
113 for (Field field : fields.values())
114 result.add(field.getName());
118 private String normalizeName(String name)
120 return caseSensitive ? name : name.toLowerCase(Locale.ENGLISH);
124 * @param name the field name
125 * @return the {@link Field} with the given name, or null if no such field exists
127 public Field get(String name)
129 return fields.get(normalizeName(name));
133 * <p>Inserts or replaces the given name/value pair as a single-valued {@link Field}.</p>
135 * @param name the field name
136 * @param value the field value
138 public void put(String name, String value)
140 // Preserve the case for the field name
141 Field field = new Field(name, value);
142 fields.put(normalizeName(name), field);
146 * <p>Inserts or replaces the given {@link Field}, mapped to the {@link Field#getName() field's name}</p>
148 * @param field the field to put
150 public void put(Field field)
153 fields.put(normalizeName(field.getName()), field);
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>
160 * @param name the field name
161 * @param value the field value to add
163 public void add(String name, String value)
165 String key = normalizeName(name);
166 Field field = fields.get(key);
169 // Preserve the case for the field name
170 field = new Field(name, value);
171 fields.put(key, field);
175 field = new Field(field.getName(), field.getValues(), value);
176 fields.put(key, field);
181 * <p>Removes the {@link Field} with the given name</p>
183 * @param name the name of the field to remove
184 * @return the removed field, or null if no such field existed
186 public Field remove(String name)
188 return fields.remove(normalizeName(name));
192 * <p>Empties this {@link Fields} instance from all fields</p>
201 * @return whether this {@link Fields} instance is empty
203 public boolean isEmpty()
205 return fields.isEmpty();
209 * @return the number of fields
213 return fields.size();
217 * @return an iterator over the {@link Field}s present in this instance
220 public Iterator<Field> iterator()
222 return fields.values().iterator();
226 public String toString()
228 return fields.toString();
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>
235 public static class Field
237 private final String name;
238 private final List<String> values;
240 public Field(String name, String value)
242 this(name, Collections.singletonList(value));
245 private Field(String name, List<String> values, String... moreValues)
248 List<String> list = new ArrayList<>(values.size() + moreValues.length);
250 list.addAll(Arrays.asList(moreValues));
251 this.values = Collections.unmodifiableList(list);
254 public boolean equals(Field that, boolean caseSensitive)
262 return name.equalsIgnoreCase(that.name) && values.equals(that.values);
266 public boolean equals(Object obj)
270 if (obj == null || getClass() != obj.getClass())
272 Field that = (Field)obj;
273 return name.equals(that.name) && values.equals(that.values);
277 public int hashCode()
279 int result = name.hashCode();
280 result = 31 * result + values.hashCode();
285 * @return the field's name
287 public String getName()
293 * @return the first field's value
295 public String getValue()
297 return values.get(0);
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>
305 * @return the result of {@link #getValue()} converted to an integer, or null
306 * @throws NumberFormatException if the conversion fails
308 public Integer getValueAsInt()
310 final String value = getValue();
311 return value == null ? null : Integer.valueOf(value);
315 * @return the field's values
317 public List<String> getValues()
323 * @return whether the field has multiple values
325 public boolean hasMultipleValues()
327 return values.size() > 1;
331 public String toString()
333 return String.format("%s=%s", name, values);