blob: 54e4e530d1517367ba7b3c3fbe834a15437521b8 [file] [log] [blame]
Jake Slack03928ae2014-05-13 18:41:56 -07001//
2// ========================================================================
3// Copyright (c) 1995-2014 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
19package org.eclipse.jetty.util;
20
21import java.io.Serializable;
22import java.util.Arrays;
23import java.util.Collection;
24import java.util.HashMap;
25import java.util.List;
26import java.util.Map;
27import java.util.Set;
28import java.util.concurrent.ConcurrentHashMap;
29import 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 */
42public 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 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}