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    }