0001: /*
0002: * Copyright 2001-2005 The Apache Software Foundation
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: package org.apache.commons.collections;
0017:
0018: import java.io.PrintStream;
0019: import java.text.NumberFormat;
0020: import java.text.ParseException;
0021: import java.util.Collections;
0022: import java.util.Enumeration;
0023: import java.util.HashMap;
0024: import java.util.Iterator;
0025: import java.util.Map;
0026: import java.util.Properties;
0027: import java.util.ResourceBundle;
0028: import java.util.SortedMap;
0029: import java.util.TreeMap;
0030:
0031: import org.apache.commons.collections.map.FixedSizeMap;
0032: import org.apache.commons.collections.map.FixedSizeSortedMap;
0033: import org.apache.commons.collections.map.LazyMap;
0034: import org.apache.commons.collections.map.LazySortedMap;
0035: import org.apache.commons.collections.map.ListOrderedMap;
0036: import org.apache.commons.collections.map.MultiValueMap;
0037: import org.apache.commons.collections.map.PredicatedMap;
0038: import org.apache.commons.collections.map.PredicatedSortedMap;
0039: import org.apache.commons.collections.map.TransformedMap;
0040: import org.apache.commons.collections.map.TransformedSortedMap;
0041: import org.apache.commons.collections.map.TypedMap;
0042: import org.apache.commons.collections.map.TypedSortedMap;
0043: import org.apache.commons.collections.map.UnmodifiableMap;
0044: import org.apache.commons.collections.map.UnmodifiableSortedMap;
0045:
0046: /**
0047: * Provides utility methods and decorators for
0048: * {@link Map} and {@link SortedMap} instances.
0049: * <p>
0050: * It contains various type safe methods
0051: * as well as other useful features like deep copying.
0052: * <p>
0053: * It also provides the following decorators:
0054: *
0055: * <ul>
0056: * <li>{@link #fixedSizeMap(Map)}
0057: * <li>{@link #fixedSizeSortedMap(SortedMap)}
0058: * <li>{@link #lazyMap(Map,Factory)}
0059: * <li>{@link #lazyMap(Map,Transformer)}
0060: * <li>{@link #lazySortedMap(SortedMap,Factory)}
0061: * <li>{@link #lazySortedMap(SortedMap,Transformer)}
0062: * <li>{@link #predicatedMap(Map,Predicate,Predicate)}
0063: * <li>{@link #predicatedSortedMap(SortedMap,Predicate,Predicate)}
0064: * <li>{@link #transformedMap(Map, Transformer, Transformer)}
0065: * <li>{@link #transformedSortedMap(SortedMap, Transformer, Transformer)}
0066: * <li>{@link #typedMap(Map, Class, Class)}
0067: * <li>{@link #typedSortedMap(SortedMap, Class, Class)}
0068: * <li>{@link #multiValueMap( Map )}
0069: * <li>{@link #multiValueMap( Map, Class )}
0070: * <li>{@link #multiValueMap( Map, Factory )}
0071: * </ul>
0072: *
0073: * @since Commons Collections 1.0
0074: * @version $Revision: 357494 $ $Date: 2005-12-18 19:05:31 +0000 (Sun, 18 Dec 2005) $
0075: *
0076: * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
0077: * @author <a href="mailto:nissim@nksystems.com">Nissim Karpenstein</a>
0078: * @author <a href="mailto:knielsen@apache.org">Kasper Nielsen</a>
0079: * @author Paul Jack
0080: * @author Stephen Colebourne
0081: * @author Matthew Hawthorne
0082: * @author Arun Mammen Thomas
0083: * @author Janek Bogucki
0084: * @author Max Rydahl Andersen
0085: * @author <a href="mailto:equinus100@hotmail.com">Ashwin S</a>
0086: * @author <a href="mailto:jcarman@apache.org">James Carman</a>
0087: * @author Neil O'Toole
0088: */
0089: public class MapUtils {
0090:
0091: /**
0092: * An empty unmodifiable map.
0093: * This was not provided in JDK1.2.
0094: */
0095: public static final Map EMPTY_MAP = UnmodifiableMap
0096: .decorate(new HashMap(1));
0097: /**
0098: * An empty unmodifiable sorted map.
0099: * This is not provided in the JDK.
0100: */
0101: public static final SortedMap EMPTY_SORTED_MAP = UnmodifiableSortedMap
0102: .decorate(new TreeMap());
0103: /**
0104: * String used to indent the verbose and debug Map prints.
0105: */
0106: private static final String INDENT_STRING = " ";
0107:
0108: /**
0109: * <code>MapUtils</code> should not normally be instantiated.
0110: */
0111: public MapUtils() {
0112: }
0113:
0114: // Type safe getters
0115: //-------------------------------------------------------------------------
0116: /**
0117: * Gets from a Map in a null-safe manner.
0118: *
0119: * @param map the map to use
0120: * @param key the key to look up
0121: * @return the value in the Map, <code>null</code> if null map input
0122: */
0123: public static Object getObject(final Map map, final Object key) {
0124: if (map != null) {
0125: return map.get(key);
0126: }
0127: return null;
0128: }
0129:
0130: /**
0131: * Gets a String from a Map in a null-safe manner.
0132: * <p>
0133: * The String is obtained via <code>toString</code>.
0134: *
0135: * @param map the map to use
0136: * @param key the key to look up
0137: * @return the value in the Map as a String, <code>null</code> if null map input
0138: */
0139: public static String getString(final Map map, final Object key) {
0140: if (map != null) {
0141: Object answer = map.get(key);
0142: if (answer != null) {
0143: return answer.toString();
0144: }
0145: }
0146: return null;
0147: }
0148:
0149: /**
0150: * Gets a Boolean from a Map in a null-safe manner.
0151: * <p>
0152: * If the value is a <code>Boolean</code> it is returned directly.
0153: * If the value is a <code>String</code> and it equals 'true' ignoring case
0154: * then <code>true</code> is returned, otherwise <code>false</code>.
0155: * If the value is a <code>Number</code> an integer zero value returns
0156: * <code>false</code> and non-zero returns <code>true</code>.
0157: * Otherwise, <code>null</code> is returned.
0158: *
0159: * @param map the map to use
0160: * @param key the key to look up
0161: * @return the value in the Map as a Boolean, <code>null</code> if null map input
0162: */
0163: public static Boolean getBoolean(final Map map, final Object key) {
0164: if (map != null) {
0165: Object answer = map.get(key);
0166: if (answer != null) {
0167: if (answer instanceof Boolean) {
0168: return (Boolean) answer;
0169:
0170: } else if (answer instanceof String) {
0171: return new Boolean((String) answer);
0172:
0173: } else if (answer instanceof Number) {
0174: Number n = (Number) answer;
0175: return (n.intValue() != 0) ? Boolean.TRUE
0176: : Boolean.FALSE;
0177: }
0178: }
0179: }
0180: return null;
0181: }
0182:
0183: /**
0184: * Gets a Number from a Map in a null-safe manner.
0185: * <p>
0186: * If the value is a <code>Number</code> it is returned directly.
0187: * If the value is a <code>String</code> it is converted using
0188: * {@link NumberFormat#parse(String)} on the system default formatter
0189: * returning <code>null</code> if the conversion fails.
0190: * Otherwise, <code>null</code> is returned.
0191: *
0192: * @param map the map to use
0193: * @param key the key to look up
0194: * @return the value in the Map as a Number, <code>null</code> if null map input
0195: */
0196: public static Number getNumber(final Map map, final Object key) {
0197: if (map != null) {
0198: Object answer = map.get(key);
0199: if (answer != null) {
0200: if (answer instanceof Number) {
0201: return (Number) answer;
0202:
0203: } else if (answer instanceof String) {
0204: try {
0205: String text = (String) answer;
0206: return NumberFormat.getInstance().parse(text);
0207:
0208: } catch (ParseException e) {
0209: logInfo(e);
0210: }
0211: }
0212: }
0213: }
0214: return null;
0215: }
0216:
0217: /**
0218: * Gets a Byte from a Map in a null-safe manner.
0219: * <p>
0220: * The Byte is obtained from the results of {@link #getNumber(Map,Object)}.
0221: *
0222: * @param map the map to use
0223: * @param key the key to look up
0224: * @return the value in the Map as a Byte, <code>null</code> if null map input
0225: */
0226: public static Byte getByte(final Map map, final Object key) {
0227: Number answer = getNumber(map, key);
0228: if (answer == null) {
0229: return null;
0230: } else if (answer instanceof Byte) {
0231: return (Byte) answer;
0232: }
0233: return new Byte(answer.byteValue());
0234: }
0235:
0236: /**
0237: * Gets a Short from a Map in a null-safe manner.
0238: * <p>
0239: * The Short is obtained from the results of {@link #getNumber(Map,Object)}.
0240: *
0241: * @param map the map to use
0242: * @param key the key to look up
0243: * @return the value in the Map as a Short, <code>null</code> if null map input
0244: */
0245: public static Short getShort(final Map map, final Object key) {
0246: Number answer = getNumber(map, key);
0247: if (answer == null) {
0248: return null;
0249: } else if (answer instanceof Short) {
0250: return (Short) answer;
0251: }
0252: return new Short(answer.shortValue());
0253: }
0254:
0255: /**
0256: * Gets a Integer from a Map in a null-safe manner.
0257: * <p>
0258: * The Integer is obtained from the results of {@link #getNumber(Map,Object)}.
0259: *
0260: * @param map the map to use
0261: * @param key the key to look up
0262: * @return the value in the Map as a Integer, <code>null</code> if null map input
0263: */
0264: public static Integer getInteger(final Map map, final Object key) {
0265: Number answer = getNumber(map, key);
0266: if (answer == null) {
0267: return null;
0268: } else if (answer instanceof Integer) {
0269: return (Integer) answer;
0270: }
0271: return new Integer(answer.intValue());
0272: }
0273:
0274: /**
0275: * Gets a Long from a Map in a null-safe manner.
0276: * <p>
0277: * The Long is obtained from the results of {@link #getNumber(Map,Object)}.
0278: *
0279: * @param map the map to use
0280: * @param key the key to look up
0281: * @return the value in the Map as a Long, <code>null</code> if null map input
0282: */
0283: public static Long getLong(final Map map, final Object key) {
0284: Number answer = getNumber(map, key);
0285: if (answer == null) {
0286: return null;
0287: } else if (answer instanceof Long) {
0288: return (Long) answer;
0289: }
0290: return new Long(answer.longValue());
0291: }
0292:
0293: /**
0294: * Gets a Float from a Map in a null-safe manner.
0295: * <p>
0296: * The Float is obtained from the results of {@link #getNumber(Map,Object)}.
0297: *
0298: * @param map the map to use
0299: * @param key the key to look up
0300: * @return the value in the Map as a Float, <code>null</code> if null map input
0301: */
0302: public static Float getFloat(final Map map, final Object key) {
0303: Number answer = getNumber(map, key);
0304: if (answer == null) {
0305: return null;
0306: } else if (answer instanceof Float) {
0307: return (Float) answer;
0308: }
0309: return new Float(answer.floatValue());
0310: }
0311:
0312: /**
0313: * Gets a Double from a Map in a null-safe manner.
0314: * <p>
0315: * The Double is obtained from the results of {@link #getNumber(Map,Object)}.
0316: *
0317: * @param map the map to use
0318: * @param key the key to look up
0319: * @return the value in the Map as a Double, <code>null</code> if null map input
0320: */
0321: public static Double getDouble(final Map map, final Object key) {
0322: Number answer = getNumber(map, key);
0323: if (answer == null) {
0324: return null;
0325: } else if (answer instanceof Double) {
0326: return (Double) answer;
0327: }
0328: return new Double(answer.doubleValue());
0329: }
0330:
0331: /**
0332: * Gets a Map from a Map in a null-safe manner.
0333: * <p>
0334: * If the value returned from the specified map is not a Map then
0335: * <code>null</code> is returned.
0336: *
0337: * @param map the map to use
0338: * @param key the key to look up
0339: * @return the value in the Map as a Map, <code>null</code> if null map input
0340: */
0341: public static Map getMap(final Map map, final Object key) {
0342: if (map != null) {
0343: Object answer = map.get(key);
0344: if (answer != null && answer instanceof Map) {
0345: return (Map) answer;
0346: }
0347: }
0348: return null;
0349: }
0350:
0351: // Type safe getters with default values
0352: //-------------------------------------------------------------------------
0353: /**
0354: * Looks up the given key in the given map, converting null into the
0355: * given default value.
0356: *
0357: * @param map the map whose value to look up
0358: * @param key the key of the value to look up in that map
0359: * @param defaultValue what to return if the value is null
0360: * @return the value in the map, or defaultValue if the original value
0361: * is null or the map is null
0362: */
0363: public static Object getObject(Map map, Object key,
0364: Object defaultValue) {
0365: if (map != null) {
0366: Object answer = map.get(key);
0367: if (answer != null) {
0368: return answer;
0369: }
0370: }
0371: return defaultValue;
0372: }
0373:
0374: /**
0375: * Looks up the given key in the given map, converting the result into
0376: * a string, using the default value if the the conversion fails.
0377: *
0378: * @param map the map whose value to look up
0379: * @param key the key of the value to look up in that map
0380: * @param defaultValue what to return if the value is null or if the
0381: * conversion fails
0382: * @return the value in the map as a string, or defaultValue if the
0383: * original value is null, the map is null or the string conversion
0384: * fails
0385: */
0386: public static String getString(Map map, Object key,
0387: String defaultValue) {
0388: String answer = getString(map, key);
0389: if (answer == null) {
0390: answer = defaultValue;
0391: }
0392: return answer;
0393: }
0394:
0395: /**
0396: * Looks up the given key in the given map, converting the result into
0397: * a boolean, using the default value if the the conversion fails.
0398: *
0399: * @param map the map whose value to look up
0400: * @param key the key of the value to look up in that map
0401: * @param defaultValue what to return if the value is null or if the
0402: * conversion fails
0403: * @return the value in the map as a boolean, or defaultValue if the
0404: * original value is null, the map is null or the boolean conversion
0405: * fails
0406: */
0407: public static Boolean getBoolean(Map map, Object key,
0408: Boolean defaultValue) {
0409: Boolean answer = getBoolean(map, key);
0410: if (answer == null) {
0411: answer = defaultValue;
0412: }
0413: return answer;
0414: }
0415:
0416: /**
0417: * Looks up the given key in the given map, converting the result into
0418: * a number, using the default value if the the conversion fails.
0419: *
0420: * @param map the map whose value to look up
0421: * @param key the key of the value to look up in that map
0422: * @param defaultValue what to return if the value is null or if the
0423: * conversion fails
0424: * @return the value in the map as a number, or defaultValue if the
0425: * original value is null, the map is null or the number conversion
0426: * fails
0427: */
0428: public static Number getNumber(Map map, Object key,
0429: Number defaultValue) {
0430: Number answer = getNumber(map, key);
0431: if (answer == null) {
0432: answer = defaultValue;
0433: }
0434: return answer;
0435: }
0436:
0437: /**
0438: * Looks up the given key in the given map, converting the result into
0439: * a byte, using the default value if the the conversion fails.
0440: *
0441: * @param map the map whose value to look up
0442: * @param key the key of the value to look up in that map
0443: * @param defaultValue what to return if the value is null or if the
0444: * conversion fails
0445: * @return the value in the map as a number, or defaultValue if the
0446: * original value is null, the map is null or the number conversion
0447: * fails
0448: */
0449: public static Byte getByte(Map map, Object key, Byte defaultValue) {
0450: Byte answer = getByte(map, key);
0451: if (answer == null) {
0452: answer = defaultValue;
0453: }
0454: return answer;
0455: }
0456:
0457: /**
0458: * Looks up the given key in the given map, converting the result into
0459: * a short, using the default value if the the conversion fails.
0460: *
0461: * @param map the map whose value to look up
0462: * @param key the key of the value to look up in that map
0463: * @param defaultValue what to return if the value is null or if the
0464: * conversion fails
0465: * @return the value in the map as a number, or defaultValue if the
0466: * original value is null, the map is null or the number conversion
0467: * fails
0468: */
0469: public static Short getShort(Map map, Object key, Short defaultValue) {
0470: Short answer = getShort(map, key);
0471: if (answer == null) {
0472: answer = defaultValue;
0473: }
0474: return answer;
0475: }
0476:
0477: /**
0478: * Looks up the given key in the given map, converting the result into
0479: * an integer, using the default value if the the conversion fails.
0480: *
0481: * @param map the map whose value to look up
0482: * @param key the key of the value to look up in that map
0483: * @param defaultValue what to return if the value is null or if the
0484: * conversion fails
0485: * @return the value in the map as a number, or defaultValue if the
0486: * original value is null, the map is null or the number conversion
0487: * fails
0488: */
0489: public static Integer getInteger(Map map, Object key,
0490: Integer defaultValue) {
0491: Integer answer = getInteger(map, key);
0492: if (answer == null) {
0493: answer = defaultValue;
0494: }
0495: return answer;
0496: }
0497:
0498: /**
0499: * Looks up the given key in the given map, converting the result into
0500: * a long, using the default value if the the conversion fails.
0501: *
0502: * @param map the map whose value to look up
0503: * @param key the key of the value to look up in that map
0504: * @param defaultValue what to return if the value is null or if the
0505: * conversion fails
0506: * @return the value in the map as a number, or defaultValue if the
0507: * original value is null, the map is null or the number conversion
0508: * fails
0509: */
0510: public static Long getLong(Map map, Object key, Long defaultValue) {
0511: Long answer = getLong(map, key);
0512: if (answer == null) {
0513: answer = defaultValue;
0514: }
0515: return answer;
0516: }
0517:
0518: /**
0519: * Looks up the given key in the given map, converting the result into
0520: * a float, using the default value if the the conversion fails.
0521: *
0522: * @param map the map whose value to look up
0523: * @param key the key of the value to look up in that map
0524: * @param defaultValue what to return if the value is null or if the
0525: * conversion fails
0526: * @return the value in the map as a number, or defaultValue if the
0527: * original value is null, the map is null or the number conversion
0528: * fails
0529: */
0530: public static Float getFloat(Map map, Object key, Float defaultValue) {
0531: Float answer = getFloat(map, key);
0532: if (answer == null) {
0533: answer = defaultValue;
0534: }
0535: return answer;
0536: }
0537:
0538: /**
0539: * Looks up the given key in the given map, converting the result into
0540: * a double, using the default value if the the conversion fails.
0541: *
0542: * @param map the map whose value to look up
0543: * @param key the key of the value to look up in that map
0544: * @param defaultValue what to return if the value is null or if the
0545: * conversion fails
0546: * @return the value in the map as a number, or defaultValue if the
0547: * original value is null, the map is null or the number conversion
0548: * fails
0549: */
0550: public static Double getDouble(Map map, Object key,
0551: Double defaultValue) {
0552: Double answer = getDouble(map, key);
0553: if (answer == null) {
0554: answer = defaultValue;
0555: }
0556: return answer;
0557: }
0558:
0559: /**
0560: * Looks up the given key in the given map, converting the result into
0561: * a map, using the default value if the the conversion fails.
0562: *
0563: * @param map the map whose value to look up
0564: * @param key the key of the value to look up in that map
0565: * @param defaultValue what to return if the value is null or if the
0566: * conversion fails
0567: * @return the value in the map as a number, or defaultValue if the
0568: * original value is null, the map is null or the map conversion
0569: * fails
0570: */
0571: public static Map getMap(Map map, Object key, Map defaultValue) {
0572: Map answer = getMap(map, key);
0573: if (answer == null) {
0574: answer = defaultValue;
0575: }
0576: return answer;
0577: }
0578:
0579: // Type safe primitive getters
0580: //-------------------------------------------------------------------------
0581: /**
0582: * Gets a boolean from a Map in a null-safe manner.
0583: * <p>
0584: * If the value is a <code>Boolean</code> its value is returned.
0585: * If the value is a <code>String</code> and it equals 'true' ignoring case
0586: * then <code>true</code> is returned, otherwise <code>false</code>.
0587: * If the value is a <code>Number</code> an integer zero value returns
0588: * <code>false</code> and non-zero returns <code>true</code>.
0589: * Otherwise, <code>false</code> is returned.
0590: *
0591: * @param map the map to use
0592: * @param key the key to look up
0593: * @return the value in the Map as a Boolean, <code>false</code> if null map input
0594: */
0595: public static boolean getBooleanValue(final Map map,
0596: final Object key) {
0597: Boolean booleanObject = getBoolean(map, key);
0598: if (booleanObject == null) {
0599: return false;
0600: }
0601: return booleanObject.booleanValue();
0602: }
0603:
0604: /**
0605: * Gets a byte from a Map in a null-safe manner.
0606: * <p>
0607: * The byte is obtained from the results of {@link #getNumber(Map,Object)}.
0608: *
0609: * @param map the map to use
0610: * @param key the key to look up
0611: * @return the value in the Map as a byte, <code>0</code> if null map input
0612: */
0613: public static byte getByteValue(final Map map, final Object key) {
0614: Byte byteObject = getByte(map, key);
0615: if (byteObject == null) {
0616: return 0;
0617: }
0618: return byteObject.byteValue();
0619: }
0620:
0621: /**
0622: * Gets a short from a Map in a null-safe manner.
0623: * <p>
0624: * The short is obtained from the results of {@link #getNumber(Map,Object)}.
0625: *
0626: * @param map the map to use
0627: * @param key the key to look up
0628: * @return the value in the Map as a short, <code>0</code> if null map input
0629: */
0630: public static short getShortValue(final Map map, final Object key) {
0631: Short shortObject = getShort(map, key);
0632: if (shortObject == null) {
0633: return 0;
0634: }
0635: return shortObject.shortValue();
0636: }
0637:
0638: /**
0639: * Gets an int from a Map in a null-safe manner.
0640: * <p>
0641: * The int is obtained from the results of {@link #getNumber(Map,Object)}.
0642: *
0643: * @param map the map to use
0644: * @param key the key to look up
0645: * @return the value in the Map as an int, <code>0</code> if null map input
0646: */
0647: public static int getIntValue(final Map map, final Object key) {
0648: Integer integerObject = getInteger(map, key);
0649: if (integerObject == null) {
0650: return 0;
0651: }
0652: return integerObject.intValue();
0653: }
0654:
0655: /**
0656: * Gets a long from a Map in a null-safe manner.
0657: * <p>
0658: * The long is obtained from the results of {@link #getNumber(Map,Object)}.
0659: *
0660: * @param map the map to use
0661: * @param key the key to look up
0662: * @return the value in the Map as a long, <code>0L</code> if null map input
0663: */
0664: public static long getLongValue(final Map map, final Object key) {
0665: Long longObject = getLong(map, key);
0666: if (longObject == null) {
0667: return 0L;
0668: }
0669: return longObject.longValue();
0670: }
0671:
0672: /**
0673: * Gets a float from a Map in a null-safe manner.
0674: * <p>
0675: * The float is obtained from the results of {@link #getNumber(Map,Object)}.
0676: *
0677: * @param map the map to use
0678: * @param key the key to look up
0679: * @return the value in the Map as a float, <code>0.0F</code> if null map input
0680: */
0681: public static float getFloatValue(final Map map, final Object key) {
0682: Float floatObject = getFloat(map, key);
0683: if (floatObject == null) {
0684: return 0f;
0685: }
0686: return floatObject.floatValue();
0687: }
0688:
0689: /**
0690: * Gets a double from a Map in a null-safe manner.
0691: * <p>
0692: * The double is obtained from the results of {@link #getNumber(Map,Object)}.
0693: *
0694: * @param map the map to use
0695: * @param key the key to look up
0696: * @return the value in the Map as a double, <code>0.0</code> if null map input
0697: */
0698: public static double getDoubleValue(final Map map, final Object key) {
0699: Double doubleObject = getDouble(map, key);
0700: if (doubleObject == null) {
0701: return 0d;
0702: }
0703: return doubleObject.doubleValue();
0704: }
0705:
0706: // Type safe primitive getters with default values
0707: //-------------------------------------------------------------------------
0708: /**
0709: * Gets a boolean from a Map in a null-safe manner,
0710: * using the default value if the the conversion fails.
0711: * <p>
0712: * If the value is a <code>Boolean</code> its value is returned.
0713: * If the value is a <code>String</code> and it equals 'true' ignoring case
0714: * then <code>true</code> is returned, otherwise <code>false</code>.
0715: * If the value is a <code>Number</code> an integer zero value returns
0716: * <code>false</code> and non-zero returns <code>true</code>.
0717: * Otherwise, <code>defaultValue</code> is returned.
0718: *
0719: * @param map the map to use
0720: * @param key the key to look up
0721: * @param defaultValue return if the value is null or if the
0722: * conversion fails
0723: * @return the value in the Map as a Boolean, <code>defaultValue</code> if null map input
0724: */
0725: public static boolean getBooleanValue(final Map map,
0726: final Object key, boolean defaultValue) {
0727: Boolean booleanObject = getBoolean(map, key);
0728: if (booleanObject == null) {
0729: return defaultValue;
0730: }
0731: return booleanObject.booleanValue();
0732: }
0733:
0734: /**
0735: * Gets a byte from a Map in a null-safe manner,
0736: * using the default value if the the conversion fails.
0737: * <p>
0738: * The byte is obtained from the results of {@link #getNumber(Map,Object)}.
0739: *
0740: * @param map the map to use
0741: * @param key the key to look up
0742: * @param defaultValue return if the value is null or if the
0743: * conversion fails
0744: * @return the value in the Map as a byte, <code>defaultValue</code> if null map input
0745: */
0746: public static byte getByteValue(final Map map, final Object key,
0747: byte defaultValue) {
0748: Byte byteObject = getByte(map, key);
0749: if (byteObject == null) {
0750: return defaultValue;
0751: }
0752: return byteObject.byteValue();
0753: }
0754:
0755: /**
0756: * Gets a short from a Map in a null-safe manner,
0757: * using the default value if the the conversion fails.
0758: * <p>
0759: * The short is obtained from the results of {@link #getNumber(Map,Object)}.
0760: *
0761: * @param map the map to use
0762: * @param key the key to look up
0763: * @param defaultValue return if the value is null or if the
0764: * conversion fails
0765: * @return the value in the Map as a short, <code>defaultValue</code> if null map input
0766: */
0767: public static short getShortValue(final Map map, final Object key,
0768: short defaultValue) {
0769: Short shortObject = getShort(map, key);
0770: if (shortObject == null) {
0771: return defaultValue;
0772: }
0773: return shortObject.shortValue();
0774: }
0775:
0776: /**
0777: * Gets an int from a Map in a null-safe manner,
0778: * using the default value if the the conversion fails.
0779: * <p>
0780: * The int is obtained from the results of {@link #getNumber(Map,Object)}.
0781: *
0782: * @param map the map to use
0783: * @param key the key to look up
0784: * @param defaultValue return if the value is null or if the
0785: * conversion fails
0786: * @return the value in the Map as an int, <code>defaultValue</code> if null map input
0787: */
0788: public static int getIntValue(final Map map, final Object key,
0789: int defaultValue) {
0790: Integer integerObject = getInteger(map, key);
0791: if (integerObject == null) {
0792: return defaultValue;
0793: }
0794: return integerObject.intValue();
0795: }
0796:
0797: /**
0798: * Gets a long from a Map in a null-safe manner,
0799: * using the default value if the the conversion fails.
0800: * <p>
0801: * The long is obtained from the results of {@link #getNumber(Map,Object)}.
0802: *
0803: * @param map the map to use
0804: * @param key the key to look up
0805: * @param defaultValue return if the value is null or if the
0806: * conversion fails
0807: * @return the value in the Map as a long, <code>defaultValue</code> if null map input
0808: */
0809: public static long getLongValue(final Map map, final Object key,
0810: long defaultValue) {
0811: Long longObject = getLong(map, key);
0812: if (longObject == null) {
0813: return defaultValue;
0814: }
0815: return longObject.longValue();
0816: }
0817:
0818: /**
0819: * Gets a float from a Map in a null-safe manner,
0820: * using the default value if the the conversion fails.
0821: * <p>
0822: * The float is obtained from the results of {@link #getNumber(Map,Object)}.
0823: *
0824: * @param map the map to use
0825: * @param key the key to look up
0826: * @param defaultValue return if the value is null or if the
0827: * conversion fails
0828: * @return the value in the Map as a float, <code>defaultValue</code> if null map input
0829: */
0830: public static float getFloatValue(final Map map, final Object key,
0831: float defaultValue) {
0832: Float floatObject = getFloat(map, key);
0833: if (floatObject == null) {
0834: return defaultValue;
0835: }
0836: return floatObject.floatValue();
0837: }
0838:
0839: /**
0840: * Gets a double from a Map in a null-safe manner,
0841: * using the default value if the the conversion fails.
0842: * <p>
0843: * The double is obtained from the results of {@link #getNumber(Map,Object)}.
0844: *
0845: * @param map the map to use
0846: * @param key the key to look up
0847: * @param defaultValue return if the value is null or if the
0848: * conversion fails
0849: * @return the value in the Map as a double, <code>defaultValue</code> if null map input
0850: */
0851: public static double getDoubleValue(final Map map,
0852: final Object key, double defaultValue) {
0853: Double doubleObject = getDouble(map, key);
0854: if (doubleObject == null) {
0855: return defaultValue;
0856: }
0857: return doubleObject.doubleValue();
0858: }
0859:
0860: // Conversion methods
0861: //-------------------------------------------------------------------------
0862: /**
0863: * Gets a new Properties object initialised with the values from a Map.
0864: * A null input will return an empty properties object.
0865: *
0866: * @param map the map to convert to a Properties object, may not be null
0867: * @return the properties object
0868: */
0869: public static Properties toProperties(final Map map) {
0870: Properties answer = new Properties();
0871: if (map != null) {
0872: for (Iterator iter = map.entrySet().iterator(); iter
0873: .hasNext();) {
0874: Map.Entry entry = (Map.Entry) iter.next();
0875: Object key = entry.getKey();
0876: Object value = entry.getValue();
0877: answer.put(key, value);
0878: }
0879: }
0880: return answer;
0881: }
0882:
0883: /**
0884: * Creates a new HashMap using data copied from a ResourceBundle.
0885: *
0886: * @param resourceBundle the resource bundle to convert, may not be null
0887: * @return the hashmap containing the data
0888: * @throws NullPointerException if the bundle is null
0889: */
0890: public static Map toMap(final ResourceBundle resourceBundle) {
0891: Enumeration enumeration = resourceBundle.getKeys();
0892: Map map = new HashMap();
0893:
0894: while (enumeration.hasMoreElements()) {
0895: String key = (String) enumeration.nextElement();
0896: Object value = resourceBundle.getObject(key);
0897: map.put(key, value);
0898: }
0899:
0900: return map;
0901: }
0902:
0903: // Printing methods
0904: //-------------------------------------------------------------------------
0905: /**
0906: * Prints the given map with nice line breaks.
0907: * <p>
0908: * This method prints a nicely formatted String describing the Map.
0909: * Each map entry will be printed with key and value.
0910: * When the value is a Map, recursive behaviour occurs.
0911: * <p>
0912: * This method is NOT thread-safe in any special way. You must manually
0913: * synchronize on either this class or the stream as required.
0914: *
0915: * @param out the stream to print to, must not be null
0916: * @param label The label to be used, may be <code>null</code>.
0917: * If <code>null</code>, the label is not output.
0918: * It typically represents the name of the property in a bean or similar.
0919: * @param map The map to print, may be <code>null</code>.
0920: * If <code>null</code>, the text 'null' is output.
0921: * @throws NullPointerException if the stream is <code>null</code>
0922: */
0923: public static void verbosePrint(final PrintStream out,
0924: final Object label, final Map map) {
0925:
0926: verbosePrintInternal(out, label, map, new ArrayStack(), false);
0927: }
0928:
0929: /**
0930: * Prints the given map with nice line breaks.
0931: * <p>
0932: * This method prints a nicely formatted String describing the Map.
0933: * Each map entry will be printed with key, value and value classname.
0934: * When the value is a Map, recursive behaviour occurs.
0935: * <p>
0936: * This method is NOT thread-safe in any special way. You must manually
0937: * synchronize on either this class or the stream as required.
0938: *
0939: * @param out the stream to print to, must not be null
0940: * @param label The label to be used, may be <code>null</code>.
0941: * If <code>null</code>, the label is not output.
0942: * It typically represents the name of the property in a bean or similar.
0943: * @param map The map to print, may be <code>null</code>.
0944: * If <code>null</code>, the text 'null' is output.
0945: * @throws NullPointerException if the stream is <code>null</code>
0946: */
0947: public static void debugPrint(final PrintStream out,
0948: final Object label, final Map map) {
0949:
0950: verbosePrintInternal(out, label, map, new ArrayStack(), true);
0951: }
0952:
0953: // Implementation methods
0954: //-------------------------------------------------------------------------
0955: /**
0956: * Logs the given exception to <code>System.out</code>.
0957: * <p>
0958: * This method exists as Jakarta Collections does not depend on logging.
0959: *
0960: * @param ex the exception to log
0961: */
0962: protected static void logInfo(final Exception ex) {
0963: System.out.println("INFO: Exception: " + ex);
0964: }
0965:
0966: /**
0967: * Implementation providing functionality for {@link #debugPrint} and for
0968: * {@link #verbosePrint}. This prints the given map with nice line breaks.
0969: * If the debug flag is true, it additionally prints the type of the object
0970: * value. If the contents of a map include the map itself, then the text
0971: * <em>(this Map)</em> is printed out. If the contents include a
0972: * parent container of the map, the the text <em>(ancestor[i] Map)</em> is
0973: * printed, where i actually indicates the number of levels which must be
0974: * traversed in the sequential list of ancestors (e.g. father, grandfather,
0975: * great-grandfather, etc).
0976: *
0977: * @param out the stream to print to
0978: * @param label the label to be used, may be <code>null</code>.
0979: * If <code>null</code>, the label is not output.
0980: * It typically represents the name of the property in a bean or similar.
0981: * @param map the map to print, may be <code>null</code>.
0982: * If <code>null</code>, the text 'null' is output
0983: * @param lineage a stack consisting of any maps in which the previous
0984: * argument is contained. This is checked to avoid infinite recursion when
0985: * printing the output
0986: * @param debug flag indicating whether type names should be output.
0987: * @throws NullPointerException if the stream is <code>null</code>
0988: */
0989: private static void verbosePrintInternal(final PrintStream out,
0990: final Object label, final Map map,
0991: final ArrayStack lineage, final boolean debug) {
0992:
0993: printIndent(out, lineage.size());
0994:
0995: if (map == null) {
0996: if (label != null) {
0997: out.print(label);
0998: out.print(" = ");
0999: }
1000: out.println("null");
1001: return;
1002: }
1003: if (label != null) {
1004: out.print(label);
1005: out.println(" = ");
1006: }
1007:
1008: printIndent(out, lineage.size());
1009: out.println("{");
1010:
1011: lineage.push(map);
1012:
1013: for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
1014: Map.Entry entry = (Map.Entry) it.next();
1015: Object childKey = entry.getKey();
1016: Object childValue = entry.getValue();
1017: if (childValue instanceof Map
1018: && !lineage.contains(childValue)) {
1019: verbosePrintInternal(out, (childKey == null ? "null"
1020: : childKey), (Map) childValue, lineage, debug);
1021: } else {
1022: printIndent(out, lineage.size());
1023: out.print(childKey);
1024: out.print(" = ");
1025:
1026: final int lineageIndex = lineage.indexOf(childValue);
1027: if (lineageIndex == -1) {
1028: out.print(childValue);
1029: } else if (lineage.size() - 1 == lineageIndex) {
1030: out.print("(this Map)");
1031: } else {
1032: out.print("(ancestor["
1033: + (lineage.size() - 1 - lineageIndex - 1)
1034: + "] Map)");
1035: }
1036:
1037: if (debug && childValue != null) {
1038: out.print(' ');
1039: out.println(childValue.getClass().getName());
1040: } else {
1041: out.println();
1042: }
1043: }
1044: }
1045:
1046: lineage.pop();
1047:
1048: printIndent(out, lineage.size());
1049: out.println(debug ? "} " + map.getClass().getName() : "}");
1050: }
1051:
1052: /**
1053: * Writes indentation to the given stream.
1054: *
1055: * @param out the stream to indent
1056: */
1057: private static void printIndent(final PrintStream out,
1058: final int indent) {
1059: for (int i = 0; i < indent; i++) {
1060: out.print(INDENT_STRING);
1061: }
1062: }
1063:
1064: // Misc
1065: //-----------------------------------------------------------------------
1066: /**
1067: * Inverts the supplied map returning a new HashMap such that the keys of
1068: * the input are swapped with the values.
1069: * <p>
1070: * This operation assumes that the inverse mapping is well defined.
1071: * If the input map had multiple entries with the same value mapped to
1072: * different keys, the returned map will map one of those keys to the
1073: * value, but the exact key which will be mapped is undefined.
1074: *
1075: * @param map the map to invert, may not be null
1076: * @return a new HashMap containing the inverted data
1077: * @throws NullPointerException if the map is null
1078: */
1079: public static Map invertMap(Map map) {
1080: Map out = new HashMap(map.size());
1081: for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
1082: Map.Entry entry = (Map.Entry) it.next();
1083: out.put(entry.getValue(), entry.getKey());
1084: }
1085: return out;
1086: }
1087:
1088: //-----------------------------------------------------------------------
1089: /**
1090: * Protects against adding null values to a map.
1091: * <p>
1092: * This method checks the value being added to the map, and if it is null
1093: * it is replaced by an empty string.
1094: * <p>
1095: * This could be useful if the map does not accept null values, or for
1096: * receiving data from a source that may provide null or empty string
1097: * which should be held in the same way in the map.
1098: * <p>
1099: * Keys are not validated.
1100: *
1101: * @param map the map to add to, may not be null
1102: * @param key the key
1103: * @param value the value, null converted to ""
1104: * @throws NullPointerException if the map is null
1105: */
1106: public static void safeAddToMap(Map map, Object key, Object value)
1107: throws NullPointerException {
1108: if (value == null) {
1109: map.put(key, "");
1110: } else {
1111: map.put(key, value);
1112: }
1113: }
1114:
1115: //-----------------------------------------------------------------------
1116: /**
1117: * Puts all the keys and values from the specified array into the map.
1118: * <p>
1119: * This method is an alternative to the {@link java.util.Map#putAll(java.util.Map)}
1120: * method and constructors. It allows you to build a map from an object array
1121: * of various possible styles.
1122: * <p>
1123: * If the first entry in the object array implements {@link java.util.Map.Entry}
1124: * or {@link KeyValue} then the key and value are added from that object.
1125: * If the first entry in the object array is an object array itself, then
1126: * it is assumed that index 0 in the sub-array is the key and index 1 is the value.
1127: * Otherwise, the array is treated as keys and values in alternate indices.
1128: * <p>
1129: * For example, to create a color map:
1130: * <pre>
1131: * Map colorMap = MapUtils.putAll(new HashMap(), new String[][] {
1132: * {"RED", "#FF0000"},
1133: * {"GREEN", "#00FF00"},
1134: * {"BLUE", "#0000FF"}
1135: * });
1136: * </pre>
1137: * or:
1138: * <pre>
1139: * Map colorMap = MapUtils.putAll(new HashMap(), new String[] {
1140: * "RED", "#FF0000",
1141: * "GREEN", "#00FF00",
1142: * "BLUE", "#0000FF"
1143: * });
1144: * </pre>
1145: * or:
1146: * <pre>
1147: * Map colorMap = MapUtils.putAll(new HashMap(), new Map.Entry[] {
1148: * new DefaultMapEntry("RED", "#FF0000"),
1149: * new DefaultMapEntry("GREEN", "#00FF00"),
1150: * new DefaultMapEntry("BLUE", "#0000FF")
1151: * });
1152: * </pre>
1153: *
1154: * @param map the map to populate, must not be null
1155: * @param array an array to populate from, null ignored
1156: * @return the input map
1157: * @throws NullPointerException if map is null
1158: * @throws IllegalArgumentException if sub-array or entry matching used and an
1159: * entry is invalid
1160: * @throws ClassCastException if the array contents is mixed
1161: * @since Commons Collections 3.2
1162: */
1163: public static Map putAll(Map map, Object[] array) {
1164: map.size(); // force NPE
1165: if (array == null || array.length == 0) {
1166: return map;
1167: }
1168: Object obj = array[0];
1169: if (obj instanceof Map.Entry) {
1170: for (int i = 0; i < array.length; i++) {
1171: Map.Entry entry = (Map.Entry) array[i];
1172: map.put(entry.getKey(), entry.getValue());
1173: }
1174: } else if (obj instanceof KeyValue) {
1175: for (int i = 0; i < array.length; i++) {
1176: KeyValue keyval = (KeyValue) array[i];
1177: map.put(keyval.getKey(), keyval.getValue());
1178: }
1179: } else if (obj instanceof Object[]) {
1180: for (int i = 0; i < array.length; i++) {
1181: Object[] sub = (Object[]) array[i];
1182: if (sub == null || sub.length < 2) {
1183: throw new IllegalArgumentException(
1184: "Invalid array element: " + i);
1185: }
1186: map.put(sub[0], sub[1]);
1187: }
1188: } else {
1189: for (int i = 0; i < array.length - 1;) {
1190: map.put(array[i++], array[i++]);
1191: }
1192: }
1193: return map;
1194: }
1195:
1196: //-----------------------------------------------------------------------
1197: /**
1198: * Null-safe check if the specified map is empty.
1199: * <p>
1200: * Null returns true.
1201: *
1202: * @param map the map to check, may be null
1203: * @return true if empty or null
1204: * @since Commons Collections 3.2
1205: */
1206: public static boolean isEmpty(Map map) {
1207: return (map == null || map.isEmpty());
1208: }
1209:
1210: /**
1211: * Null-safe check if the specified map is not empty.
1212: * <p>
1213: * Null returns false.
1214: *
1215: * @param map the map to check, may be null
1216: * @return true if non-null and non-empty
1217: * @since Commons Collections 3.2
1218: */
1219: public static boolean isNotEmpty(Map map) {
1220: return !MapUtils.isEmpty(map);
1221: }
1222:
1223: // Map decorators
1224: //-----------------------------------------------------------------------
1225: /**
1226: * Returns a synchronized map backed by the given map.
1227: * <p>
1228: * You must manually synchronize on the returned buffer's iterator to
1229: * avoid non-deterministic behavior:
1230: *
1231: * <pre>
1232: * Map m = MapUtils.synchronizedMap(myMap);
1233: * Set s = m.keySet(); // outside synchronized block
1234: * synchronized (m) { // synchronized on MAP!
1235: * Iterator i = s.iterator();
1236: * while (i.hasNext()) {
1237: * process (i.next());
1238: * }
1239: * }
1240: * </pre>
1241: *
1242: * This method uses the implementation in {@link java.util.Collections Collections}.
1243: *
1244: * @param map the map to synchronize, must not be null
1245: * @return a synchronized map backed by the given map
1246: * @throws IllegalArgumentException if the map is null
1247: */
1248: public static Map synchronizedMap(Map map) {
1249: return Collections.synchronizedMap(map);
1250: }
1251:
1252: /**
1253: * Returns an unmodifiable map backed by the given map.
1254: * <p>
1255: * This method uses the implementation in the decorators subpackage.
1256: *
1257: * @param map the map to make unmodifiable, must not be null
1258: * @return an unmodifiable map backed by the given map
1259: * @throws IllegalArgumentException if the map is null
1260: */
1261: public static Map unmodifiableMap(Map map) {
1262: return UnmodifiableMap.decorate(map);
1263: }
1264:
1265: /**
1266: * Returns a predicated (validating) map backed by the given map.
1267: * <p>
1268: * Only objects that pass the tests in the given predicates can be added to the map.
1269: * Trying to add an invalid object results in an IllegalArgumentException.
1270: * Keys must pass the key predicate, values must pass the value predicate.
1271: * It is important not to use the original map after invoking this method,
1272: * as it is a backdoor for adding invalid objects.
1273: *
1274: * @param map the map to predicate, must not be null
1275: * @param keyPred the predicate for keys, null means no check
1276: * @param valuePred the predicate for values, null means no check
1277: * @return a predicated map backed by the given map
1278: * @throws IllegalArgumentException if the Map is null
1279: */
1280: public static Map predicatedMap(Map map, Predicate keyPred,
1281: Predicate valuePred) {
1282: return PredicatedMap.decorate(map, keyPred, valuePred);
1283: }
1284:
1285: /**
1286: * Returns a typed map backed by the given map.
1287: * <p>
1288: * Only keys and values of the specified types can be added to the map.
1289: *
1290: * @param map the map to limit to a specific type, must not be null
1291: * @param keyType the type of keys which may be added to the map, must not be null
1292: * @param valueType the type of values which may be added to the map, must not be null
1293: * @return a typed map backed by the specified map
1294: * @throws IllegalArgumentException if the Map or Class is null
1295: */
1296: public static Map typedMap(Map map, Class keyType, Class valueType) {
1297: return TypedMap.decorate(map, keyType, valueType);
1298: }
1299:
1300: /**
1301: * Returns a transformed map backed by the given map.
1302: * <p>
1303: * This method returns a new map (decorating the specified map) that
1304: * will transform any new entries added to it.
1305: * Existing entries in the specified map will not be transformed.
1306: * If you want that behaviour, see {@link TransformedMap#decorateTransform}.
1307: * <p>
1308: * Each object is passed through the transformers as it is added to the
1309: * Map. It is important not to use the original map after invoking this
1310: * method, as it is a backdoor for adding untransformed objects.
1311: * <p>
1312: * If there are any elements already in the map being decorated, they
1313: * are NOT transformed.
1314: *
1315: * @param map the map to transform, must not be null, typically empty
1316: * @param keyTransformer the transformer for the map keys, null means no transformation
1317: * @param valueTransformer the transformer for the map values, null means no transformation
1318: * @return a transformed map backed by the given map
1319: * @throws IllegalArgumentException if the Map is null
1320: */
1321: public static Map transformedMap(Map map,
1322: Transformer keyTransformer, Transformer valueTransformer) {
1323: return TransformedMap.decorate(map, keyTransformer,
1324: valueTransformer);
1325: }
1326:
1327: /**
1328: * Returns a fixed-sized map backed by the given map.
1329: * Elements may not be added or removed from the returned map, but
1330: * existing elements can be changed (for instance, via the
1331: * {@link Map#put(Object,Object)} method).
1332: *
1333: * @param map the map whose size to fix, must not be null
1334: * @return a fixed-size map backed by that map
1335: * @throws IllegalArgumentException if the Map is null
1336: */
1337: public static Map fixedSizeMap(Map map) {
1338: return FixedSizeMap.decorate(map);
1339: }
1340:
1341: /**
1342: * Returns a "lazy" map whose values will be created on demand.
1343: * <p>
1344: * When the key passed to the returned map's {@link Map#get(Object)}
1345: * method is not present in the map, then the factory will be used
1346: * to create a new object and that object will become the value
1347: * associated with that key.
1348: * <p>
1349: * For instance:
1350: * <pre>
1351: * Factory factory = new Factory() {
1352: * public Object create() {
1353: * return new Date();
1354: * }
1355: * }
1356: * Map lazyMap = MapUtils.lazyMap(new HashMap(), factory);
1357: * Object obj = lazyMap.get("test");
1358: * </pre>
1359: *
1360: * After the above code is executed, <code>obj</code> will contain
1361: * a new <code>Date</code> instance. Furthermore, that <code>Date</code>
1362: * instance is the value for the <code>"test"</code> key in the map.
1363: *
1364: * @param map the map to make lazy, must not be null
1365: * @param factory the factory for creating new objects, must not be null
1366: * @return a lazy map backed by the given map
1367: * @throws IllegalArgumentException if the Map or Factory is null
1368: */
1369: public static Map lazyMap(Map map, Factory factory) {
1370: return LazyMap.decorate(map, factory);
1371: }
1372:
1373: /**
1374: * Returns a "lazy" map whose values will be created on demand.
1375: * <p>
1376: * When the key passed to the returned map's {@link Map#get(Object)}
1377: * method is not present in the map, then the factory will be used
1378: * to create a new object and that object will become the value
1379: * associated with that key. The factory is a {@link Transformer}
1380: * that will be passed the key which it must transform into the value.
1381: * <p>
1382: * For instance:
1383: * <pre>
1384: * Transformer factory = new Transformer() {
1385: * public Object transform(Object mapKey) {
1386: * return new File(mapKey);
1387: * }
1388: * }
1389: * Map lazyMap = MapUtils.lazyMap(new HashMap(), factory);
1390: * Object obj = lazyMap.get("C:/dev");
1391: * </pre>
1392: *
1393: * After the above code is executed, <code>obj</code> will contain
1394: * a new <code>File</code> instance for the C drive dev directory.
1395: * Furthermore, that <code>File</code> instance is the value for the
1396: * <code>"C:/dev"</code> key in the map.
1397: * <p>
1398: * If a lazy map is wrapped by a synchronized map, the result is a simple
1399: * synchronized cache. When an object is not is the cache, the cache itself
1400: * calls back to the factory Transformer to populate itself, all within the
1401: * same synchronized block.
1402: *
1403: * @param map the map to make lazy, must not be null
1404: * @param transformerFactory the factory for creating new objects, must not be null
1405: * @return a lazy map backed by the given map
1406: * @throws IllegalArgumentException if the Map or Transformer is null
1407: */
1408: public static Map lazyMap(Map map, Transformer transformerFactory) {
1409: return LazyMap.decorate(map, transformerFactory);
1410: }
1411:
1412: /**
1413: * Returns a map that maintains the order of keys that are added
1414: * backed by the given map.
1415: * <p>
1416: * If a key is added twice, the order is determined by the first add.
1417: * The order is observed through the keySet, values and entrySet.
1418: *
1419: * @param map the map to order, must not be null
1420: * @return an ordered map backed by the given map
1421: * @throws IllegalArgumentException if the Map is null
1422: */
1423: public static Map orderedMap(Map map) {
1424: return ListOrderedMap.decorate(map);
1425: }
1426:
1427: /**
1428: * Creates a mult-value map backed by the given map which returns
1429: * collections of type ArrayList.
1430: *
1431: * @param map the map to decorate
1432: * @return a multi-value map backed by the given map which returns ArrayLists of values.
1433: * @see MultiValueMap
1434: * @since Commons Collections 3.2
1435: */
1436: public static Map multiValueMap(Map map) {
1437: return MultiValueMap.decorate(map);
1438: }
1439:
1440: /**
1441: * Creates a multi-value map backed by the given map which returns
1442: * collections of the specified type.
1443: *
1444: * @param map the map to decorate
1445: * @param collectionClass the type of collections to return from the map (must contain public no-arg constructor
1446: * and extend Collection).
1447: * @return a multi-value map backed by the given map which returns collections of the specified type
1448: * @see MultiValueMap
1449: * @since Commons Collections 3.2
1450: */
1451: public static Map multiValueMap(Map map, Class collectionClass) {
1452: return MultiValueMap.decorate(map, collectionClass);
1453: }
1454:
1455: /**
1456: * Creates a multi-value map backed by the given map which returns
1457: * collections created by the specified collection factory.
1458: *
1459: * @param map the map to decorate
1460: * @param collectionFactory a factor which creates collection objects
1461: * @return a multi-value map backed by the given map which returns collections
1462: * created by the specified collection factory
1463: * @see MultiValueMap
1464: * @since Commons Collections 3.2
1465: */
1466: public static Map multiValueMap(Map map, Factory collectionFactory) {
1467: return MultiValueMap.decorate(map, collectionFactory);
1468: }
1469:
1470: // SortedMap decorators
1471: //-----------------------------------------------------------------------
1472: /**
1473: * Returns a synchronized sorted map backed by the given sorted map.
1474: * <p>
1475: * You must manually synchronize on the returned buffer's iterator to
1476: * avoid non-deterministic behavior:
1477: *
1478: * <pre>
1479: * Map m = MapUtils.synchronizedSortedMap(myMap);
1480: * Set s = m.keySet(); // outside synchronized block
1481: * synchronized (m) { // synchronized on MAP!
1482: * Iterator i = s.iterator();
1483: * while (i.hasNext()) {
1484: * process (i.next());
1485: * }
1486: * }
1487: * </pre>
1488: *
1489: * This method uses the implementation in {@link java.util.Collections Collections}.
1490: *
1491: * @param map the map to synchronize, must not be null
1492: * @return a synchronized map backed by the given map
1493: * @throws IllegalArgumentException if the map is null
1494: */
1495: public static Map synchronizedSortedMap(SortedMap map) {
1496: return Collections.synchronizedSortedMap(map);
1497: }
1498:
1499: /**
1500: * Returns an unmodifiable sorted map backed by the given sorted map.
1501: * <p>
1502: * This method uses the implementation in the decorators subpackage.
1503: *
1504: * @param map the sorted map to make unmodifiable, must not be null
1505: * @return an unmodifiable map backed by the given map
1506: * @throws IllegalArgumentException if the map is null
1507: */
1508: public static Map unmodifiableSortedMap(SortedMap map) {
1509: return UnmodifiableSortedMap.decorate(map);
1510: }
1511:
1512: /**
1513: * Returns a predicated (validating) sorted map backed by the given map.
1514: * <p>
1515: * Only objects that pass the tests in the given predicates can be added to the map.
1516: * Trying to add an invalid object results in an IllegalArgumentException.
1517: * Keys must pass the key predicate, values must pass the value predicate.
1518: * It is important not to use the original map after invoking this method,
1519: * as it is a backdoor for adding invalid objects.
1520: *
1521: * @param map the map to predicate, must not be null
1522: * @param keyPred the predicate for keys, null means no check
1523: * @param valuePred the predicate for values, null means no check
1524: * @return a predicated map backed by the given map
1525: * @throws IllegalArgumentException if the SortedMap is null
1526: */
1527: public static SortedMap predicatedSortedMap(SortedMap map,
1528: Predicate keyPred, Predicate valuePred) {
1529: return PredicatedSortedMap.decorate(map, keyPred, valuePred);
1530: }
1531:
1532: /**
1533: * Returns a typed sorted map backed by the given map.
1534: * <p>
1535: * Only keys and values of the specified types can be added to the map.
1536: *
1537: * @param map the map to limit to a specific type, must not be null
1538: * @param keyType the type of keys which may be added to the map, must not be null
1539: * @param valueType the type of values which may be added to the map, must not be null
1540: * @return a typed map backed by the specified map
1541: */
1542: public static SortedMap typedSortedMap(SortedMap map,
1543: Class keyType, Class valueType) {
1544: return TypedSortedMap.decorate(map, keyType, valueType);
1545: }
1546:
1547: /**
1548: * Returns a transformed sorted map backed by the given map.
1549: * <p>
1550: * This method returns a new sorted map (decorating the specified map) that
1551: * will transform any new entries added to it.
1552: * Existing entries in the specified map will not be transformed.
1553: * If you want that behaviour, see {@link TransformedSortedMap#decorateTransform}.
1554: * <p>
1555: * Each object is passed through the transformers as it is added to the
1556: * Map. It is important not to use the original map after invoking this
1557: * method, as it is a backdoor for adding untransformed objects.
1558: * <p>
1559: * If there are any elements already in the map being decorated, they
1560: * are NOT transformed.
1561: *
1562: * @param map the map to transform, must not be null, typically empty
1563: * @param keyTransformer the transformer for the map keys, null means no transformation
1564: * @param valueTransformer the transformer for the map values, null means no transformation
1565: * @return a transformed map backed by the given map
1566: * @throws IllegalArgumentException if the SortedMap is null
1567: */
1568: public static SortedMap transformedSortedMap(SortedMap map,
1569: Transformer keyTransformer, Transformer valueTransformer) {
1570: return TransformedSortedMap.decorate(map, keyTransformer,
1571: valueTransformer);
1572: }
1573:
1574: /**
1575: * Returns a fixed-sized sorted map backed by the given sorted map.
1576: * Elements may not be added or removed from the returned map, but
1577: * existing elements can be changed (for instance, via the
1578: * {@link Map#put(Object,Object)} method).
1579: *
1580: * @param map the map whose size to fix, must not be null
1581: * @return a fixed-size map backed by that map
1582: * @throws IllegalArgumentException if the SortedMap is null
1583: */
1584: public static SortedMap fixedSizeSortedMap(SortedMap map) {
1585: return FixedSizeSortedMap.decorate(map);
1586: }
1587:
1588: /**
1589: * Returns a "lazy" sorted map whose values will be created on demand.
1590: * <p>
1591: * When the key passed to the returned map's {@link Map#get(Object)}
1592: * method is not present in the map, then the factory will be used
1593: * to create a new object and that object will become the value
1594: * associated with that key.
1595: * <p>
1596: * For instance:
1597: *
1598: * <pre>
1599: * Factory factory = new Factory() {
1600: * public Object create() {
1601: * return new Date();
1602: * }
1603: * }
1604: * SortedMap lazy = MapUtils.lazySortedMap(new TreeMap(), factory);
1605: * Object obj = lazy.get("test");
1606: * </pre>
1607: *
1608: * After the above code is executed, <code>obj</code> will contain
1609: * a new <code>Date</code> instance. Furthermore, that <code>Date</code>
1610: * instance is the value for the <code>"test"</code> key.
1611: *
1612: * @param map the map to make lazy, must not be null
1613: * @param factory the factory for creating new objects, must not be null
1614: * @return a lazy map backed by the given map
1615: * @throws IllegalArgumentException if the SortedMap or Factory is null
1616: */
1617: public static SortedMap lazySortedMap(SortedMap map, Factory factory) {
1618: return LazySortedMap.decorate(map, factory);
1619: }
1620:
1621: /**
1622: * Returns a "lazy" sorted map whose values will be created on demand.
1623: * <p>
1624: * When the key passed to the returned map's {@link Map#get(Object)}
1625: * method is not present in the map, then the factory will be used
1626: * to create a new object and that object will become the value
1627: * associated with that key. The factory is a {@link Transformer}
1628: * that will be passed the key which it must transform into the value.
1629: * <p>
1630: * For instance:
1631: * <pre>
1632: * Transformer factory = new Transformer() {
1633: * public Object transform(Object mapKey) {
1634: * return new File(mapKey);
1635: * }
1636: * }
1637: * SortedMap lazy = MapUtils.lazySortedMap(new TreeMap(), factory);
1638: * Object obj = lazy.get("C:/dev");
1639: * </pre>
1640: *
1641: * After the above code is executed, <code>obj</code> will contain
1642: * a new <code>File</code> instance for the C drive dev directory.
1643: * Furthermore, that <code>File</code> instance is the value for the
1644: * <code>"C:/dev"</code> key in the map.
1645: * <p>
1646: * If a lazy map is wrapped by a synchronized map, the result is a simple
1647: * synchronized cache. When an object is not is the cache, the cache itself
1648: * calls back to the factory Transformer to populate itself, all within the
1649: * same synchronized block.
1650: *
1651: * @param map the map to make lazy, must not be null
1652: * @param transformerFactory the factory for creating new objects, must not be null
1653: * @return a lazy map backed by the given map
1654: * @throws IllegalArgumentException if the Map or Transformer is null
1655: */
1656: public static SortedMap lazySortedMap(SortedMap map,
1657: Transformer transformerFactory) {
1658: return LazySortedMap.decorate(map, transformerFactory);
1659: }
1660:
1661: }
|