1// 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} 416