001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.collections; 018 019 import java.io.PrintStream; 020 import java.text.NumberFormat; 021 import java.text.ParseException; 022 import java.util.Collections; 023 import java.util.Enumeration; 024 import java.util.HashMap; 025 import java.util.Iterator; 026 import java.util.Map; 027 import java.util.Properties; 028 import java.util.ResourceBundle; 029 import java.util.SortedMap; 030 import java.util.TreeMap; 031 032 import org.apache.commons.collections.map.FixedSizeMap; 033 import org.apache.commons.collections.map.FixedSizeSortedMap; 034 import org.apache.commons.collections.map.LazyMap; 035 import org.apache.commons.collections.map.LazySortedMap; 036 import org.apache.commons.collections.map.ListOrderedMap; 037 import org.apache.commons.collections.map.MultiValueMap; 038 import org.apache.commons.collections.map.PredicatedMap; 039 import org.apache.commons.collections.map.PredicatedSortedMap; 040 import org.apache.commons.collections.map.TransformedMap; 041 import org.apache.commons.collections.map.TransformedSortedMap; 042 import org.apache.commons.collections.map.TypedMap; 043 import org.apache.commons.collections.map.TypedSortedMap; 044 import org.apache.commons.collections.map.UnmodifiableMap; 045 import org.apache.commons.collections.map.UnmodifiableSortedMap; 046 047 /** 048 * Provides utility methods and decorators for 049 * {@link Map} and {@link SortedMap} instances. 050 * <p> 051 * It contains various type safe methods 052 * as well as other useful features like deep copying. 053 * <p> 054 * It also provides the following decorators: 055 * 056 * <ul> 057 * <li>{@link #fixedSizeMap(Map)} 058 * <li>{@link #fixedSizeSortedMap(SortedMap)} 059 * <li>{@link #lazyMap(Map,Factory)} 060 * <li>{@link #lazyMap(Map,Transformer)} 061 * <li>{@link #lazySortedMap(SortedMap,Factory)} 062 * <li>{@link #lazySortedMap(SortedMap,Transformer)} 063 * <li>{@link #predicatedMap(Map,Predicate,Predicate)} 064 * <li>{@link #predicatedSortedMap(SortedMap,Predicate,Predicate)} 065 * <li>{@link #transformedMap(Map, Transformer, Transformer)} 066 * <li>{@link #transformedSortedMap(SortedMap, Transformer, Transformer)} 067 * <li>{@link #typedMap(Map, Class, Class)} 068 * <li>{@link #typedSortedMap(SortedMap, Class, Class)} 069 * <li>{@link #multiValueMap( Map )} 070 * <li>{@link #multiValueMap( Map, Class )} 071 * <li>{@link #multiValueMap( Map, Factory )} 072 * </ul> 073 * 074 * @since Commons Collections 1.0 075 * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ 076 * 077 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> 078 * @author <a href="mailto:nissim@nksystems.com">Nissim Karpenstein</a> 079 * @author <a href="mailto:knielsen@apache.org">Kasper Nielsen</a> 080 * @author Paul Jack 081 * @author Stephen Colebourne 082 * @author Matthew Hawthorne 083 * @author Arun Mammen Thomas 084 * @author Janek Bogucki 085 * @author Max Rydahl Andersen 086 * @author <a href="mailto:equinus100@hotmail.com">Ashwin S</a> 087 * @author <a href="mailto:jcarman@apache.org">James Carman</a> 088 * @author Neil O'Toole 089 */ 090 public class MapUtils { 091 092 /** 093 * An empty unmodifiable map. 094 * This was not provided in JDK1.2. 095 */ 096 public static final Map EMPTY_MAP = UnmodifiableMap.decorate(new HashMap(1)); 097 /** 098 * An empty unmodifiable sorted map. 099 * 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 */ 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 */ 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 */ 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 */ 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 */ 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 logInfo(e); 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 1641 public static SortedMap lazySortedMap(SortedMap map, Transformer transformerFactory) { 1642 return LazySortedMap.decorate(map, transformerFactory); 1643 } 1644 1645 }