com.ning.http.client.FluentCaseInsensitiveStringsMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payment-retries-plugin Show documentation
Show all versions of payment-retries-plugin Show documentation
Kill Bill Payment Retries plugin
The newest version!
/*
* Copyright 2010 Ning, Inc.
*
* Ning licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
*/
package com.ning.http.client;
import static com.ning.http.util.MiscUtils.isNonEmpty;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
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;
/**
* An implementation of a {@code String -> List} map that adds a fluent interface, i.e. methods that
* return this instance. This class differs from {@link FluentStringsMap} in that keys are treated in an
* case-insensitive matter, i.e. case of the key doesn't matter when retrieving values or changing the map.
* However, the map preserves the key case (of the first insert or replace) and returns the keys in their
* original case in the appropriate methods (e.g. {@link FluentCaseInsensitiveStringsMap#keySet()}).
*/
public class FluentCaseInsensitiveStringsMap implements Map>, Iterable>> {
private final Map> values = new LinkedHashMap<>();
private final Map keyLookup = new LinkedHashMap<>();
public FluentCaseInsensitiveStringsMap() {
}
public FluentCaseInsensitiveStringsMap(FluentCaseInsensitiveStringsMap src) {
if (src != null) {
for (Map.Entry> header : src) {
add(header.getKey(), header.getValue());
}
}
}
public FluentCaseInsensitiveStringsMap(Map> src) {
if (src != null) {
for (Map.Entry> header : src.entrySet()) {
add(header.getKey(), header.getValue());
}
}
}
public FluentCaseInsensitiveStringsMap add(String key, String value) {
if (key != null) {
String lcKey = key.toLowerCase(Locale.ENGLISH);
String realKey = keyLookup.get(lcKey);
List curValues = null;
if (realKey == null) {
keyLookup.put(lcKey, key);
curValues = new ArrayList<>();
values.put(key, curValues);
} else {
curValues = values.get(realKey);
}
String nonNullValue = value != null? value : "";
curValues.add(nonNullValue);
}
return this;
}
/**
* Adds the specified values and returns this object.
*
* @param key The key
* @param values The value(s); if the array is null then this method has no effect. Individual null values are turned into empty strings
* @return This object
*/
public FluentCaseInsensitiveStringsMap add(String key, String... values) {
if (isNonEmpty(values)) {
add(key, Arrays.asList(values));
}
return this;
}
private List fetchValues(Collection values) {
List result = null;
if (values != null) {
for (String value : values) {
if (value == null) {
value = "";
}
if (result == null) {
// lazy initialization
result = new ArrayList<>();
}
result.add(value);
}
}
return result;
}
/**
* Adds the specified values and returns this object.
*
* @param key The key
* @param values The value(s); if null then this method has no effect. Use an empty collection
* to generate an empty value
* @return This object
*/
public FluentCaseInsensitiveStringsMap add(String key, Collection values) {
if (key != null) {
List nonNullValues = fetchValues(values);
if (nonNullValues != null) {
String lcKey = key.toLowerCase(Locale.ENGLISH);
String realKey = keyLookup.get(lcKey);
List curValues = null;
if (realKey == null) {
realKey = key;
keyLookup.put(lcKey, key);
} else {
curValues = this.values.get(realKey);
}
if (curValues == null) {
curValues = new ArrayList<>();
this.values.put(realKey, curValues);
}
curValues.addAll(nonNullValues);
}
}
return this;
}
/**
* Adds all key-values pairs from the given object to this object and returns this object.
*
* @param src The source object
* @return This object
*/
public FluentCaseInsensitiveStringsMap addAll(FluentCaseInsensitiveStringsMap src) {
if (src != null) {
for (Map.Entry> header : src) {
add(header.getKey(), header.getValue());
}
}
return this;
}
/**
* Adds all key-values pairs from the given map to this object and returns this object.
*
* @param src The source map
* @return This object
*/
public FluentCaseInsensitiveStringsMap addAll(Map> src) {
if (src != null) {
for (Map.Entry> header : src.entrySet()) {
add(header.getKey(), header.getValue());
}
}
return this;
}
/**
* Replaces the values for the given key with the given values.
*
* @param key The key
* @param values The new values
* @return This object
*/
public FluentCaseInsensitiveStringsMap replaceWith(final String key, final String... values) {
return replaceWith(key, Arrays.asList(values));
}
/**
* Replaces the values for the given key with the given values.
*
* @param key The key
* @param values The new values
* @return This object
*/
public FluentCaseInsensitiveStringsMap replaceWith(final String key, final Collection values) {
if (key != null) {
List nonNullValues = fetchValues(values);
String lcKkey = key.toLowerCase(Locale.ENGLISH);
String realKey = keyLookup.get(lcKkey);
if (nonNullValues == null) {
keyLookup.remove(lcKkey);
if (realKey != null) {
this.values.remove(realKey);
}
} else {
if (!key.equals(realKey)) {
keyLookup.put(lcKkey, key);
this.values.remove(realKey);
}
this.values.put(key, nonNullValues);
}
}
return this;
}
/**
* Replace the values for all keys from the given map that are also present in this object, with the values from the given map.
* All key-values from the given object that are not present in this object, will be added to it.
*
* @param src The source object
* @return This object
*/
public FluentCaseInsensitiveStringsMap replaceAll(FluentCaseInsensitiveStringsMap src) {
if (src != null) {
for (Map.Entry> header : src) {
replaceWith(header.getKey(), header.getValue());
}
}
return this;
}
/**
* Replace the values for all keys from the given map that are also present in this object, with the values from the given map.
* All key-values from the given object that are not present in this object, will be added to it.
*
* @param src The source map
* @return This object
*/
public FluentCaseInsensitiveStringsMap replaceAll(Map extends String, ? extends Collection> src) {
if (src != null) {
for (Map.Entry extends String, ? extends Collection> header : src.entrySet()) {
replaceWith(header.getKey(), header.getValue());
}
}
return this;
}
@Override
public List put(String key, List value) {
if (key == null) {
throw new NullPointerException("Null keys are not allowed");
}
List oldValue = get(key);
replaceWith(key, value);
return oldValue;
}
@Override
public void putAll(Map extends String, ? extends List> values) {
replaceAll(values);
}
/**
* Removes the values for the given key if present and returns this object.
*
* @param key The key
* @return This object
*/
public FluentCaseInsensitiveStringsMap delete(String key) {
if (key != null) {
String lcKey = key.toLowerCase(Locale.ENGLISH);
String realKey = keyLookup.remove(lcKey);
if (realKey != null) {
values.remove(realKey);
}
}
return this;
}
/**
* Removes the values for the given keys if present and returns this object.
*
* @param keys The keys
* @return This object
*/
public FluentCaseInsensitiveStringsMap deleteAll(String... keys) {
if (keys != null) {
for (String key : keys) {
remove(key);
}
}
return this;
}
/**
* Removes the values for the given keys if present and returns this object.
*
* @param keys The keys
* @return This object
*/
public FluentCaseInsensitiveStringsMap deleteAll(Collection keys) {
if (keys != null) {
for (String key : keys) {
remove(key);
}
}
return this;
}
@Override
public List remove(Object key) {
if (key == null) {
return null;
} else {
List oldValues = get(key.toString());
delete(key.toString());
return oldValues;
}
}
@Override
public void clear() {
keyLookup.clear();
values.clear();
}
@Override
public Iterator>> iterator() {
return Collections.unmodifiableSet(values.entrySet()).iterator();
}
@Override
public Set keySet() {
return new LinkedHashSet<>(keyLookup.values());
}
@Override
public Set>> entrySet() {
return values.entrySet();
}
@Override
public int size() {
return values.size();
}
@Override
public boolean isEmpty() {
return values.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return key == null ? false : keyLookup.containsKey(key.toString().toLowerCase(Locale.ENGLISH));
}
@Override
public boolean containsValue(Object value) {
return values.containsValue(value);
}
/**
* Returns the value for the given key. If there are multiple values for this key,
* then only the first one will be returned.
*
* @param key The key
* @return The first value
*/
public String getFirstValue(String key) {
List values = get(key);
if (values.isEmpty()) {
return null;
} else {
return values.get(0);
}
}
/**
* Returns the values for the given key joined into a single string using the given delimiter.
*
* @param key The key
* @return The value as a single string
*/
public String getJoinedValue(String key, String delimiter) {
List values = get(key);
if (values.isEmpty()) {
return null;
} else if (values.size() == 1) {
return values.get(0);
} else {
StringBuilder result = new StringBuilder();
for (String value : values) {
if (result.length() > 0) {
result.append(delimiter);
}
result.append(value);
}
return result.toString();
}
}
@Override
public List get(Object key) {
if (key == null)
return Collections.emptyList();
String lcKey = key.toString().toLowerCase(Locale.ENGLISH);
String realKey = keyLookup.get(lcKey);
return realKey != null ? values.get(realKey) : Collections. emptyList();
}
@Override
public Collection> values() {
return values.values();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final FluentCaseInsensitiveStringsMap other = (FluentCaseInsensitiveStringsMap) obj;
if (values == null) {
if (other.values != null) {
return false;
}
} else if (!values.equals(other.values)) {
return false;
}
return true;
}
@Override
public int hashCode() {
return values == null ? 0 : values.hashCode();
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
for (Map.Entry> entry : values.entrySet()) {
if (result.length() > 0) {
result.append("; ");
}
result.append("\"");
result.append(entry.getKey());
result.append("=");
boolean needsComma = false;
for (String value : entry.getValue()) {
if (needsComma) {
result.append(", ");
} else {
needsComma = true;
}
result.append(value);
}
result.append("\"");
}
return result.toString();
}
}