//
//  ========================================================================
//  Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

A container for name/value pairs, known as fields.

A Field is composed of a name string that can be case-sensitive or case-insensitive (by specifying the option at the constructor) and of a case-sensitive set of value strings.

The implementation of this class is not thread safe.

/** * <p>A container for name/value pairs, known as fields.</p> * <p>A {@link Field} is composed of a name string that can be case-sensitive * or case-insensitive (by specifying the option at the constructor) and * of a case-sensitive set of value strings.</p> * <p>The implementation of this class is not thread safe.</p> */
public class Fields implements Iterable<Fields.Field> { private final boolean caseSensitive; private final Map<String, Field> fields;

Creates an empty, modifiable, case insensitive Fields instance.

See Also:
/** * <p>Creates an empty, modifiable, case insensitive {@link Fields} instance.</p> * * @see #Fields(Fields, boolean) */
public Fields() { this(false); }

Creates an empty, modifiable, case insensitive Fields instance.

Params:
  • caseSensitive – whether this Fields instance must be case sensitive
See Also:
/** * <p>Creates an empty, modifiable, case insensitive {@link Fields} instance.</p> * * @param caseSensitive whether this {@link Fields} instance must be case sensitive * @see #Fields(Fields, boolean) */
public Fields(boolean caseSensitive) { this.caseSensitive = caseSensitive; fields = new LinkedHashMap<>(); }

Creates a Fields instance by copying the fields from the given Fields and making it (im)mutable depending on the given immutable parameter

Params:
  • original – the Fields to copy fields from
  • immutable – whether this instance is immutable
/** * <p>Creates a {@link Fields} instance by copying the fields from the given * {@link Fields} and making it (im)mutable depending on the given {@code immutable} parameter</p> * * @param original the {@link Fields} to copy fields from * @param immutable whether this instance is immutable */
public Fields(Fields original, boolean immutable) { this.caseSensitive = original.caseSensitive; Map<String, Field> copy = new LinkedHashMap<>(); copy.putAll(original.fields); fields = immutable ? Collections.unmodifiableMap(copy) : copy; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Fields that = (Fields)obj; if (getSize() != that.getSize()) return false; if (caseSensitive != that.caseSensitive) return false; for (Map.Entry<String, Field> entry : fields.entrySet()) { String name = entry.getKey(); Field value = entry.getValue(); if (!value.equals(that.get(name), caseSensitive)) return false; } return true; } @Override public int hashCode() { return fields.hashCode(); }
Returns:a set of field names
/** * @return a set of field names */
public Set<String> getNames() { Set<String> result = new LinkedHashSet<>(); for (Field field : fields.values()) { result.add(field.getName()); } return result; } private String normalizeName(String name) { return caseSensitive ? name : name.toLowerCase(Locale.ENGLISH); }
Params:
  • name – the field name
Returns:the Field with the given name, or null if no such field exists
/** * @param name the field name * @return the {@link Field} with the given name, or null if no such field exists */
public Field get(String name) { return fields.get(normalizeName(name)); }

Inserts or replaces the given name/value pair as a single-valued Field.

Params:
  • name – the field name
  • value – the field value
/** * <p>Inserts or replaces the given name/value pair as a single-valued {@link Field}.</p> * * @param name the field name * @param value the field value */
public void put(String name, String value) { // Preserve the case for the field name Field field = new Field(name, value); fields.put(normalizeName(name), field); }

Inserts or replaces the given Field, mapped to the field's name

Params:
  • field – the field to put
/** * <p>Inserts or replaces the given {@link Field}, mapped to the {@link Field#getName() field's name}</p> * * @param field the field to put */
public void put(Field field) { if (field != null) fields.put(normalizeName(field.getName()), field); }

Adds the given value to a field with the given name, creating a Field is none exists for the given name.

Params:
  • name – the field name
  • value – the field value to add
/** * <p>Adds the given value to a field with the given name, * creating a {@link Field} is none exists for the given name.</p> * * @param name the field name * @param value the field value to add */
public void add(String name, String value) { String key = normalizeName(name); Field field = fields.get(key); if (field == null) { // Preserve the case for the field name field = new Field(name, value); fields.put(key, field); } else { field = new Field(field.getName(), field.getValues(), value); fields.put(key, field); } }

Removes the Field with the given name

Params:
  • name – the name of the field to remove
Returns:the removed field, or null if no such field existed
/** * <p>Removes the {@link Field} with the given name</p> * * @param name the name of the field to remove * @return the removed field, or null if no such field existed */
public Field remove(String name) { return fields.remove(normalizeName(name)); }

Empties this Fields instance from all fields

See Also:
/** * <p>Empties this {@link Fields} instance from all fields</p> * * @see #isEmpty() */
public void clear() { fields.clear(); }
Returns:whether this Fields instance is empty
/** * @return whether this {@link Fields} instance is empty */
public boolean isEmpty() { return fields.isEmpty(); }
Returns:the number of fields
/** * @return the number of fields */
public int getSize() { return fields.size(); }
Returns:an iterator over the Fields present in this instance
/** * @return an iterator over the {@link Field}s present in this instance */
@Override public Iterator<Field> iterator() { return fields.values().iterator(); } @Override public String toString() { return fields.toString(); }

A named list of string values.

The name is case-sensitive and there must be at least one value.

/** * <p>A named list of string values.</p> * <p>The name is case-sensitive and there must be at least one value.</p> */
public static class Field { private final String name; private final List<String> values; public Field(String name, String value) { this(name, Collections.singletonList(value)); } private Field(String name, List<String> values, String... moreValues) { this.name = name; List<String> list = new ArrayList<>(values.size() + moreValues.length); list.addAll(values); list.addAll(Arrays.asList(moreValues)); this.values = Collections.unmodifiableList(list); } @SuppressWarnings("ReferenceEquality") public boolean equals(Field that, boolean caseSensitive) { if (this == that) return true; if (that == null) return false; if (caseSensitive) return equals(that); return name.equalsIgnoreCase(that.name) && values.equals(that.values); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Field that = (Field)obj; return name.equals(that.name) && values.equals(that.values); } @Override public int hashCode() { int result = name.hashCode(); result = 31 * result + values.hashCode(); return result; }
Returns:the field's name
/** * @return the field's name */
public String getName() { return name; }
Returns:the first field's value
/** * @return the first field's value */
public String getValue() { return values.get(0); }

Attempts to convert the result of getValue() to an integer, returning it if the conversion is successful; returns null if the result of getValue() is null.

Throws:
Returns:the result of getValue() converted to an integer, or null
/** * <p>Attempts to convert the result of {@link #getValue()} to an integer, * returning it if the conversion is successful; returns null if the * result of {@link #getValue()} is null.</p> * * @return the result of {@link #getValue()} converted to an integer, or null * @throws NumberFormatException if the conversion fails */
public Integer getValueAsInt() { final String value = getValue(); return value == null ? null : (Integer)Integer.parseInt(value); }
Returns:the field's values
/** * @return the field's values */
public List<String> getValues() { return values; }
Returns:whether the field has multiple values
/** * @return whether the field has multiple values */
public boolean hasMultipleValues() { return values.size() > 1; } @Override public String toString() { return String.format("%s=%s", name, values); } } }