diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /jdisc_core/src/main/java/com/yahoo/jdisc/HeaderFields.java |
Publish
Diffstat (limited to 'jdisc_core/src/main/java/com/yahoo/jdisc/HeaderFields.java')
-rw-r--r-- | jdisc_core/src/main/java/com/yahoo/jdisc/HeaderFields.java | 305 |
1 files changed, 305 insertions, 0 deletions
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.*; + +/** + * <p>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 <tt>get("foo")</tt> returns the same entry as <tt>get("FOO")</tt>.</p> + * + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class HeaderFields implements Map<String, List<String>> { + + private final TreeMap<String, List<String>> content = new TreeMap<>(new Comparator<String>() { + + @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); + } + + /** + * <p>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 + * <em>false</em>.</p> + * + * <p><em>NOTE:</em> This method is case-SENSITIVE.</p> + * + * @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<String> lst = content.get(key); + if (lst == null) { + return false; + } + return lst.contains(value); + } + + /** + * <p>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 <em>false</em>.</p> + * + * <p><em>NOTE:</em> This method is case-INSENSITIVE.</p> + * + * @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<String> lst = content.get(key); + if (lst == null) { + return false; + } + for (String val : lst) { + if (value.equalsIgnoreCase(val)) { + return true; + } + } + return false; + } + + /** + * <p>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.</p> + * + * @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<String> lst = content.get(key); + if (lst != null) { + lst.add(value); + } else { + put(key, value); + } + } + + /** + * <p>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.</p> + * + * @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<String> values) { + List<String> lst = content.get(key); + if (lst != null) { + lst.addAll(values); + } else { + put(key, values); + } + } + + /** + * <p>Adds all the entries of the given map to this. This is the same as calling {@link #add(String, List)} for each + * entry in <tt>values</tt>.</p> + * + * @param values The values to be added to this. + */ + public void addAll(Map<? extends String, ? extends List<String>> values) { + for (Entry<? extends String, ? extends List<String>> entry : values.entrySet()) { + add(entry.getKey(), entry.getValue()); + } + } + + /** + * <p>Convenience method to call {@link #put(String, List)} with a singleton list that contains the specified + * value.</p> + * + * @param key The key of the entry to put. + * @param value The value to put. + * @return The previous value associated with <tt>key</tt>, or <tt>null</tt> if there was no mapping for + * <tt>key</tt>. + */ + public List<String> put(String key, String value) { + ArrayList<String> list = new ArrayList<String>(1); + list.add(value); + return content.put(key, list); + } + + @Override + public List<String> put(String key, List<String> value) { + return content.put(key, new ArrayList<>(value)); + } + + @Override + public void putAll(Map<? extends String, ? extends List<String>> values) { + for (Entry<? extends String, ? extends List<String>> entry : values.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @Override + public List<String> remove(Object key) { + return content.remove(key); + } + + /** + * <p>Removes the given value from the entry of the specified key.</p> + * + * @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<String> 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<String> get(Object key) { + return content.get(key); + } + + /** + * <p>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.</p> + * + * @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<String> lst = get(key); + if (lst == null || lst.isEmpty()) { + return null; + } + return lst.get(0); + } + + /** + * <p>Convenience method for checking whether or not a named header field is <em>true</em>. To satisfy this, the + * header field needs to have at least 1 entry, and Boolean.valueOf() of all its values must parse as + * <em>true</em>.</p> + * + * @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<String> lst = content.get(key); + if (lst == null) { + return false; + } + for (String value : lst) { + if (!Boolean.valueOf(value)) { + return false; + } + } + return true; + } + + @Override + public Set<String> keySet() { + return content.keySet(); + } + + @Override + public Collection<List<String>> values() { + return content.values(); + } + + @Override + public Set<Entry<String, List<String>>> entrySet() { + return content.entrySet(); + } + + @Override + public String toString() { + return content.toString(); + } + + /** + * <p>Returns an unmodifiable list of all key-value pairs of this. This provides a flattened view on the content of + * this map.</p> + * + * @return The collection of entries. + */ + public List<Entry<String, String>> entries() { + List<Entry<String, String>> list = new ArrayList<>(content.size()); + for (Entry<String, List<String>> 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<String, String> { + + 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 |