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.util.Arrays;
17 import java.util.Collection;
18 import java.util.HashMap;
19 import java.util.Iterator;
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 *
33 * @see LazyList
34 *
35 */
36 public class MultiMap<K> implements ConcurrentMap<K,Object>
37 {
38 Map<K,Object> _map;
39 ConcurrentMap<K, Object> _cmap;
40
41 public MultiMap()
42 {
43 _map=new HashMap<K, Object>();
44 }
45
46 public MultiMap(Map map)
47 {
48 if (map instanceof ConcurrentMap)
49 _map=_cmap=new ConcurrentHashMap<K, Object>(map);
50 else
51 _map=new HashMap<K, Object>(map);
52 }
53
54 public MultiMap(int capacity)
55 {
56 _map=new HashMap<K, Object>(capacity);
57 }
58
59 public MultiMap(boolean concurrent)
60 {
61 if (concurrent)
62 _map=_cmap=new ConcurrentHashMap<K, Object>();
63 else
64 _map=new HashMap<K, Object>();
65 }
66
67
68 /* ------------------------------------------------------------ */
69 /** Get multiple values.
70 * Single valued entries are converted to singleton lists.
71 * @param name The entry key.
72 * @return Unmodifieable List of values.
73 */
74 public List getValues(Object name)
75 {
76 return LazyList.getList(_map.get(name),true);
77 }
78
79 /* ------------------------------------------------------------ */
80 /** Get a value from a multiple value.
81 * If the value is not a multivalue, then index 0 retrieves the
82 * value or null.
83 * @param name The entry key.
84 * @param i Index of element to get.
85 * @return Unmodifieable List of values.
86 */
87 public Object getValue(Object name,int i)
88 {
89 Object l=_map.get(name);
90 if (i==0 && LazyList.size(l)==0)
91 return null;
92 return LazyList.get(l,i);
93 }
94
95
96 /* ------------------------------------------------------------ */
97 /** Get value as String.
98 * Single valued items are converted to a String with the toString()
99 * Object method. Multi valued entries are converted to a comma separated
100 * List. No quoting of commas within values is performed.
101 * @param name The entry key.
102 * @return String value.
103 */
104 public String getString(Object name)
105 {
106 Object l=_map.get(name);
107 switch(LazyList.size(l))
108 {
109 case 0:
110 return null;
111 case 1:
112 Object o=LazyList.get(l,0);
113 return o==null?null:o.toString();
114 default:
115 {
116 StringBuilder values=new StringBuilder(128);
117 for (int i=0; i<LazyList.size(l); i++)
118 {
119 Object e=LazyList.get(l,i);
120 if (e!=null)
121 {
122 if (values.length()>0)
123 values.append(',');
124 values.append(e.toString());
125 }
126 }
127 return values.toString();
128 }
129 }
130 }
131
132 /* ------------------------------------------------------------ */
133 public Object get(Object name)
134 {
135 Object l=_map.get(name);
136 switch(LazyList.size(l))
137 {
138 case 0:
139 return null;
140 case 1:
141 Object o=LazyList.get(l,0);
142 return o;
143 default:
144 return LazyList.getList(l,true);
145 }
146 }
147
148 /* ------------------------------------------------------------ */
149 /** Put and entry into the map.
150 * @param name The entry key.
151 * @param value The entry value.
152 * @return The previous value or null.
153 */
154 public Object put(K name, Object value)
155 {
156 return _map.put(name,LazyList.add(null,value));
157 }
158
159 /* ------------------------------------------------------------ */
160 /** Put multi valued entry.
161 * @param name The entry key.
162 * @param values The List of multiple values.
163 * @return The previous value or null.
164 */
165 public Object putValues(K name, List values)
166 {
167 return _map.put(name,values);
168 }
169
170 /* ------------------------------------------------------------ */
171 /** Put multi valued entry.
172 * @param name The entry key.
173 * @param values The String array of multiple values.
174 * @return The previous value or null.
175 */
176 public Object putValues(K name, String[] values)
177 {
178 Object list=null;
179 for (int i=0;i<values.length;i++)
180 list=LazyList.add(list,values[i]);
181 return put(name,list);
182 }
183
184
185 /* ------------------------------------------------------------ */
186 /** Add value to multi valued entry.
187 * If the entry is single valued, it is converted to the first
188 * value of a multi valued entry.
189 * @param name The entry key.
190 * @param value The entry value.
191 */
192 public void add(K name, Object value)
193 {
194 Object lo = _map.get(name);
195 Object ln = LazyList.add(lo,value);
196 if (lo!=ln)
197 _map.put(name,ln);
198 }
199
200 /* ------------------------------------------------------------ */
201 /** Add values 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 values The List of multiple values.
206 */
207 public void addValues(K name, List values)
208 {
209 Object lo = _map.get(name);
210 Object ln = LazyList.addCollection(lo,values);
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 String array of multiple values.
221 */
222 public void addValues(K name, String[] values)
223 {
224 Object lo = _map.get(name);
225 Object ln = LazyList.addCollection(lo,Arrays.asList(values));
226 if (lo!=ln)
227 _map.put(name,ln);
228 }
229
230 /* ------------------------------------------------------------ */
231 /** Remove value.
232 * @param name The entry key.
233 * @param value The entry value.
234 * @return true if it was removed.
235 */
236 public boolean removeValue(K name,Object value)
237 {
238 Object lo = _map.get(name);
239 Object ln=lo;
240 int s=LazyList.size(lo);
241 if (s>0)
242 {
243 ln=LazyList.remove(lo,value);
244 if (ln==null)
245 _map.remove(name);
246 else
247 _map.put(name, ln);
248 }
249 return LazyList.size(ln)!=s;
250 }
251
252 /* ------------------------------------------------------------ */
253 /** Put all contents of map.
254 * @param m Map
255 */
256 public void putAll(Map m)
257 {
258 Iterator i = m.entrySet().iterator();
259 boolean multi=m instanceof MultiMap;
260 while(i.hasNext())
261 {
262 Map.Entry entry = (Map.Entry)i.next();
263 if (multi)
264 _map.put((K)(entry.getKey()),LazyList.clone(entry.getValue()));
265 else
266 put((K)(entry.getKey()),entry.getValue());
267 }
268 }
269
270 /* ------------------------------------------------------------ */
271 /**
272 * @return Map of String arrays
273 */
274 public Map toStringArrayMap()
275 {
276 HashMap map = new HashMap(_map.size()*3/2);
277
278 Iterator i = _map.entrySet().iterator();
279 while(i.hasNext())
280 {
281 Map.Entry entry = (Map.Entry)i.next();
282 Object l = entry.getValue();
283 String[] a = LazyList.toStringArray(l);
284 // for (int j=a.length;j-->0;)
285 // if (a[j]==null)
286 // a[j]="";
287 map.put(entry.getKey(),a);
288 }
289 return map;
290 }
291
292 public void clear()
293 {
294 _map.clear();
295 }
296
297 public boolean containsKey(Object key)
298 {
299 return _map.containsKey(key);
300 }
301
302 public boolean containsValue(Object value)
303 {
304 return _map.containsValue(value);
305 }
306
307 public Set<Entry<K, Object>> entrySet()
308 {
309 return _map.entrySet();
310 }
311
312 @Override
313 public boolean equals(Object o)
314 {
315 return _map.equals(o);
316 }
317
318 @Override
319 public int hashCode()
320 {
321 return _map.hashCode();
322 }
323
324 public boolean isEmpty()
325 {
326 return _map.isEmpty();
327 }
328
329 public Set<K> keySet()
330 {
331 return _map.keySet();
332 }
333
334 public Object remove(Object key)
335 {
336 return _map.remove(key);
337 }
338
339 public int size()
340 {
341 return _map.size();
342 }
343
344 public Collection<Object> values()
345 {
346 return _map.values();
347 }
348
349
350
351 public Object putIfAbsent(K key, Object value)
352 {
353 if (_cmap==null)
354 throw new UnsupportedOperationException();
355 return _cmap.putIfAbsent(key,value);
356 }
357
358 public boolean remove(Object key, Object value)
359 {
360 if (_cmap==null)
361 throw new UnsupportedOperationException();
362 return _cmap.remove(key,value);
363 }
364
365 public boolean replace(K key, Object oldValue, Object newValue)
366 {
367 if (_cmap==null)
368 throw new UnsupportedOperationException();
369 return _cmap.replace(key,oldValue,newValue);
370 }
371
372 public Object replace(K key, Object value)
373 {
374 if (_cmap==null)
375 throw new UnsupportedOperationException();
376 return _cmap.replace(key,value);
377 }
378
379
380 }