From 72231250ed81e10d66bfe70701e64fa5fe50f712 Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Wed, 15 Jun 2016 23:09:44 +0200 Subject: Publish --- .../main/java/com/yahoo/jdisc/HeaderFields.java | 305 +++++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 jdisc_core/src/main/java/com/yahoo/jdisc/HeaderFields.java (limited to 'jdisc_core/src/main/java/com/yahoo/jdisc/HeaderFields.java') diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/HeaderFields.java b/jdisc_core/src/main/java/com/yahoo/jdisc/HeaderFields.java new file mode 100644 index 00000000000..a81fb3ff152 --- /dev/null +++ b/jdisc_core/src/main/java/com/yahoo/jdisc/HeaderFields.java @@ -0,0 +1,305 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc; + +import com.google.common.collect.ImmutableList; + +import java.util.*; + +/** + *

This is an encapsulation of the header fields that belong to either a {@link Request} or a {@link Response}. It is + * a multimap from String to String, with some additional methods for convenience. The keys of this map are compared by + * ignoring their case, so that get("foo") returns the same entry as get("FOO").

+ * + * @author Simon Thoresen + */ +public class HeaderFields implements Map> { + + private final TreeMap> content = new TreeMap<>(new Comparator() { + + @Override + public int compare(String lhs, String rhs) { + return lhs.compareToIgnoreCase(rhs); + } + }); + + @Override + public int size() { + return content.size(); + } + + @Override + public boolean isEmpty() { + return content.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return content.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return content.containsValue(value); + } + + /** + *

Convenience method for checking whether or not a named header contains a specific value. If the named header + * is not set, or if the given value is not contained within that header's value list, this method returns + * false.

+ * + *

NOTE: This method is case-SENSITIVE.

+ * + * @param key The key whose values to search in. + * @param value The values to search for. + * @return True if the given value was found in the named header. + * @see #containsIgnoreCase + */ + public boolean contains(String key, String value) { + List lst = content.get(key); + if (lst == null) { + return false; + } + return lst.contains(value); + } + + /** + *

Convenience method for checking whether or not a named header contains a specific value, regardless of case. + * If the named header is not set, or if the given value is not contained within that header's value list, this + * method returns false.

+ * + *

NOTE: This method is case-INSENSITIVE.

+ * + * @param key The key whose values to search in. + * @param value The values to search for, ignoring case. + * @return True if the given value was found in the named header. + * @see #contains + */ + public boolean containsIgnoreCase(String key, String value) { + List lst = content.get(key); + if (lst == null) { + return false; + } + for (String val : lst) { + if (value.equalsIgnoreCase(val)) { + return true; + } + } + return false; + } + + /** + *

Adds the given value to the entry of the specified key. If no entry exists for the given key, a new one is + * created containing only the given value.

+ * + * @param key The key with which the specified value is to be associated. + * @param value The value to be added to the list associated with the specified key. + */ + public void add(String key, String value) { + List lst = content.get(key); + if (lst != null) { + lst.add(value); + } else { + put(key, value); + } + } + + /** + *

Adds the given values to the entry of the specified key. If no entry exists for the given key, a new one is + * created containing only the given values.

+ * + * @param key The key with which the specified value is to be associated. + * @param values The values to be added to the list associated with the specified key. + */ + public void add(String key, List values) { + List lst = content.get(key); + if (lst != null) { + lst.addAll(values); + } else { + put(key, values); + } + } + + /** + *

Adds all the entries of the given map to this. This is the same as calling {@link #add(String, List)} for each + * entry in values.

+ * + * @param values The values to be added to this. + */ + public void addAll(Map> values) { + for (Entry> entry : values.entrySet()) { + add(entry.getKey(), entry.getValue()); + } + } + + /** + *

Convenience method to call {@link #put(String, List)} with a singleton list that contains the specified + * value.

+ * + * @param key The key of the entry to put. + * @param value The value to put. + * @return The previous value associated with key, or null if there was no mapping for + * key. + */ + public List put(String key, String value) { + ArrayList list = new ArrayList(1); + list.add(value); + return content.put(key, list); + } + + @Override + public List put(String key, List value) { + return content.put(key, new ArrayList<>(value)); + } + + @Override + public void putAll(Map> values) { + for (Entry> entry : values.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @Override + public List remove(Object key) { + return content.remove(key); + } + + /** + *

Removes the given value from the entry of the specified key.

+ * + * @param key The key of the entry to remove from. + * @param value The value to remove from the entry. + * @return True if the value was removed. + */ + public boolean remove(String key, String value) { + List lst = content.get(key); + if (lst == null) { + return false; + } + if (!lst.remove(value)) { + return false; + } + if (lst.isEmpty()) { + content.remove(key); + } + return true; + } + + @Override + public void clear() { + content.clear(); + } + + @Override + public List get(Object key) { + return content.get(key); + } + + /** + *

Convenience method for retrieving the first value of a named header field. If the header is not set, or if the + * value list is empty, this method returns null.

+ * + * @param key The key whose first value to return. + * @return The first value of the named header, or null. + */ + public String getFirst(String key) { + List lst = get(key); + if (lst == null || lst.isEmpty()) { + return null; + } + return lst.get(0); + } + + /** + *

Convenience method for checking whether or not a named header field is true. To satisfy this, the + * header field needs to have at least 1 entry, and Boolean.valueOf() of all its values must parse as + * true.

+ * + * @param key The key whose values to parse as a boolean. + * @return The boolean value of the named header. + */ + public boolean isTrue(String key) { + List lst = content.get(key); + if (lst == null) { + return false; + } + for (String value : lst) { + if (!Boolean.valueOf(value)) { + return false; + } + } + return true; + } + + @Override + public Set keySet() { + return content.keySet(); + } + + @Override + public Collection> values() { + return content.values(); + } + + @Override + public Set>> entrySet() { + return content.entrySet(); + } + + @Override + public String toString() { + return content.toString(); + } + + /** + *

Returns an unmodifiable list of all key-value pairs of this. This provides a flattened view on the content of + * this map.

+ * + * @return The collection of entries. + */ + public List> entries() { + List> list = new ArrayList<>(content.size()); + for (Entry> entry : content.entrySet()) { + String key = entry.getKey(); + for (String value : entry.getValue()) { + list.add(new MyEntry(key, value)); + } + } + return ImmutableList.copyOf(list); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof HeaderFields && content.equals(((HeaderFields)obj).content); + } + + @Override + public int hashCode() { + return content.hashCode(); + } + + private static class MyEntry implements Map.Entry { + + final String key; + final String value; + + private MyEntry(String key, String value) { + this.key = key; + this.value = value; + } + + @Override + public String getKey() { + return key; + } + + @Override + public String getValue() { + return value; + } + + @Override + public String setValue(String value) { + throw new UnsupportedOperationException(); + } + } +} \ No newline at end of file -- cgit v1.2.3