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 }