001: package net.sourceforge.pmd.util;
002:
003: import java.util.HashMap;
004: import java.util.HashSet;
005: import java.util.Map;
006: import java.util.Set;
007:
008: /**
009: * Generic collection and array-related utility functions.
010: *
011: * @author Brian Remedios
012: * @version $Revision$
013: */
014: public class CollectionUtil {
015:
016: public static final TypeMap collectionInterfacesByNames = new TypeMap(
017: new Class[] { java.util.List.class,
018: java.util.Collection.class, java.util.Map.class,
019: java.util.Set.class, });
020:
021: public static final TypeMap collectionClassesByNames = new TypeMap(
022: new Class[] { java.util.ArrayList.class,
023: java.util.LinkedList.class, java.util.Vector.class,
024: java.util.HashMap.class,
025: java.util.LinkedHashMap.class,
026: java.util.TreeMap.class, java.util.TreeSet.class,
027: java.util.HashSet.class,
028: java.util.LinkedHashSet.class });
029:
030: private CollectionUtil() {
031: };
032:
033: /**
034: * Returns the collection type if we recognize it by its short name.
035: *
036: * @param shortName String
037: * @return Class
038: */
039: public static Class getCollectionTypeFor(String shortName) {
040: Class cls = collectionClassesByNames.typeFor(shortName);
041: if (cls != null)
042: return cls;
043:
044: return collectionInterfacesByNames.typeFor(shortName);
045: }
046:
047: /**
048: * Return whether we can identify the typeName as a java.util collection class
049: * or interface as specified.
050: *
051: * @param typeName String
052: * @param includeInterfaces boolean
053: * @return boolean
054: */
055: public static boolean isCollectionType(String typeName,
056: boolean includeInterfaces) {
057:
058: if (collectionClassesByNames.contains(typeName))
059: return true;
060:
061: return includeInterfaces
062: && collectionInterfacesByNames.contains(typeName);
063: }
064:
065: /**
066: * Return whether we can identify the typeName as a java.util collection class
067: * or interface as specified.
068: *
069: * @param clazzType Class
070: * @param includeInterfaces boolean
071: * @return boolean
072: */
073: public static boolean isCollectionType(Class clazzType,
074: boolean includeInterfaces) {
075:
076: if (collectionClassesByNames.contains(clazzType)) {
077: return true;
078: }
079:
080: return includeInterfaces
081: && collectionInterfacesByNames.contains(clazzType);
082: }
083:
084: /**
085: * Returns the items as a populated set.
086: *
087: * @param items Object[]
088: * @return Set
089: */
090: public static <T> Set<T> asSet(T[] items) {
091:
092: Set<T> set = new HashSet<T>(items.length);
093: for (int i = 0; i < items.length; i++) {
094: set.add(items[i]);
095: }
096: return set;
097: }
098:
099: /**
100: * Creates and returns a map populated with the keyValuesSets where
101: * the value held by the tuples are they key and value in that order.
102: *
103: * @param keys K[]
104: * @param values V[]
105: * @return Map
106: */
107: public static <K, V> Map<K, V> mapFrom(K[] keys, V[] values) {
108: if (keys.length != values.length) {
109: throw new RuntimeException(
110: "mapFrom keys and values arrays have different sizes");
111: }
112: Map<K, V> map = new HashMap<K, V>(keys.length);
113: for (int i = 0; i < keys.length; i++) {
114: map.put(keys[i], values[i]);
115: }
116: return map;
117: }
118:
119: /**
120: * Returns a map based on the source but with the key & values swapped.
121: *
122: * @param source Map
123: * @return Map
124: */
125: public static <K, V> Map<V, K> invertedMapFrom(Map<K, V> source) {
126: Map<V, K> map = new HashMap<V, K>(source.size());
127: for (Map.Entry<K, V> entry : source.entrySet()) {
128: map.put(entry.getValue(), entry.getKey());
129: }
130: return map;
131: }
132:
133: /**
134: * Returns true if the objects are array instances and each of their elements compares
135: * via equals as well.
136: *
137: * @param value Object
138: * @param otherValue Object
139: * @return boolean
140: */
141: public static final boolean arraysAreEqual(Object value,
142: Object otherValue) {
143: if (value instanceof Object[]) {
144: if (otherValue instanceof Object[])
145: return valuesAreTransitivelyEqual((Object[]) value,
146: (Object[]) otherValue);
147: return false;
148: }
149: return false;
150: }
151:
152: /**
153: * Returns whether the arrays are equal by examining each of their elements, even if they are
154: * arrays themselves.
155: *
156: * @param thisArray Object[]
157: * @param thatArray Object[]
158: * @return boolean
159: */
160: public static final boolean valuesAreTransitivelyEqual(
161: Object[] this Array, Object[] thatArray) {
162: if (this Array == thatArray)
163: return true;
164: if ((this Array == null) || (thatArray == null))
165: return false;
166: if (this Array.length != thatArray.length)
167: return false;
168: for (int i = 0; i < this Array.length; i++) {
169: if (!areEqual(this Array[i], thatArray[i]))
170: return false; // recurse if req'd
171: }
172: return true;
173: }
174:
175: /**
176: * A comprehensive isEqual method that handles nulls and arrays safely.
177: *
178: * @param value Object
179: * @param otherValue Object
180: * @return boolean
181: */
182: public static final boolean areEqual(Object value, Object otherValue) {
183: if (value == otherValue)
184: return true;
185: if (value == null)
186: return false;
187: if (otherValue == null)
188: return false;
189:
190: if (value.getClass().getComponentType() != null)
191: return arraysAreEqual(value, otherValue);
192: return value.equals(otherValue);
193: }
194: }
|