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