blob: 54e4e530d1517367ba7b3c3fbe834a15437521b8 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2014 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.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/* ------------------------------------------------------------ */
/** A multi valued Map.
* This Map specializes HashMap and provides methods
* that operate on multi valued items.
* <P>
* Implemented as a map of LazyList values
* @param <K> The key type of the map.
*
* @see LazyList
*
*/
public class MultiMap<K> implements ConcurrentMap<K,Object>, Serializable
{
private static final long serialVersionUID = -6878723138353851005L;
Map<K,Object> _map;
ConcurrentMap<K, Object> _cmap;
public MultiMap()
{
_map=new HashMap<K, Object>();
}
public MultiMap(Map<K,Object> map)
{
if (map instanceof ConcurrentMap)
_map=_cmap=new ConcurrentHashMap<K, Object>(map);
else
_map=new HashMap<K, Object>(map);
}
public MultiMap(MultiMap<K> map)
{
if (map._cmap!=null)
_map=_cmap=new ConcurrentHashMap<K, Object>(map._cmap);
else
_map=new HashMap<K,Object>(map._map);
}
public MultiMap(int capacity)
{
_map=new HashMap<K, Object>(capacity);
}
public MultiMap(boolean concurrent)
{
if (concurrent)
_map=_cmap=new ConcurrentHashMap<K, Object>();
else
_map=new HashMap<K, Object>();
}
/* ------------------------------------------------------------ */
/** Get multiple values.
* Single valued entries are converted to singleton lists.
* @param name The entry key.
* @return Unmodifieable List of values.
*/
public List getValues(Object name)
{
return LazyList.getList(_map.get(name),true);
}
/* ------------------------------------------------------------ */
/** 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 Object getValue(Object name,int i)
{
Object l=_map.get(name);
if (i==0 && LazyList.size(l)==0)
return null;
return LazyList.get(l,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(Object name)
{
Object l=_map.get(name);
switch(LazyList.size(l))
{
case 0:
return null;
case 1:
Object o=LazyList.get(l,0);
return o==null?null:o.toString();
default:
{
StringBuilder values=new StringBuilder(128);
for (int i=0; i<LazyList.size(l); i++)
{
Object e=LazyList.get(l,i);
if (e!=null)
{
if (values.length()>0)
values.append(',');
values.append(e.toString());
}
}
return values.toString();
}
}
}
/* ------------------------------------------------------------ */
public Object get(Object name)
{
Object l=_map.get(name);
switch(LazyList.size(l))
{
case 0:
return null;
case 1:
Object o=LazyList.get(l,0);
return o;
default:
return LazyList.getList(l,true);
}
}
/* ------------------------------------------------------------ */
/** Put and entry into the map.
* @param name The entry key.
* @param value The entry value.
* @return The previous value or null.
*/
public Object put(K name, Object value)
{
return _map.put(name,LazyList.add(null,value));
}
/* ------------------------------------------------------------ */
/** Put multi valued entry.
* @param name The entry key.
* @param values The List of multiple values.
* @return The previous value or null.
*/
public Object putValues(K name, List<? extends Object> values)
{
return _map.put(name,values);
}
/* ------------------------------------------------------------ */
/** Put multi valued entry.
* @param name The entry key.
* @param values The String array of multiple values.
* @return The previous value or null.
*/
public Object putValues(K name, String... values)
{
Object list=null;
for (int i=0;i<values.length;i++)
list=LazyList.add(list,values[i]);
return _map.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(K name, Object value)
{
Object lo = _map.get(name);
Object ln = LazyList.add(lo,value);
if (lo!=ln)
_map.put(name,ln);
}
/* ------------------------------------------------------------ */
/** 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(K name, List<? extends Object> values)
{
Object lo = _map.get(name);
Object ln = LazyList.addCollection(lo,values);
if (lo!=ln)
_map.put(name,ln);
}
/* ------------------------------------------------------------ */
/** 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(K name, String[] values)
{
Object lo = _map.get(name);
Object ln = LazyList.addCollection(lo,Arrays.asList(values));
if (lo!=ln)
_map.put(name,ln);
}
/* ------------------------------------------------------------ */
/** Remove value.
* @param name The entry key.
* @param value The entry value.
* @return true if it was removed.
*/
public boolean removeValue(K name,Object value)
{
Object lo = _map.get(name);
Object ln=lo;
int s=LazyList.size(lo);
if (s>0)
{
ln=LazyList.remove(lo,value);
if (ln==null)
_map.remove(name);
else
_map.put(name, ln);
}
return LazyList.size(ln)!=s;
}
/* ------------------------------------------------------------ */
/** Put all contents of map.
* @param m Map
*/
public void putAll(Map<? extends K, ? extends Object> m)
{
boolean multi = (m instanceof MultiMap);
if (multi)
{
for (Map.Entry<? extends K, ? extends Object> entry : m.entrySet())
{
_map.put(entry.getKey(),LazyList.clone(entry.getValue()));
}
}
else
{
_map.putAll(m);
}
}
/* ------------------------------------------------------------ */
/**
* @return Map of String arrays
*/
public Map<K,String[]> toStringArrayMap()
{
HashMap<K,String[]> map = new HashMap<K,String[]>(_map.size()*3/2)
{
public String toString()
{
StringBuilder b=new StringBuilder();
b.append('{');
for (K k:keySet())
{
if(b.length()>1)
b.append(',');
b.append(k);
b.append('=');
b.append(Arrays.asList(get(k)));
}
b.append('}');
return b.toString();
}
};
for(Map.Entry<K,Object> entry: _map.entrySet())
{
String[] a = LazyList.toStringArray(entry.getValue());
map.put(entry.getKey(),a);
}
return map;
}
@Override
public String toString()
{
return _cmap==null?_map.toString():_cmap.toString();
}
public void clear()
{
_map.clear();
}
public boolean containsKey(Object key)
{
return _map.containsKey(key);
}
public boolean containsValue(Object value)
{
return _map.containsValue(value);
}
public Set<Entry<K, Object>> entrySet()
{
return _map.entrySet();
}
@Override
public boolean equals(Object o)
{
return _map.equals(o);
}
@Override
public int hashCode()
{
return _map.hashCode();
}
public boolean isEmpty()
{
return _map.isEmpty();
}
public Set<K> keySet()
{
return _map.keySet();
}
public Object remove(Object key)
{
return _map.remove(key);
}
public int size()
{
return _map.size();
}
public Collection<Object> values()
{
return _map.values();
}
public Object putIfAbsent(K key, Object value)
{
if (_cmap==null)
throw new UnsupportedOperationException();
return _cmap.putIfAbsent(key,value);
}
public boolean remove(Object key, Object value)
{
if (_cmap==null)
throw new UnsupportedOperationException();
return _cmap.remove(key,value);
}
public boolean replace(K key, Object oldValue, Object newValue)
{
if (_cmap==null)
throw new UnsupportedOperationException();
return _cmap.replace(key,oldValue,newValue);
}
public Object replace(K key, Object value)
{
if (_cmap==null)
throw new UnsupportedOperationException();
return _cmap.replace(key,value);
}
}