view src/org/eclipse/jetty/util/MultiMap.java @ 1022:3718afd99988

HttpHeaders uses StringCache
author Franklin Schmidt <fschmidt@gmail.com>
date Tue, 01 Nov 2016 01:04:46 -0600
parents 3428c60d7cfc
children
line wrap: on
line source

//
//  ========================================================================
//  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);
    }
}