1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.collections; 18 19 import java.io.PrintStream; 20 import java.text.NumberFormat; 21 import java.text.ParseException; 22 import java.util.Collections; 23 import java.util.Enumeration; 24 import java.util.HashMap; 25 import java.util.Iterator; 26 import java.util.Map; 27 import java.util.Properties; 28 import java.util.ResourceBundle; 29 import java.util.SortedMap; 30 import java.util.TreeMap; 31 32 import org.apache.commons.collections.map.FixedSizeMap; 33 import org.apache.commons.collections.map.FixedSizeSortedMap; 34 import org.apache.commons.collections.map.LazyMap; 35 import org.apache.commons.collections.map.LazySortedMap; 36 import org.apache.commons.collections.map.ListOrderedMap; 37 import org.apache.commons.collections.map.MultiValueMap; 38 import org.apache.commons.collections.map.PredicatedMap; 39 import org.apache.commons.collections.map.PredicatedSortedMap; 40 import org.apache.commons.collections.map.TransformedMap; 41 import org.apache.commons.collections.map.TransformedSortedMap; 42 import org.apache.commons.collections.map.TypedMap; 43 import org.apache.commons.collections.map.TypedSortedMap; 44 import org.apache.commons.collections.map.UnmodifiableMap; 45 import org.apache.commons.collections.map.UnmodifiableSortedMap; 46 47 /** 48 * Provides utility methods and decorators for 49 * {@link Map} and {@link SortedMap} instances. 50 * <p> 51 * It contains various type safe methods 52 * as well as other useful features like deep copying. 53 * <p> 54 * It also provides the following decorators: 55 * 56 * <ul> 57 * <li>{@link #fixedSizeMap(Map)} 58 * <li>{@link #fixedSizeSortedMap(SortedMap)} 59 * <li>{@link #lazyMap(Map,Factory)} 60 * <li>{@link #lazyMap(Map,Transformer)} 61 * <li>{@link #lazySortedMap(SortedMap,Factory)} 62 * <li>{@link #lazySortedMap(SortedMap,Transformer)} 63 * <li>{@link #predicatedMap(Map,Predicate,Predicate)} 64 * <li>{@link #predicatedSortedMap(SortedMap,Predicate,Predicate)} 65 * <li>{@link #transformedMap(Map, Transformer, Transformer)} 66 * <li>{@link #transformedSortedMap(SortedMap, Transformer, Transformer)} 67 * <li>{@link #typedMap(Map, Class, Class)} 68 * <li>{@link #typedSortedMap(SortedMap, Class, Class)} 69 * <li>{@link #multiValueMap( Map )} 70 * <li>{@link #multiValueMap( Map, Class )} 71 * <li>{@link #multiValueMap( Map, Factory )} 72 * </ul> 73 * 74 * @since Commons Collections 1.0 75 * @version $Revision: 1713178 $ $Date: 2015-11-07 21:59:09 +0100 (Sat, 07 Nov 2015) $ 76 * 77 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> 78 * @author <a href="mailto:nissim@nksystems.com">Nissim Karpenstein</a> 79 * @author <a href="mailto:knielsen@apache.org">Kasper Nielsen</a> 80 * @author Paul Jack 81 * @author Stephen Colebourne 82 * @author Matthew Hawthorne 83 * @author Arun Mammen Thomas 84 * @author Janek Bogucki 85 * @author Max Rydahl Andersen 86 * @author <a href="mailto:equinus100@hotmail.com">Ashwin S</a> 87 * @author <a href="mailto:jcarman@apache.org">James Carman</a> 88 * @author Neil O'Toole 89 */ 90 public class MapUtils { 91 92 /** 93 * An empty unmodifiable map. 94 * This was not provided in JDK1.2. 95 */ 96 public static final Map EMPTY_MAP = UnmodifiableMap.decorate(new HashMap(1)); 97 /** 98 * An empty unmodifiable sorted map. 99 * This is not provided in the JDK. 100 */ 101 public static final SortedMap EMPTY_SORTED_MAP = UnmodifiableSortedMap.decorate(new TreeMap()); 102 /** 103 * String used to indent the verbose and debug Map prints. 104 */ 105 private static final String INDENT_STRING = " "; 106 107 /** 108 * <code>MapUtils</code> should not normally be instantiated. 109 */ MapUtils()110 public MapUtils() { 111 } 112 113 // Type safe getters 114 //------------------------------------------------------------------------- 115 /** 116 * Gets from a Map in a null-safe manner. 117 * 118 * @param map the map to use 119 * @param key the key to look up 120 * @return the value in the Map, <code>null</code> if null map input 121 */ getObject(final Map map, final Object key)122 public static Object getObject(final Map map, final Object key) { 123 if (map != null) { 124 return map.get(key); 125 } 126 return null; 127 } 128 129 /** 130 * Gets a String from a Map in a null-safe manner. 131 * <p> 132 * The String is obtained via <code>toString</code>. 133 * 134 * @param map the map to use 135 * @param key the key to look up 136 * @return the value in the Map as a String, <code>null</code> if null map input 137 */ getString(final Map map, final Object key)138 public static String getString(final Map map, final Object key) { 139 if (map != null) { 140 Object answer = map.get(key); 141 if (answer != null) { 142 return answer.toString(); 143 } 144 } 145 return null; 146 } 147 148 /** 149 * Gets a Boolean from a Map in a null-safe manner. 150 * <p> 151 * If the value is a <code>Boolean</code> it is returned directly. 152 * If the value is a <code>String</code> and it equals 'true' ignoring case 153 * then <code>true</code> is returned, otherwise <code>false</code>. 154 * If the value is a <code>Number</code> an integer zero value returns 155 * <code>false</code> and non-zero returns <code>true</code>. 156 * Otherwise, <code>null</code> is returned. 157 * 158 * @param map the map to use 159 * @param key the key to look up 160 * @return the value in the Map as a Boolean, <code>null</code> if null map input 161 */ getBoolean(final Map map, final Object key)162 public static Boolean getBoolean(final Map map, final Object key) { 163 if (map != null) { 164 Object answer = map.get(key); 165 if (answer != null) { 166 if (answer instanceof Boolean) { 167 return (Boolean) answer; 168 169 } else if (answer instanceof String) { 170 return new Boolean((String) answer); 171 172 } else if (answer instanceof Number) { 173 Number n = (Number) answer; 174 return (n.intValue() != 0) ? Boolean.TRUE : Boolean.FALSE; 175 } 176 } 177 } 178 return null; 179 } 180 181 /** 182 * Gets a Number from a Map in a null-safe manner. 183 * <p> 184 * If the value is a <code>Number</code> it is returned directly. 185 * If the value is a <code>String</code> it is converted using 186 * {@link NumberFormat#parse(String)} on the system default formatter 187 * returning <code>null</code> if the conversion fails. 188 * Otherwise, <code>null</code> is returned. 189 * 190 * @param map the map to use 191 * @param key the key to look up 192 * @return the value in the Map as a Number, <code>null</code> if null map input 193 */ getNumber(final Map map, final Object key)194 public static Number getNumber(final Map map, final Object key) { 195 if (map != null) { 196 Object answer = map.get(key); 197 if (answer != null) { 198 if (answer instanceof Number) { 199 return (Number) answer; 200 201 } else if (answer instanceof String) { 202 try { 203 String text = (String) answer; 204 return NumberFormat.getInstance().parse(text); 205 206 } catch (ParseException e) { 207 // failure means null is returned 208 } 209 } 210 } 211 } 212 return null; 213 } 214 215 /** 216 * Gets a Byte from a Map in a null-safe manner. 217 * <p> 218 * The Byte is obtained from the results of {@link #getNumber(Map,Object)}. 219 * 220 * @param map the map to use 221 * @param key the key to look up 222 * @return the value in the Map as a Byte, <code>null</code> if null map input 223 */ getByte(final Map map, final Object key)224 public static Byte getByte(final Map map, final Object key) { 225 Number answer = getNumber(map, key); 226 if (answer == null) { 227 return null; 228 } else if (answer instanceof Byte) { 229 return (Byte) answer; 230 } 231 return new Byte(answer.byteValue()); 232 } 233 234 /** 235 * Gets a Short from a Map in a null-safe manner. 236 * <p> 237 * The Short is obtained from the results of {@link #getNumber(Map,Object)}. 238 * 239 * @param map the map to use 240 * @param key the key to look up 241 * @return the value in the Map as a Short, <code>null</code> if null map input 242 */ getShort(final Map map, final Object key)243 public static Short getShort(final Map map, final Object key) { 244 Number answer = getNumber(map, key); 245 if (answer == null) { 246 return null; 247 } else if (answer instanceof Short) { 248 return (Short) answer; 249 } 250 return new Short(answer.shortValue()); 251 } 252 253 /** 254 * Gets a Integer from a Map in a null-safe manner. 255 * <p> 256 * The Integer is obtained from the results of {@link #getNumber(Map,Object)}. 257 * 258 * @param map the map to use 259 * @param key the key to look up 260 * @return the value in the Map as a Integer, <code>null</code> if null map input 261 */ getInteger(final Map map, final Object key)262 public static Integer getInteger(final Map map, final Object key) { 263 Number answer = getNumber(map, key); 264 if (answer == null) { 265 return null; 266 } else if (answer instanceof Integer) { 267 return (Integer) answer; 268 } 269 return new Integer(answer.intValue()); 270 } 271 272 /** 273 * Gets a Long from a Map in a null-safe manner. 274 * <p> 275 * The Long is obtained from the results of {@link #getNumber(Map,Object)}. 276 * 277 * @param map the map to use 278 * @param key the key to look up 279 * @return the value in the Map as a Long, <code>null</code> if null map input 280 */ getLong(final Map map, final Object key)281 public static Long getLong(final Map map, final Object key) { 282 Number answer = getNumber(map, key); 283 if (answer == null) { 284 return null; 285 } else if (answer instanceof Long) { 286 return (Long) answer; 287 } 288 return new Long(answer.longValue()); 289 } 290 291 /** 292 * Gets a Float from a Map in a null-safe manner. 293 * <p> 294 * The Float is obtained from the results of {@link #getNumber(Map,Object)}. 295 * 296 * @param map the map to use 297 * @param key the key to look up 298 * @return the value in the Map as a Float, <code>null</code> if null map input 299 */ getFloat(final Map map, final Object key)300 public static Float getFloat(final Map map, final Object key) { 301 Number answer = getNumber(map, key); 302 if (answer == null) { 303 return null; 304 } else if (answer instanceof Float) { 305 return (Float) answer; 306 } 307 return new Float(answer.floatValue()); 308 } 309 310 /** 311 * Gets a Double from a Map in a null-safe manner. 312 * <p> 313 * The Double is obtained from the results of {@link #getNumber(Map,Object)}. 314 * 315 * @param map the map to use 316 * @param key the key to look up 317 * @return the value in the Map as a Double, <code>null</code> if null map input 318 */ getDouble(final Map map, final Object key)319 public static Double getDouble(final Map map, final Object key) { 320 Number answer = getNumber(map, key); 321 if (answer == null) { 322 return null; 323 } else if (answer instanceof Double) { 324 return (Double) answer; 325 } 326 return new Double(answer.doubleValue()); 327 } 328 329 /** 330 * Gets a Map from a Map in a null-safe manner. 331 * <p> 332 * If the value returned from the specified map is not a Map then 333 * <code>null</code> is returned. 334 * 335 * @param map the map to use 336 * @param key the key to look up 337 * @return the value in the Map as a Map, <code>null</code> if null map input 338 */ getMap(final Map map, final Object key)339 public static Map getMap(final Map map, final Object key) { 340 if (map != null) { 341 Object answer = map.get(key); 342 if (answer != null && answer instanceof Map) { 343 return (Map) answer; 344 } 345 } 346 return null; 347 } 348 349 // Type safe getters with default values 350 //------------------------------------------------------------------------- 351 /** 352 * Looks up the given key in the given map, converting null into the 353 * given default value. 354 * 355 * @param map the map whose value to look up 356 * @param key the key of the value to look up in that map 357 * @param defaultValue what to return if the value is null 358 * @return the value in the map, or defaultValue if the original value 359 * is null or the map is null 360 */ getObject( Map map, Object key, Object defaultValue )361 public static Object getObject( Map map, Object key, Object defaultValue ) { 362 if ( map != null ) { 363 Object answer = map.get( key ); 364 if ( answer != null ) { 365 return answer; 366 } 367 } 368 return defaultValue; 369 } 370 371 /** 372 * Looks up the given key in the given map, converting the result into 373 * a string, using the default value if the the conversion fails. 374 * 375 * @param map the map whose value to look up 376 * @param key the key of the value to look up in that map 377 * @param defaultValue what to return if the value is null or if the 378 * conversion fails 379 * @return the value in the map as a string, or defaultValue if the 380 * original value is null, the map is null or the string conversion 381 * fails 382 */ getString( Map map, Object key, String defaultValue )383 public static String getString( Map map, Object key, String defaultValue ) { 384 String answer = getString( map, key ); 385 if ( answer == null ) { 386 answer = defaultValue; 387 } 388 return answer; 389 } 390 391 /** 392 * Looks up the given key in the given map, converting the result into 393 * a boolean, using the default value if the the conversion fails. 394 * 395 * @param map the map whose value to look up 396 * @param key the key of the value to look up in that map 397 * @param defaultValue what to return if the value is null or if the 398 * conversion fails 399 * @return the value in the map as a boolean, or defaultValue if the 400 * original value is null, the map is null or the boolean conversion 401 * fails 402 */ getBoolean( Map map, Object key, Boolean defaultValue )403 public static Boolean getBoolean( Map map, Object key, Boolean defaultValue ) { 404 Boolean answer = getBoolean( map, key ); 405 if ( answer == null ) { 406 answer = defaultValue; 407 } 408 return answer; 409 } 410 411 /** 412 * Looks up the given key in the given map, converting the result into 413 * a number, using the default value if the the conversion fails. 414 * 415 * @param map the map whose value to look up 416 * @param key the key of the value to look up in that map 417 * @param defaultValue what to return if the value is null or if the 418 * conversion fails 419 * @return the value in the map as a number, or defaultValue if the 420 * original value is null, the map is null or the number conversion 421 * fails 422 */ getNumber( Map map, Object key, Number defaultValue )423 public static Number getNumber( Map map, Object key, Number defaultValue ) { 424 Number answer = getNumber( map, key ); 425 if ( answer == null ) { 426 answer = defaultValue; 427 } 428 return answer; 429 } 430 431 /** 432 * Looks up the given key in the given map, converting the result into 433 * a byte, using the default value if the the conversion fails. 434 * 435 * @param map the map whose value to look up 436 * @param key the key of the value to look up in that map 437 * @param defaultValue what to return if the value is null or if the 438 * conversion fails 439 * @return the value in the map as a number, or defaultValue if the 440 * original value is null, the map is null or the number conversion 441 * fails 442 */ getByte( Map map, Object key, Byte defaultValue )443 public static Byte getByte( Map map, Object key, Byte defaultValue ) { 444 Byte answer = getByte( map, key ); 445 if ( answer == null ) { 446 answer = defaultValue; 447 } 448 return answer; 449 } 450 451 /** 452 * Looks up the given key in the given map, converting the result into 453 * a short, using the default value if the the conversion fails. 454 * 455 * @param map the map whose value to look up 456 * @param key the key of the value to look up in that map 457 * @param defaultValue what to return if the value is null or if the 458 * conversion fails 459 * @return the value in the map as a number, or defaultValue if the 460 * original value is null, the map is null or the number conversion 461 * fails 462 */ getShort( Map map, Object key, Short defaultValue )463 public static Short getShort( Map map, Object key, Short defaultValue ) { 464 Short answer = getShort( map, key ); 465 if ( answer == null ) { 466 answer = defaultValue; 467 } 468 return answer; 469 } 470 471 /** 472 * Looks up the given key in the given map, converting the result into 473 * an integer, using the default value if the the conversion fails. 474 * 475 * @param map the map whose value to look up 476 * @param key the key of the value to look up in that map 477 * @param defaultValue what to return if the value is null or if the 478 * conversion fails 479 * @return the value in the map as a number, or defaultValue if the 480 * original value is null, the map is null or the number conversion 481 * fails 482 */ getInteger( Map map, Object key, Integer defaultValue )483 public static Integer getInteger( Map map, Object key, Integer defaultValue ) { 484 Integer answer = getInteger( map, key ); 485 if ( answer == null ) { 486 answer = defaultValue; 487 } 488 return answer; 489 } 490 491 /** 492 * Looks up the given key in the given map, converting the result into 493 * a long, using the default value if the the conversion fails. 494 * 495 * @param map the map whose value to look up 496 * @param key the key of the value to look up in that map 497 * @param defaultValue what to return if the value is null or if the 498 * conversion fails 499 * @return the value in the map as a number, or defaultValue if the 500 * original value is null, the map is null or the number conversion 501 * fails 502 */ getLong( Map map, Object key, Long defaultValue )503 public static Long getLong( Map map, Object key, Long defaultValue ) { 504 Long answer = getLong( map, key ); 505 if ( answer == null ) { 506 answer = defaultValue; 507 } 508 return answer; 509 } 510 511 /** 512 * Looks up the given key in the given map, converting the result into 513 * a float, using the default value if the the conversion fails. 514 * 515 * @param map the map whose value to look up 516 * @param key the key of the value to look up in that map 517 * @param defaultValue what to return if the value is null or if the 518 * conversion fails 519 * @return the value in the map as a number, or defaultValue if the 520 * original value is null, the map is null or the number conversion 521 * fails 522 */ getFloat( Map map, Object key, Float defaultValue )523 public static Float getFloat( Map map, Object key, Float defaultValue ) { 524 Float answer = getFloat( map, key ); 525 if ( answer == null ) { 526 answer = defaultValue; 527 } 528 return answer; 529 } 530 531 /** 532 * Looks up the given key in the given map, converting the result into 533 * a double, using the default value if the the conversion fails. 534 * 535 * @param map the map whose value to look up 536 * @param key the key of the value to look up in that map 537 * @param defaultValue what to return if the value is null or if the 538 * conversion fails 539 * @return the value in the map as a number, or defaultValue if the 540 * original value is null, the map is null or the number conversion 541 * fails 542 */ getDouble( Map map, Object key, Double defaultValue )543 public static Double getDouble( Map map, Object key, Double defaultValue ) { 544 Double answer = getDouble( map, key ); 545 if ( answer == null ) { 546 answer = defaultValue; 547 } 548 return answer; 549 } 550 551 /** 552 * Looks up the given key in the given map, converting the result into 553 * a map, using the default value if the the conversion fails. 554 * 555 * @param map the map whose value to look up 556 * @param key the key of the value to look up in that map 557 * @param defaultValue what to return if the value is null or if the 558 * conversion fails 559 * @return the value in the map as a number, or defaultValue if the 560 * original value is null, the map is null or the map conversion 561 * fails 562 */ getMap( Map map, Object key, Map defaultValue )563 public static Map getMap( Map map, Object key, Map defaultValue ) { 564 Map answer = getMap( map, key ); 565 if ( answer == null ) { 566 answer = defaultValue; 567 } 568 return answer; 569 } 570 571 572 // Type safe primitive getters 573 //------------------------------------------------------------------------- 574 /** 575 * Gets a boolean from a Map in a null-safe manner. 576 * <p> 577 * If the value is a <code>Boolean</code> its value is returned. 578 * If the value is a <code>String</code> and it equals 'true' ignoring case 579 * then <code>true</code> is returned, otherwise <code>false</code>. 580 * If the value is a <code>Number</code> an integer zero value returns 581 * <code>false</code> and non-zero returns <code>true</code>. 582 * Otherwise, <code>false</code> is returned. 583 * 584 * @param map the map to use 585 * @param key the key to look up 586 * @return the value in the Map as a Boolean, <code>false</code> if null map input 587 */ getBooleanValue(final Map map, final Object key)588 public static boolean getBooleanValue(final Map map, final Object key) { 589 Boolean booleanObject = getBoolean(map, key); 590 if (booleanObject == null) { 591 return false; 592 } 593 return booleanObject.booleanValue(); 594 } 595 596 /** 597 * Gets a byte from a Map in a null-safe manner. 598 * <p> 599 * The byte is obtained from the results of {@link #getNumber(Map,Object)}. 600 * 601 * @param map the map to use 602 * @param key the key to look up 603 * @return the value in the Map as a byte, <code>0</code> if null map input 604 */ getByteValue(final Map map, final Object key)605 public static byte getByteValue(final Map map, final Object key) { 606 Byte byteObject = getByte(map, key); 607 if (byteObject == null) { 608 return 0; 609 } 610 return byteObject.byteValue(); 611 } 612 613 /** 614 * Gets a short from a Map in a null-safe manner. 615 * <p> 616 * The short is obtained from the results of {@link #getNumber(Map,Object)}. 617 * 618 * @param map the map to use 619 * @param key the key to look up 620 * @return the value in the Map as a short, <code>0</code> if null map input 621 */ getShortValue(final Map map, final Object key)622 public static short getShortValue(final Map map, final Object key) { 623 Short shortObject = getShort(map, key); 624 if (shortObject == null) { 625 return 0; 626 } 627 return shortObject.shortValue(); 628 } 629 630 /** 631 * Gets an int from a Map in a null-safe manner. 632 * <p> 633 * The int is obtained from the results of {@link #getNumber(Map,Object)}. 634 * 635 * @param map the map to use 636 * @param key the key to look up 637 * @return the value in the Map as an int, <code>0</code> if null map input 638 */ getIntValue(final Map map, final Object key)639 public static int getIntValue(final Map map, final Object key) { 640 Integer integerObject = getInteger(map, key); 641 if (integerObject == null) { 642 return 0; 643 } 644 return integerObject.intValue(); 645 } 646 647 /** 648 * Gets a long from a Map in a null-safe manner. 649 * <p> 650 * The long is obtained from the results of {@link #getNumber(Map,Object)}. 651 * 652 * @param map the map to use 653 * @param key the key to look up 654 * @return the value in the Map as a long, <code>0L</code> if null map input 655 */ getLongValue(final Map map, final Object key)656 public static long getLongValue(final Map map, final Object key) { 657 Long longObject = getLong(map, key); 658 if (longObject == null) { 659 return 0L; 660 } 661 return longObject.longValue(); 662 } 663 664 /** 665 * Gets a float from a Map in a null-safe manner. 666 * <p> 667 * The float is obtained from the results of {@link #getNumber(Map,Object)}. 668 * 669 * @param map the map to use 670 * @param key the key to look up 671 * @return the value in the Map as a float, <code>0.0F</code> if null map input 672 */ getFloatValue(final Map map, final Object key)673 public static float getFloatValue(final Map map, final Object key) { 674 Float floatObject = getFloat(map, key); 675 if (floatObject == null) { 676 return 0f; 677 } 678 return floatObject.floatValue(); 679 } 680 681 /** 682 * Gets a double from a Map in a null-safe manner. 683 * <p> 684 * The double is obtained from the results of {@link #getNumber(Map,Object)}. 685 * 686 * @param map the map to use 687 * @param key the key to look up 688 * @return the value in the Map as a double, <code>0.0</code> if null map input 689 */ getDoubleValue(final Map map, final Object key)690 public static double getDoubleValue(final Map map, final Object key) { 691 Double doubleObject = getDouble(map, key); 692 if (doubleObject == null) { 693 return 0d; 694 } 695 return doubleObject.doubleValue(); 696 } 697 698 // Type safe primitive getters with default values 699 //------------------------------------------------------------------------- 700 /** 701 * Gets a boolean from a Map in a null-safe manner, 702 * using the default value if the the conversion fails. 703 * <p> 704 * If the value is a <code>Boolean</code> its value is returned. 705 * If the value is a <code>String</code> and it equals 'true' ignoring case 706 * then <code>true</code> is returned, otherwise <code>false</code>. 707 * If the value is a <code>Number</code> an integer zero value returns 708 * <code>false</code> and non-zero returns <code>true</code>. 709 * Otherwise, <code>defaultValue</code> is returned. 710 * 711 * @param map the map to use 712 * @param key the key to look up 713 * @param defaultValue return if the value is null or if the 714 * conversion fails 715 * @return the value in the Map as a Boolean, <code>defaultValue</code> if null map input 716 */ getBooleanValue(final Map map, final Object key, boolean defaultValue)717 public static boolean getBooleanValue(final Map map, final Object key, boolean defaultValue) { 718 Boolean booleanObject = getBoolean(map, key); 719 if (booleanObject == null) { 720 return defaultValue; 721 } 722 return booleanObject.booleanValue(); 723 } 724 725 /** 726 * Gets a byte from a Map in a null-safe manner, 727 * using the default value if the the conversion fails. 728 * <p> 729 * The byte is obtained from the results of {@link #getNumber(Map,Object)}. 730 * 731 * @param map the map to use 732 * @param key the key to look up 733 * @param defaultValue return if the value is null or if the 734 * conversion fails 735 * @return the value in the Map as a byte, <code>defaultValue</code> if null map input 736 */ getByteValue(final Map map, final Object key, byte defaultValue)737 public static byte getByteValue(final Map map, final Object key, byte defaultValue) { 738 Byte byteObject = getByte(map, key); 739 if (byteObject == null) { 740 return defaultValue; 741 } 742 return byteObject.byteValue(); 743 } 744 745 /** 746 * Gets a short from a Map in a null-safe manner, 747 * using the default value if the the conversion fails. 748 * <p> 749 * The short is obtained from the results of {@link #getNumber(Map,Object)}. 750 * 751 * @param map the map to use 752 * @param key the key to look up 753 * @param defaultValue return if the value is null or if the 754 * conversion fails 755 * @return the value in the Map as a short, <code>defaultValue</code> if null map input 756 */ getShortValue(final Map map, final Object key, short defaultValue)757 public static short getShortValue(final Map map, final Object key, short defaultValue) { 758 Short shortObject = getShort(map, key); 759 if (shortObject == null) { 760 return defaultValue; 761 } 762 return shortObject.shortValue(); 763 } 764 765 /** 766 * Gets an int from a Map in a null-safe manner, 767 * using the default value if the the conversion fails. 768 * <p> 769 * The int is obtained from the results of {@link #getNumber(Map,Object)}. 770 * 771 * @param map the map to use 772 * @param key the key to look up 773 * @param defaultValue return if the value is null or if the 774 * conversion fails 775 * @return the value in the Map as an int, <code>defaultValue</code> if null map input 776 */ getIntValue(final Map map, final Object key, int defaultValue)777 public static int getIntValue(final Map map, final Object key, int defaultValue) { 778 Integer integerObject = getInteger(map, key); 779 if (integerObject == null) { 780 return defaultValue; 781 } 782 return integerObject.intValue(); 783 } 784 785 /** 786 * Gets a long from a Map in a null-safe manner, 787 * using the default value if the the conversion fails. 788 * <p> 789 * The long is obtained from the results of {@link #getNumber(Map,Object)}. 790 * 791 * @param map the map to use 792 * @param key the key to look up 793 * @param defaultValue return if the value is null or if the 794 * conversion fails 795 * @return the value in the Map as a long, <code>defaultValue</code> if null map input 796 */ getLongValue(final Map map, final Object key, long defaultValue)797 public static long getLongValue(final Map map, final Object key, long defaultValue) { 798 Long longObject = getLong(map, key); 799 if (longObject == null) { 800 return defaultValue; 801 } 802 return longObject.longValue(); 803 } 804 805 /** 806 * Gets a float from a Map in a null-safe manner, 807 * using the default value if the the conversion fails. 808 * <p> 809 * The float is obtained from the results of {@link #getNumber(Map,Object)}. 810 * 811 * @param map the map to use 812 * @param key the key to look up 813 * @param defaultValue return if the value is null or if the 814 * conversion fails 815 * @return the value in the Map as a float, <code>defaultValue</code> if null map input 816 */ getFloatValue(final Map map, final Object key, float defaultValue)817 public static float getFloatValue(final Map map, final Object key, float defaultValue) { 818 Float floatObject = getFloat(map, key); 819 if (floatObject == null) { 820 return defaultValue; 821 } 822 return floatObject.floatValue(); 823 } 824 825 /** 826 * Gets a double from a Map in a null-safe manner, 827 * using the default value if the the conversion fails. 828 * <p> 829 * The double is obtained from the results of {@link #getNumber(Map,Object)}. 830 * 831 * @param map the map to use 832 * @param key the key to look up 833 * @param defaultValue return if the value is null or if the 834 * conversion fails 835 * @return the value in the Map as a double, <code>defaultValue</code> if null map input 836 */ getDoubleValue(final Map map, final Object key, double defaultValue)837 public static double getDoubleValue(final Map map, final Object key, double defaultValue) { 838 Double doubleObject = getDouble(map, key); 839 if (doubleObject == null) { 840 return defaultValue; 841 } 842 return doubleObject.doubleValue(); 843 } 844 845 // Conversion methods 846 //------------------------------------------------------------------------- 847 /** 848 * Gets a new Properties object initialised with the values from a Map. 849 * A null input will return an empty properties object. 850 * 851 * @param map the map to convert to a Properties object, may not be null 852 * @return the properties object 853 */ toProperties(final Map map)854 public static Properties toProperties(final Map map) { 855 Properties answer = new Properties(); 856 if (map != null) { 857 for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) { 858 Map.Entry entry = (Map.Entry) iter.next(); 859 Object key = entry.getKey(); 860 Object value = entry.getValue(); 861 answer.put(key, value); 862 } 863 } 864 return answer; 865 } 866 867 /** 868 * Creates a new HashMap using data copied from a ResourceBundle. 869 * 870 * @param resourceBundle the resource bundle to convert, may not be null 871 * @return the hashmap containing the data 872 * @throws NullPointerException if the bundle is null 873 */ toMap(final ResourceBundle resourceBundle)874 public static Map toMap(final ResourceBundle resourceBundle) { 875 Enumeration enumeration = resourceBundle.getKeys(); 876 Map map = new HashMap(); 877 878 while (enumeration.hasMoreElements()) { 879 String key = (String) enumeration.nextElement(); 880 Object value = resourceBundle.getObject(key); 881 map.put(key, value); 882 } 883 884 return map; 885 } 886 887 // Printing methods 888 //------------------------------------------------------------------------- 889 /** 890 * Prints the given map with nice line breaks. 891 * <p> 892 * This method prints a nicely formatted String describing the Map. 893 * Each map entry will be printed with key and value. 894 * When the value is a Map, recursive behaviour occurs. 895 * <p> 896 * This method is NOT thread-safe in any special way. You must manually 897 * synchronize on either this class or the stream as required. 898 * 899 * @param out the stream to print to, must not be null 900 * @param label The label to be used, may be <code>null</code>. 901 * If <code>null</code>, the label is not output. 902 * It typically represents the name of the property in a bean or similar. 903 * @param map The map to print, may be <code>null</code>. 904 * If <code>null</code>, the text 'null' is output. 905 * @throws NullPointerException if the stream is <code>null</code> 906 */ verbosePrint( final PrintStream out, final Object label, final Map map)907 public static void verbosePrint( 908 final PrintStream out, 909 final Object label, 910 final Map map) { 911 912 verbosePrintInternal(out, label, map, new ArrayStack(), false); 913 } 914 915 /** 916 * Prints the given map with nice line breaks. 917 * <p> 918 * This method prints a nicely formatted String describing the Map. 919 * Each map entry will be printed with key, value and value classname. 920 * When the value is a Map, recursive behaviour occurs. 921 * <p> 922 * This method is NOT thread-safe in any special way. You must manually 923 * synchronize on either this class or the stream as required. 924 * 925 * @param out the stream to print to, must not be null 926 * @param label The label to be used, may be <code>null</code>. 927 * If <code>null</code>, the label is not output. 928 * It typically represents the name of the property in a bean or similar. 929 * @param map The map to print, may be <code>null</code>. 930 * If <code>null</code>, the text 'null' is output. 931 * @throws NullPointerException if the stream is <code>null</code> 932 */ debugPrint( final PrintStream out, final Object label, final Map map)933 public static void debugPrint( 934 final PrintStream out, 935 final Object label, 936 final Map map) { 937 938 verbosePrintInternal(out, label, map, new ArrayStack(), true); 939 } 940 941 // Implementation methods 942 //------------------------------------------------------------------------- 943 /** 944 * Logs the given exception to <code>System.out</code>. 945 * <p> 946 * This method exists as Jakarta Collections does not depend on logging. 947 * 948 * @param ex the exception to log 949 */ logInfo(final Exception ex)950 protected static void logInfo(final Exception ex) { 951 System.out.println("INFO: Exception: " + ex); 952 } 953 954 /** 955 * Implementation providing functionality for {@link #debugPrint} and for 956 * {@link #verbosePrint}. This prints the given map with nice line breaks. 957 * If the debug flag is true, it additionally prints the type of the object 958 * value. If the contents of a map include the map itself, then the text 959 * <em>(this Map)</em> is printed out. If the contents include a 960 * parent container of the map, the the text <em>(ancestor[i] Map)</em> is 961 * printed, where i actually indicates the number of levels which must be 962 * traversed in the sequential list of ancestors (e.g. father, grandfather, 963 * great-grandfather, etc). 964 * 965 * @param out the stream to print to 966 * @param label the label to be used, may be <code>null</code>. 967 * If <code>null</code>, the label is not output. 968 * It typically represents the name of the property in a bean or similar. 969 * @param map the map to print, may be <code>null</code>. 970 * If <code>null</code>, the text 'null' is output 971 * @param lineage a stack consisting of any maps in which the previous 972 * argument is contained. This is checked to avoid infinite recursion when 973 * printing the output 974 * @param debug flag indicating whether type names should be output. 975 * @throws NullPointerException if the stream is <code>null</code> 976 */ verbosePrintInternal( final PrintStream out, final Object label, final Map map, final ArrayStack lineage, final boolean debug)977 private static void verbosePrintInternal( 978 final PrintStream out, 979 final Object label, 980 final Map map, 981 final ArrayStack lineage, 982 final boolean debug) { 983 984 printIndent(out, lineage.size()); 985 986 if (map == null) { 987 if (label != null) { 988 out.print(label); 989 out.print(" = "); 990 } 991 out.println("null"); 992 return; 993 } 994 if (label != null) { 995 out.print(label); 996 out.println(" = "); 997 } 998 999 printIndent(out, lineage.size()); 1000 out.println("{"); 1001 1002 lineage.push(map); 1003 1004 for (Iterator it = map.entrySet().iterator(); it.hasNext();) { 1005 Map.Entry entry = (Map.Entry) it.next(); 1006 Object childKey = entry.getKey(); 1007 Object childValue = entry.getValue(); 1008 if (childValue instanceof Map && !lineage.contains(childValue)) { 1009 verbosePrintInternal( 1010 out, 1011 (childKey == null ? "null" : childKey), 1012 (Map) childValue, 1013 lineage, 1014 debug); 1015 } else { 1016 printIndent(out, lineage.size()); 1017 out.print(childKey); 1018 out.print(" = "); 1019 1020 final int lineageIndex = lineage.indexOf(childValue); 1021 if (lineageIndex == -1) { 1022 out.print(childValue); 1023 } else if (lineage.size() - 1 == lineageIndex) { 1024 out.print("(this Map)"); 1025 } else { 1026 out.print( 1027 "(ancestor[" 1028 + (lineage.size() - 1 - lineageIndex - 1) 1029 + "] Map)"); 1030 } 1031 1032 if (debug && childValue != null) { 1033 out.print(' '); 1034 out.println(childValue.getClass().getName()); 1035 } else { 1036 out.println(); 1037 } 1038 } 1039 } 1040 1041 lineage.pop(); 1042 1043 printIndent(out, lineage.size()); 1044 out.println(debug ? "} " + map.getClass().getName() : "}"); 1045 } 1046 1047 /** 1048 * Writes indentation to the given stream. 1049 * 1050 * @param out the stream to indent 1051 */ printIndent(final PrintStream out, final int indent)1052 private static void printIndent(final PrintStream out, final int indent) { 1053 for (int i = 0; i < indent; i++) { 1054 out.print(INDENT_STRING); 1055 } 1056 } 1057 1058 // Misc 1059 //----------------------------------------------------------------------- 1060 /** 1061 * Inverts the supplied map returning a new HashMap such that the keys of 1062 * the input are swapped with the values. 1063 * <p> 1064 * This operation assumes that the inverse mapping is well defined. 1065 * If the input map had multiple entries with the same value mapped to 1066 * different keys, the returned map will map one of those keys to the 1067 * value, but the exact key which will be mapped is undefined. 1068 * 1069 * @param map the map to invert, may not be null 1070 * @return a new HashMap containing the inverted data 1071 * @throws NullPointerException if the map is null 1072 */ invertMap(Map map)1073 public static Map invertMap(Map map) { 1074 Map out = new HashMap(map.size()); 1075 for (Iterator it = map.entrySet().iterator(); it.hasNext();) { 1076 Map.Entry entry = (Map.Entry) it.next(); 1077 out.put(entry.getValue(), entry.getKey()); 1078 } 1079 return out; 1080 } 1081 1082 //----------------------------------------------------------------------- 1083 /** 1084 * Protects against adding null values to a map. 1085 * <p> 1086 * This method checks the value being added to the map, and if it is null 1087 * it is replaced by an empty string. 1088 * <p> 1089 * This could be useful if the map does not accept null values, or for 1090 * receiving data from a source that may provide null or empty string 1091 * which should be held in the same way in the map. 1092 * <p> 1093 * Keys are not validated. 1094 * 1095 * @param map the map to add to, may not be null 1096 * @param key the key 1097 * @param value the value, null converted to "" 1098 * @throws NullPointerException if the map is null 1099 */ safeAddToMap(Map map, Object key, Object value)1100 public static void safeAddToMap(Map map, Object key, Object value) throws NullPointerException { 1101 if (value == null) { 1102 map.put(key, ""); 1103 } else { 1104 map.put(key, value); 1105 } 1106 } 1107 1108 //----------------------------------------------------------------------- 1109 /** 1110 * Puts all the keys and values from the specified array into the map. 1111 * <p> 1112 * This method is an alternative to the {@link java.util.Map#putAll(java.util.Map)} 1113 * method and constructors. It allows you to build a map from an object array 1114 * of various possible styles. 1115 * <p> 1116 * If the first entry in the object array implements {@link java.util.Map.Entry} 1117 * or {@link KeyValue} then the key and value are added from that object. 1118 * If the first entry in the object array is an object array itself, then 1119 * it is assumed that index 0 in the sub-array is the key and index 1 is the value. 1120 * Otherwise, the array is treated as keys and values in alternate indices. 1121 * <p> 1122 * For example, to create a color map: 1123 * <pre> 1124 * Map colorMap = MapUtils.putAll(new HashMap(), new String[][] { 1125 * {"RED", "#FF0000"}, 1126 * {"GREEN", "#00FF00"}, 1127 * {"BLUE", "#0000FF"} 1128 * }); 1129 * </pre> 1130 * or: 1131 * <pre> 1132 * Map colorMap = MapUtils.putAll(new HashMap(), new String[] { 1133 * "RED", "#FF0000", 1134 * "GREEN", "#00FF00", 1135 * "BLUE", "#0000FF" 1136 * }); 1137 * </pre> 1138 * or: 1139 * <pre> 1140 * Map colorMap = MapUtils.putAll(new HashMap(), new Map.Entry[] { 1141 * new DefaultMapEntry("RED", "#FF0000"), 1142 * new DefaultMapEntry("GREEN", "#00FF00"), 1143 * new DefaultMapEntry("BLUE", "#0000FF") 1144 * }); 1145 * </pre> 1146 * 1147 * @param map the map to populate, must not be null 1148 * @param array an array to populate from, null ignored 1149 * @return the input map 1150 * @throws NullPointerException if map is null 1151 * @throws IllegalArgumentException if sub-array or entry matching used and an 1152 * entry is invalid 1153 * @throws ClassCastException if the array contents is mixed 1154 * @since Commons Collections 3.2 1155 */ putAll(Map map, Object[] array)1156 public static Map putAll(Map map, Object[] array) { 1157 map.size(); // force NPE 1158 if (array == null || array.length == 0) { 1159 return map; 1160 } 1161 Object obj = array[0]; 1162 if (obj instanceof Map.Entry) { 1163 for (int i = 0; i < array.length; i++) { 1164 Map.Entry entry = (Map.Entry) array[i]; 1165 map.put(entry.getKey(), entry.getValue()); 1166 } 1167 } else if (obj instanceof KeyValue) { 1168 for (int i = 0; i < array.length; i++) { 1169 KeyValue keyval = (KeyValue) array[i]; 1170 map.put(keyval.getKey(), keyval.getValue()); 1171 } 1172 } else if (obj instanceof Object[]) { 1173 for (int i = 0; i < array.length; i++) { 1174 Object[] sub = (Object[]) array[i]; 1175 if (sub == null || sub.length < 2) { 1176 throw new IllegalArgumentException("Invalid array element: " + i); 1177 } 1178 map.put(sub[0], sub[1]); 1179 } 1180 } else { 1181 for (int i = 0; i < array.length - 1;) { 1182 map.put(array[i++], array[i++]); 1183 } 1184 } 1185 return map; 1186 } 1187 1188 //----------------------------------------------------------------------- 1189 /** 1190 * Null-safe check if the specified map is empty. 1191 * <p> 1192 * Null returns true. 1193 * 1194 * @param map the map to check, may be null 1195 * @return true if empty or null 1196 * @since Commons Collections 3.2 1197 */ isEmpty(Map map)1198 public static boolean isEmpty(Map map) { 1199 return (map == null || map.isEmpty()); 1200 } 1201 1202 /** 1203 * Null-safe check if the specified map is not empty. 1204 * <p> 1205 * Null returns false. 1206 * 1207 * @param map the map to check, may be null 1208 * @return true if non-null and non-empty 1209 * @since Commons Collections 3.2 1210 */ isNotEmpty(Map map)1211 public static boolean isNotEmpty(Map map) { 1212 return !MapUtils.isEmpty(map); 1213 } 1214 1215 // Map decorators 1216 //----------------------------------------------------------------------- 1217 /** 1218 * Returns a synchronized map backed by the given map. 1219 * <p> 1220 * You must manually synchronize on the returned buffer's iterator to 1221 * avoid non-deterministic behavior: 1222 * 1223 * <pre> 1224 * Map m = MapUtils.synchronizedMap(myMap); 1225 * Set s = m.keySet(); // outside synchronized block 1226 * synchronized (m) { // synchronized on MAP! 1227 * Iterator i = s.iterator(); 1228 * while (i.hasNext()) { 1229 * process (i.next()); 1230 * } 1231 * } 1232 * </pre> 1233 * 1234 * This method uses the implementation in {@link java.util.Collections Collections}. 1235 * 1236 * @param map the map to synchronize, must not be null 1237 * @return a synchronized map backed by the given map 1238 * @throws IllegalArgumentException if the map is null 1239 */ synchronizedMap(Map map)1240 public static Map synchronizedMap(Map map) { 1241 return Collections.synchronizedMap(map); 1242 } 1243 1244 /** 1245 * Returns an unmodifiable map backed by the given map. 1246 * <p> 1247 * This method uses the implementation in the decorators subpackage. 1248 * 1249 * @param map the map to make unmodifiable, must not be null 1250 * @return an unmodifiable map backed by the given map 1251 * @throws IllegalArgumentException if the map is null 1252 */ unmodifiableMap(Map map)1253 public static Map unmodifiableMap(Map map) { 1254 return UnmodifiableMap.decorate(map); 1255 } 1256 1257 /** 1258 * Returns a predicated (validating) map backed by the given map. 1259 * <p> 1260 * Only objects that pass the tests in the given predicates can be added to the map. 1261 * Trying to add an invalid object results in an IllegalArgumentException. 1262 * Keys must pass the key predicate, values must pass the value predicate. 1263 * It is important not to use the original map after invoking this method, 1264 * as it is a backdoor for adding invalid objects. 1265 * 1266 * @param map the map to predicate, must not be null 1267 * @param keyPred the predicate for keys, null means no check 1268 * @param valuePred the predicate for values, null means no check 1269 * @return a predicated map backed by the given map 1270 * @throws IllegalArgumentException if the Map is null 1271 */ predicatedMap(Map map, Predicate keyPred, Predicate valuePred)1272 public static Map predicatedMap(Map map, Predicate keyPred, Predicate valuePred) { 1273 return PredicatedMap.decorate(map, keyPred, valuePred); 1274 } 1275 1276 /** 1277 * Returns a typed map backed by the given map. 1278 * <p> 1279 * Only keys and values of the specified types can be added to the map. 1280 * 1281 * @param map the map to limit to a specific type, must not be null 1282 * @param keyType the type of keys which may be added to the map, must not be null 1283 * @param valueType the type of values which may be added to the map, must not be null 1284 * @return a typed map backed by the specified map 1285 * @throws IllegalArgumentException if the Map or Class is null 1286 */ typedMap(Map map, Class keyType, Class valueType)1287 public static Map typedMap(Map map, Class keyType, Class valueType) { 1288 return TypedMap.decorate(map, keyType, valueType); 1289 } 1290 1291 /** 1292 * Returns a transformed map backed by the given map. 1293 * <p> 1294 * This method returns a new map (decorating the specified map) that 1295 * will transform any new entries added to it. 1296 * Existing entries in the specified map will not be transformed. 1297 * If you want that behaviour, see {@link TransformedMap#decorateTransform}. 1298 * <p> 1299 * Each object is passed through the transformers as it is added to the 1300 * Map. It is important not to use the original map after invoking this 1301 * method, as it is a backdoor for adding untransformed objects. 1302 * <p> 1303 * If there are any elements already in the map being decorated, they 1304 * are NOT transformed. 1305 * 1306 * @param map the map to transform, must not be null, typically empty 1307 * @param keyTransformer the transformer for the map keys, null means no transformation 1308 * @param valueTransformer the transformer for the map values, null means no transformation 1309 * @return a transformed map backed by the given map 1310 * @throws IllegalArgumentException if the Map is null 1311 */ transformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer)1312 public static Map transformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) { 1313 return TransformedMap.decorate(map, keyTransformer, valueTransformer); 1314 } 1315 1316 /** 1317 * Returns a fixed-sized map backed by the given map. 1318 * Elements may not be added or removed from the returned map, but 1319 * existing elements can be changed (for instance, via the 1320 * {@link Map#put(Object,Object)} method). 1321 * 1322 * @param map the map whose size to fix, must not be null 1323 * @return a fixed-size map backed by that map 1324 * @throws IllegalArgumentException if the Map is null 1325 */ fixedSizeMap(Map map)1326 public static Map fixedSizeMap(Map map) { 1327 return FixedSizeMap.decorate(map); 1328 } 1329 1330 /** 1331 * Returns a "lazy" map whose values will be created on demand. 1332 * <p> 1333 * When the key passed to the returned map's {@link Map#get(Object)} 1334 * method is not present in the map, then the factory will be used 1335 * to create a new object and that object will become the value 1336 * associated with that key. 1337 * <p> 1338 * For instance: 1339 * <pre> 1340 * Factory factory = new Factory() { 1341 * public Object create() { 1342 * return new Date(); 1343 * } 1344 * } 1345 * Map lazyMap = MapUtils.lazyMap(new HashMap(), factory); 1346 * Object obj = lazyMap.get("test"); 1347 * </pre> 1348 * 1349 * After the above code is executed, <code>obj</code> will contain 1350 * a new <code>Date</code> instance. Furthermore, that <code>Date</code> 1351 * instance is the value for the <code>"test"</code> key in the map. 1352 * 1353 * @param map the map to make lazy, must not be null 1354 * @param factory the factory for creating new objects, must not be null 1355 * @return a lazy map backed by the given map 1356 * @throws IllegalArgumentException if the Map or Factory is null 1357 */ lazyMap(Map map, Factory factory)1358 public static Map lazyMap(Map map, Factory factory) { 1359 return LazyMap.decorate(map, factory); 1360 } 1361 1362 /** 1363 * Returns a "lazy" map whose values will be created on demand. 1364 * <p> 1365 * When the key passed to the returned map's {@link Map#get(Object)} 1366 * method is not present in the map, then the factory will be used 1367 * to create a new object and that object will become the value 1368 * associated with that key. The factory is a {@link Transformer} 1369 * that will be passed the key which it must transform into the value. 1370 * <p> 1371 * For instance: 1372 * <pre> 1373 * Transformer factory = new Transformer() { 1374 * public Object transform(Object mapKey) { 1375 * return new File(mapKey); 1376 * } 1377 * } 1378 * Map lazyMap = MapUtils.lazyMap(new HashMap(), factory); 1379 * Object obj = lazyMap.get("C:/dev"); 1380 * </pre> 1381 * 1382 * After the above code is executed, <code>obj</code> will contain 1383 * a new <code>File</code> instance for the C drive dev directory. 1384 * Furthermore, that <code>File</code> instance is the value for the 1385 * <code>"C:/dev"</code> key in the map. 1386 * <p> 1387 * If a lazy map is wrapped by a synchronized map, the result is a simple 1388 * synchronized cache. When an object is not is the cache, the cache itself 1389 * calls back to the factory Transformer to populate itself, all within the 1390 * same synchronized block. 1391 * 1392 * @param map the map to make lazy, must not be null 1393 * @param transformerFactory the factory for creating new objects, must not be null 1394 * @return a lazy map backed by the given map 1395 * @throws IllegalArgumentException if the Map or Transformer is null 1396 */ lazyMap(Map map, Transformer transformerFactory)1397 public static Map lazyMap(Map map, Transformer transformerFactory) { 1398 return LazyMap.decorate(map, transformerFactory); 1399 } 1400 1401 /** 1402 * Returns a map that maintains the order of keys that are added 1403 * backed by the given map. 1404 * <p> 1405 * If a key is added twice, the order is determined by the first add. 1406 * The order is observed through the keySet, values and entrySet. 1407 * 1408 * @param map the map to order, must not be null 1409 * @return an ordered map backed by the given map 1410 * @throws IllegalArgumentException if the Map is null 1411 */ orderedMap(Map map)1412 public static Map orderedMap(Map map) { 1413 return ListOrderedMap.decorate(map); 1414 } 1415 1416 /** 1417 * Creates a mult-value map backed by the given map which returns 1418 * collections of type ArrayList. 1419 * 1420 * @param map the map to decorate 1421 * @return a multi-value map backed by the given map which returns ArrayLists of values. 1422 * @see MultiValueMap 1423 * @since Commons Collections 3.2 1424 */ multiValueMap(Map map)1425 public static Map multiValueMap(Map map) { 1426 return MultiValueMap.decorate(map); 1427 } 1428 1429 /** 1430 * Creates a multi-value map backed by the given map which returns 1431 * collections of the specified type. 1432 * 1433 * @param map the map to decorate 1434 * @param collectionClass the type of collections to return from the map (must contain public no-arg constructor 1435 * and extend Collection). 1436 * @return a multi-value map backed by the given map which returns collections of the specified type 1437 * @see MultiValueMap 1438 * @since Commons Collections 3.2 1439 */ multiValueMap(Map map, Class collectionClass)1440 public static Map multiValueMap(Map map, Class collectionClass) { 1441 return MultiValueMap.decorate(map, collectionClass); 1442 } 1443 1444 /** 1445 * Creates a multi-value map backed by the given map which returns 1446 * collections created by the specified collection factory. 1447 * 1448 * @param map the map to decorate 1449 * @param collectionFactory a factor which creates collection objects 1450 * @return a multi-value map backed by the given map which returns collections 1451 * created by the specified collection factory 1452 * @see MultiValueMap 1453 * @since Commons Collections 3.2 1454 */ multiValueMap(Map map, Factory collectionFactory)1455 public static Map multiValueMap(Map map, Factory collectionFactory) { 1456 return MultiValueMap.decorate(map, collectionFactory); 1457 } 1458 1459 // SortedMap decorators 1460 //----------------------------------------------------------------------- 1461 /** 1462 * Returns a synchronized sorted map backed by the given sorted map. 1463 * <p> 1464 * You must manually synchronize on the returned buffer's iterator to 1465 * avoid non-deterministic behavior: 1466 * 1467 * <pre> 1468 * Map m = MapUtils.synchronizedSortedMap(myMap); 1469 * Set s = m.keySet(); // outside synchronized block 1470 * synchronized (m) { // synchronized on MAP! 1471 * Iterator i = s.iterator(); 1472 * while (i.hasNext()) { 1473 * process (i.next()); 1474 * } 1475 * } 1476 * </pre> 1477 * 1478 * This method uses the implementation in {@link java.util.Collections Collections}. 1479 * 1480 * @param map the map to synchronize, must not be null 1481 * @return a synchronized map backed by the given map 1482 * @throws IllegalArgumentException if the map is null 1483 */ synchronizedSortedMap(SortedMap map)1484 public static Map synchronizedSortedMap(SortedMap map) { 1485 return Collections.synchronizedSortedMap(map); 1486 } 1487 1488 /** 1489 * Returns an unmodifiable sorted map backed by the given sorted map. 1490 * <p> 1491 * This method uses the implementation in the decorators subpackage. 1492 * 1493 * @param map the sorted map to make unmodifiable, must not be null 1494 * @return an unmodifiable map backed by the given map 1495 * @throws IllegalArgumentException if the map is null 1496 */ unmodifiableSortedMap(SortedMap map)1497 public static Map unmodifiableSortedMap(SortedMap map) { 1498 return UnmodifiableSortedMap.decorate(map); 1499 } 1500 1501 /** 1502 * Returns a predicated (validating) sorted map backed by the given map. 1503 * <p> 1504 * Only objects that pass the tests in the given predicates can be added to the map. 1505 * Trying to add an invalid object results in an IllegalArgumentException. 1506 * Keys must pass the key predicate, values must pass the value predicate. 1507 * It is important not to use the original map after invoking this method, 1508 * as it is a backdoor for adding invalid objects. 1509 * 1510 * @param map the map to predicate, must not be null 1511 * @param keyPred the predicate for keys, null means no check 1512 * @param valuePred the predicate for values, null means no check 1513 * @return a predicated map backed by the given map 1514 * @throws IllegalArgumentException if the SortedMap is null 1515 */ predicatedSortedMap(SortedMap map, Predicate keyPred, Predicate valuePred)1516 public static SortedMap predicatedSortedMap(SortedMap map, Predicate keyPred, Predicate valuePred) { 1517 return PredicatedSortedMap.decorate(map, keyPred, valuePred); 1518 } 1519 1520 /** 1521 * Returns a typed sorted map backed by the given map. 1522 * <p> 1523 * Only keys and values of the specified types can be added to the map. 1524 * 1525 * @param map the map to limit to a specific type, must not be null 1526 * @param keyType the type of keys which may be added to the map, must not be null 1527 * @param valueType the type of values which may be added to the map, must not be null 1528 * @return a typed map backed by the specified map 1529 */ typedSortedMap(SortedMap map, Class keyType, Class valueType)1530 public static SortedMap typedSortedMap(SortedMap map, Class keyType, Class valueType) { 1531 return TypedSortedMap.decorate(map, keyType, valueType); 1532 } 1533 1534 /** 1535 * Returns a transformed sorted map backed by the given map. 1536 * <p> 1537 * This method returns a new sorted map (decorating the specified map) that 1538 * will transform any new entries added to it. 1539 * Existing entries in the specified map will not be transformed. 1540 * If you want that behaviour, see {@link TransformedSortedMap#decorateTransform}. 1541 * <p> 1542 * Each object is passed through the transformers as it is added to the 1543 * Map. It is important not to use the original map after invoking this 1544 * method, as it is a backdoor for adding untransformed objects. 1545 * <p> 1546 * If there are any elements already in the map being decorated, they 1547 * are NOT transformed. 1548 * 1549 * @param map the map to transform, must not be null, typically empty 1550 * @param keyTransformer the transformer for the map keys, null means no transformation 1551 * @param valueTransformer the transformer for the map values, null means no transformation 1552 * @return a transformed map backed by the given map 1553 * @throws IllegalArgumentException if the SortedMap is null 1554 */ transformedSortedMap(SortedMap map, Transformer keyTransformer, Transformer valueTransformer)1555 public static SortedMap transformedSortedMap(SortedMap map, Transformer keyTransformer, Transformer valueTransformer) { 1556 return TransformedSortedMap.decorate(map, keyTransformer, valueTransformer); 1557 } 1558 1559 /** 1560 * Returns a fixed-sized sorted map backed by the given sorted map. 1561 * Elements may not be added or removed from the returned map, but 1562 * existing elements can be changed (for instance, via the 1563 * {@link Map#put(Object,Object)} method). 1564 * 1565 * @param map the map whose size to fix, must not be null 1566 * @return a fixed-size map backed by that map 1567 * @throws IllegalArgumentException if the SortedMap is null 1568 */ fixedSizeSortedMap(SortedMap map)1569 public static SortedMap fixedSizeSortedMap(SortedMap map) { 1570 return FixedSizeSortedMap.decorate(map); 1571 } 1572 1573 /** 1574 * Returns a "lazy" sorted map whose values will be created on demand. 1575 * <p> 1576 * When the key passed to the returned map's {@link Map#get(Object)} 1577 * method is not present in the map, then the factory will be used 1578 * to create a new object and that object will become the value 1579 * associated with that key. 1580 * <p> 1581 * For instance: 1582 * 1583 * <pre> 1584 * Factory factory = new Factory() { 1585 * public Object create() { 1586 * return new Date(); 1587 * } 1588 * } 1589 * SortedMap lazy = MapUtils.lazySortedMap(new TreeMap(), factory); 1590 * Object obj = lazy.get("test"); 1591 * </pre> 1592 * 1593 * After the above code is executed, <code>obj</code> will contain 1594 * a new <code>Date</code> instance. Furthermore, that <code>Date</code> 1595 * instance is the value for the <code>"test"</code> key. 1596 * 1597 * @param map the map to make lazy, must not be null 1598 * @param factory the factory for creating new objects, must not be null 1599 * @return a lazy map backed by the given map 1600 * @throws IllegalArgumentException if the SortedMap or Factory is null 1601 */ lazySortedMap(SortedMap map, Factory factory)1602 public static SortedMap lazySortedMap(SortedMap map, Factory factory) { 1603 return LazySortedMap.decorate(map, factory); 1604 } 1605 1606 /** 1607 * Returns a "lazy" sorted map whose values will be created on demand. 1608 * <p> 1609 * When the key passed to the returned map's {@link Map#get(Object)} 1610 * method is not present in the map, then the factory will be used 1611 * to create a new object and that object will become the value 1612 * associated with that key. The factory is a {@link Transformer} 1613 * that will be passed the key which it must transform into the value. 1614 * <p> 1615 * For instance: 1616 * <pre> 1617 * Transformer factory = new Transformer() { 1618 * public Object transform(Object mapKey) { 1619 * return new File(mapKey); 1620 * } 1621 * } 1622 * SortedMap lazy = MapUtils.lazySortedMap(new TreeMap(), factory); 1623 * Object obj = lazy.get("C:/dev"); 1624 * </pre> 1625 * 1626 * After the above code is executed, <code>obj</code> will contain 1627 * a new <code>File</code> instance for the C drive dev directory. 1628 * Furthermore, that <code>File</code> instance is the value for the 1629 * <code>"C:/dev"</code> key in the map. 1630 * <p> 1631 * If a lazy map is wrapped by a synchronized map, the result is a simple 1632 * synchronized cache. When an object is not is the cache, the cache itself 1633 * calls back to the factory Transformer to populate itself, all within the 1634 * same synchronized block. 1635 * 1636 * @param map the map to make lazy, must not be null 1637 * @param transformerFactory the factory for creating new objects, must not be null 1638 * @return a lazy map backed by the given map 1639 * @throws IllegalArgumentException if the Map or Transformer is null 1640 */ lazySortedMap(SortedMap map, Transformer transformerFactory)1641 public static SortedMap lazySortedMap(SortedMap map, Transformer transformerFactory) { 1642 return LazySortedMap.decorate(map, transformerFactory); 1643 } 1644 1645 } 1646