blob: dfca2faf4ef44bc16e05ee0f7d63444955f47f12 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 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.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* A multi valued Map.
* @param <V> the entry type for multimap values
*/
@SuppressWarnings("serial")
public class MultiMap<V> extends HashMap<String,List<V>>
{
public MultiMap()
{
super();
}
public MultiMap(Map<String,List<V>> map)
{
super(map);
}
public MultiMap(MultiMap<V> map)
{
super(map);
}
/* ------------------------------------------------------------ */
/** Get multiple values.
* Single valued entries are converted to singleton lists.
* @param name The entry key.
* @return Unmodifieable List of values.
*/
public List<V> getValues(String name)
{
List<V> vals = super.get(name);
if((vals == null) || vals.isEmpty()) {
return null;
}
return vals;
}
/* ------------------------------------------------------------ */
/** Get a value from a multiple value.
* If the value is not a multivalue, then index 0 retrieves the
* value or null.
* @param name The entry key.
* @param i Index of element to get.
* @return Unmodifieable List of values.
*/
public V getValue(String name,int i)
{
List<V> vals = getValues(name);
if(vals == null) {
return null;
}
if (i==0 && vals.isEmpty()) {
return null;
}
return vals.get(i);
}
/* ------------------------------------------------------------ */
/** Get value as String.
* Single valued items are converted to a String with the toString()
* Object method. Multi valued entries are converted to a comma separated
* List. No quoting of commas within values is performed.
* @param name The entry key.
* @return String value.
*/
public String getString(String name)
{
List<V> vals =get(name);
if ((vals == null) || (vals.isEmpty()))
{
return null;
}
if (vals.size() == 1)
{
// simple form.
return vals.get(0).toString();
}
// delimited form
StringBuilder values=new StringBuilder(128);
for (V e : vals)
{
if (e != null)
{
if (values.length() > 0)
values.append(',');
values.append(e.toString());
}
}
return values.toString();
}
/**
* Put multi valued entry.
* @param name The entry key.
* @param value The simple value
* @return The previous value or null.
*/
public List<V> put(String name, V value)
{
if(value == null) {
return super.put(name, null);
}
List<V> vals = new ArrayList<>();
vals.add(value);
return put(name,vals);
}
/**
* Shorthand version of putAll
* @param input the input map
*/
public void putAllValues(Map<String, V> input)
{
for(Map.Entry<String,V> entry: input.entrySet())
{
put(entry.getKey(), entry.getValue());
}
}
/* ------------------------------------------------------------ */
/** Put multi valued entry.
* @param name The entry key.
* @param values The List of multiple values.
* @return The previous value or null.
*/
public List<V> putValues(String name, List<V> values)
{
return super.put(name,values);
}
/* ------------------------------------------------------------ */
/** Put multi valued entry.
* @param name The entry key.
* @param values The array of multiple values.
* @return The previous value or null.
*/
@SafeVarargs
public final List<V> putValues(String name, V... values)
{
List<V> list = new ArrayList<>();
list.addAll(Arrays.asList(values));
return super.put(name,list);
}
/* ------------------------------------------------------------ */
/** Add value to multi valued entry.
* If the entry is single valued, it is converted to the first
* value of a multi valued entry.
* @param name The entry key.
* @param value The entry value.
*/
public void add(String name, V value)
{
List<V> lo = get(name);
if(lo == null) {
lo = new ArrayList<>();
}
lo.add(value);
super.put(name,lo);
}
/* ------------------------------------------------------------ */
/** Add values to multi valued entry.
* If the entry is single valued, it is converted to the first
* value of a multi valued entry.
* @param name The entry key.
* @param values The List of multiple values.
*/
public void addValues(String name, List<V> values)
{
List<V> lo = get(name);
if(lo == null) {
lo = new ArrayList<>();
}
lo.addAll(values);
put(name,lo);
}
/* ------------------------------------------------------------ */
/** Add values to multi valued entry.
* If the entry is single valued, it is converted to the first
* value of a multi valued entry.
* @param name The entry key.
* @param values The String array of multiple values.
*/
public void addValues(String name, V[] values)
{
List<V> lo = get(name);
if(lo == null) {
lo = new ArrayList<>();
}
lo.addAll(Arrays.asList(values));
put(name,lo);
}
/**
* Merge values.
*
* @param map
* the map to overlay on top of this one, merging together values if needed.
* @return true if an existing key was merged with potentially new values, false if either no change was made, or there were only new keys.
*/
public boolean addAllValues(MultiMap<V> map)
{
boolean merged = false;
if ((map == null) || (map.isEmpty()))
{
// done
return merged;
}
for (Map.Entry<String, List<V>> entry : map.entrySet())
{
String name = entry.getKey();
List<V> values = entry.getValue();
if (this.containsKey(name))
{
merged = true;
}
this.addValues(name,values);
}
return merged;
}
/* ------------------------------------------------------------ */
/** Remove value.
* @param name The entry key.
* @param value The entry value.
* @return true if it was removed.
*/
public boolean removeValue(String name,V value)
{
List<V> lo = get(name);
if((lo == null)||(lo.isEmpty())) {
return false;
}
boolean ret = lo.remove(value);
if(lo.isEmpty()) {
remove(name);
} else {
put(name,lo);
}
return ret;
}
/**
* Test for a specific single value in the map.
* <p>
* NOTE: This is a SLOW operation, and is actively discouraged.
* @param value the value to search for
* @return true if contains simple value
*/
public boolean containsSimpleValue(V value)
{
for (List<V> vals : values())
{
if ((vals.size() == 1) && vals.contains(value))
{
return true;
}
}
return false;
}
@Override
public String toString()
{
Iterator<Entry<String, List<V>>> iter = entrySet().iterator();
StringBuilder sb = new StringBuilder();
sb.append('{');
boolean delim = false;
while (iter.hasNext())
{
Entry<String, List<V>> e = iter.next();
if (delim)
{
sb.append(", ");
}
String key = e.getKey();
List<V> vals = e.getValue();
sb.append(key);
sb.append('=');
if (vals.size() == 1)
{
sb.append(vals.get(0));
}
else
{
sb.append(vals);
}
delim = true;
}
sb.append('}');
return sb.toString();
}
/* ------------------------------------------------------------ */
/**
* @return Map of String arrays
*/
public Map<String,String[]> toStringArrayMap()
{
HashMap<String,String[]> map = new HashMap<String,String[]>(size()*3/2)
{
@Override
public String toString()
{
StringBuilder b=new StringBuilder();
b.append('{');
for (String k:super.keySet())
{
if(b.length()>1)
b.append(',');
b.append(k);
b.append('=');
b.append(Arrays.asList(super.get(k)));
}
b.append('}');
return b.toString();
}
};
for(Map.Entry<String,List<V>> entry: entrySet())
{
String[] a = null;
if (entry.getValue() != null)
{
a = new String[entry.getValue().size()];
a = entry.getValue().toArray(a);
}
map.put(entry.getKey(),a);
}
return map;
}
}