View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.util;
20  
21  import java.io.Serializable;
22  import java.util.Arrays;
23  import java.util.Collection;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  import java.util.concurrent.ConcurrentHashMap;
29  import java.util.concurrent.ConcurrentMap;
30  
31  /* ------------------------------------------------------------ */
32  /** A multi valued Map.
33   * This Map specializes HashMap and provides methods
34   * that operate on multi valued items. 
35   * <P>
36   * Implemented as a map of LazyList values
37   * @param <K> The key type of the map.
38   *
39   * @see LazyList
40   * 
41   */
42  public class MultiMap<K> implements ConcurrentMap<K,Object>, Serializable
43  {
44      private static final long serialVersionUID = -6878723138353851005L;
45      Map<K,Object> _map;
46      ConcurrentMap<K, Object> _cmap;
47  
48      public MultiMap()
49      {
50          _map=new HashMap<K, Object>();
51      }
52      
53      public MultiMap(Map<K,Object> map)
54      {
55          if (map instanceof ConcurrentMap)
56              _map=_cmap=new ConcurrentHashMap<K, Object>(map);
57          else
58              _map=new HashMap<K, Object>(map);
59      }
60      
61      public MultiMap(MultiMap<K> map)
62      {
63          if (map._cmap!=null)
64              _map=_cmap=new ConcurrentHashMap<K, Object>(map._cmap);
65          else
66              _map=new HashMap<K,Object>(map._map);
67      }
68      
69      public MultiMap(int capacity)
70      {
71          _map=new HashMap<K, Object>(capacity);
72      }
73      
74      public MultiMap(boolean concurrent)
75      {
76          if (concurrent)
77              _map=_cmap=new ConcurrentHashMap<K, Object>();
78          else
79              _map=new HashMap<K, Object>();
80      }
81      
82  
83      /* ------------------------------------------------------------ */
84      /** Get multiple values.
85       * Single valued entries are converted to singleton lists.
86       * @param name The entry key. 
87       * @return Unmodifieable List of values.
88       */
89      public List<Object> getValues(Object name)
90      {
91          return LazyList.getList(_map.get(name),true);
92      }
93      
94      /* ------------------------------------------------------------ */
95      /** Get a value from a multiple value.
96       * If the value is not a multivalue, then index 0 retrieves the
97       * value or null.
98       * @param name The entry key.
99       * @param i Index of element to get.
100      * @return Unmodifieable List of values.
101      */
102     public Object getValue(Object name,int i)
103     {
104         Object l=_map.get(name);
105         if (i==0 && LazyList.size(l)==0)
106             return null;
107         return LazyList.get(l,i);
108     }
109     
110     
111     /* ------------------------------------------------------------ */
112     /** Get value as String.
113      * Single valued items are converted to a String with the toString()
114      * Object method. Multi valued entries are converted to a comma separated
115      * List.  No quoting of commas within values is performed.
116      * @param name The entry key. 
117      * @return String value.
118      */
119     public String getString(Object name)
120     {
121         Object l=_map.get(name);
122         switch(LazyList.size(l))
123         {
124           case 0:
125               return null;
126           case 1:
127               Object o=LazyList.get(l,0);
128               return o==null?null:o.toString();
129           default:
130           {
131               StringBuilder values=new StringBuilder(128);
132               for (int i=0; i<LazyList.size(l); i++)              
133               {
134                   Object e=LazyList.get(l,i);
135                   if (e!=null)
136                   {
137                       if (values.length()>0)
138                           values.append(',');
139                       values.append(e.toString());
140                   }
141               }   
142               return values.toString();
143           }
144         }
145     }
146     
147     /* ------------------------------------------------------------ */
148     public Object get(Object name) 
149     {
150         Object l=_map.get(name);
151         switch(LazyList.size(l))
152         {
153           case 0:
154               return null;
155           case 1:
156               Object o=LazyList.get(l,0);
157               return o;
158           default:
159               return LazyList.getList(l,true);
160         }
161     }
162     
163     /* ------------------------------------------------------------ */
164     /** Put and entry into the map.
165      * @param name The entry key. 
166      * @param value The entry value.
167      * @return The previous value or null.
168      */
169     public Object put(K name, Object value) 
170     {
171         return _map.put(name,LazyList.add(null,value));
172     }
173 
174     /* ------------------------------------------------------------ */
175     /** Put multi valued entry.
176      * @param name The entry key. 
177      * @param values The List of multiple values.
178      * @return The previous value or null.
179      */
180     public Object putValues(K name, List<? extends Object> values) 
181     {
182         return _map.put(name,values);
183     }
184     
185     /* ------------------------------------------------------------ */
186     /** Put multi valued entry.
187      * @param name The entry key. 
188      * @param values The String array of multiple values.
189      * @return The previous value or null.
190      */
191     public Object putValues(K name, String... values) 
192     {
193         Object list=null;
194         for (int i=0;i<values.length;i++)
195             list=LazyList.add(list,values[i]);
196         return _map.put(name,list);
197     }
198     
199     
200     /* ------------------------------------------------------------ */
201     /** Add value to multi valued entry.
202      * If the entry is single valued, it is converted to the first
203      * value of a multi valued entry.
204      * @param name The entry key. 
205      * @param value The entry value.
206      */
207     public void add(K name, Object value) 
208     {
209         Object lo = _map.get(name);
210         Object ln = LazyList.add(lo,value);
211         if (lo!=ln)
212             _map.put(name,ln);
213     }
214 
215     /* ------------------------------------------------------------ */
216     /** Add values to multi valued entry.
217      * If the entry is single valued, it is converted to the first
218      * value of a multi valued entry.
219      * @param name The entry key. 
220      * @param values The List of multiple values.
221      */
222     public void addValues(K name, List<? extends Object> values) 
223     {
224         Object lo = _map.get(name);
225         Object ln = LazyList.addCollection(lo,values);
226         if (lo!=ln)
227             _map.put(name,ln);
228     }
229     
230     /* ------------------------------------------------------------ */
231     /** Add values to multi valued entry.
232      * If the entry is single valued, it is converted to the first
233      * value of a multi valued entry.
234      * @param name The entry key. 
235      * @param values The String array of multiple values.
236      */
237     public void addValues(K name, String[] values) 
238     {
239         Object lo = _map.get(name);
240         Object ln = LazyList.addCollection(lo,Arrays.asList(values));
241         if (lo!=ln)
242             _map.put(name,ln);
243     }
244     
245     /* ------------------------------------------------------------ */
246     /** Remove value.
247      * @param name The entry key. 
248      * @param value The entry value. 
249      * @return true if it was removed.
250      */
251     public boolean removeValue(K name,Object value)
252     {
253         Object lo = _map.get(name);
254         Object ln=lo;
255         int s=LazyList.size(lo);
256         if (s>0)
257         {
258             ln=LazyList.remove(lo,value);
259             if (ln==null)
260                 _map.remove(name);
261             else
262                 _map.put(name, ln);
263         }
264         return LazyList.size(ln)!=s;
265     }
266     
267     
268     /* ------------------------------------------------------------ */
269     /** Put all contents of map.
270      * @param m Map
271      */
272     public void putAll(Map<? extends K, ? extends Object> m)
273     {
274         boolean multi = (m instanceof MultiMap);
275 
276         if (multi)
277         {
278             for (Map.Entry<? extends K, ? extends Object> entry : m.entrySet())
279             {
280                 _map.put(entry.getKey(),LazyList.clone(entry.getValue()));
281             }
282         }
283         else
284         {
285             _map.putAll(m);
286         }
287     }
288 
289     /* ------------------------------------------------------------ */
290     /** 
291      * @return Map of String arrays
292      */
293     public Map<K,String[]> toStringArrayMap()
294     {
295         HashMap<K,String[]> map = new HashMap<K,String[]>(_map.size()*3/2)
296         {
297             public String toString()
298             {
299                 StringBuilder b=new StringBuilder();
300                 b.append('{');
301                 for (K k:keySet())
302                 {
303                     if(b.length()>1)
304                         b.append(',');
305                     b.append(k);
306                     b.append('=');
307                     b.append(Arrays.asList(get(k)));
308                 }
309 
310                 b.append('}');
311                 return b.toString();
312             }
313         };
314         
315         for(Map.Entry<K,Object> entry: _map.entrySet())
316         {
317             String[] a = LazyList.toStringArray(entry.getValue());
318             map.put(entry.getKey(),a);
319         }
320         return map;
321     }
322 
323     @Override
324     public String toString()
325     {
326         return _cmap==null?_map.toString():_cmap.toString();
327     }
328     
329     public void clear()
330     {
331         _map.clear();
332     }
333 
334     public boolean containsKey(Object key)
335     {
336         return _map.containsKey(key);
337     }
338 
339     public boolean containsValue(Object value)
340     {
341         return _map.containsValue(value);
342     }
343 
344     public Set<Entry<K, Object>> entrySet()
345     {
346         return _map.entrySet();
347     }
348 
349     @Override
350     public boolean equals(Object o)
351     {
352         return _map.equals(o);
353     }
354 
355     @Override
356     public int hashCode()
357     {
358         return _map.hashCode();
359     }
360 
361     public boolean isEmpty()
362     {
363         return _map.isEmpty();
364     }
365 
366     public Set<K> keySet()
367     {
368         return _map.keySet();
369     }
370 
371     public Object remove(Object key)
372     {
373         return _map.remove(key);
374     }
375 
376     public int size()
377     {
378         return _map.size();
379     }
380 
381     public Collection<Object> values()
382     {
383         return _map.values();
384     }
385 
386     
387     
388     public Object putIfAbsent(K key, Object value)
389     {
390         if (_cmap==null)
391             throw new UnsupportedOperationException();
392         return _cmap.putIfAbsent(key,value);
393     }
394 
395     public boolean remove(Object key, Object value)
396     {
397         if (_cmap==null)
398             throw new UnsupportedOperationException();
399         return _cmap.remove(key,value);
400     }
401 
402     public boolean replace(K key, Object oldValue, Object newValue)
403     {
404         if (_cmap==null)
405             throw new UnsupportedOperationException();
406         return _cmap.replace(key,oldValue,newValue);
407     }
408 
409     public Object replace(K key, Object value)
410     {
411         if (_cmap==null)
412             throw new UnsupportedOperationException();
413         return _cmap.replace(key,value);
414     }
415 }