View Javadoc

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