1 package net.sourceforge.pmd.util;
2
3 import java.util.HashMap;
4 import java.util.HashSet;
5 import java.util.Map;
6 import java.util.Set;
7
8 /**
9 * Generic collection and array-related utility functions.
10 *
11 * @author Brian Remedios
12 * @version $Revision$
13 */
14 public class CollectionUtil {
15
16 public static final TypeMap collectionInterfacesByNames = new TypeMap( new Class[] {
17 java.util.List.class,
18 java.util.Collection.class,
19 java.util.Map.class,
20 java.util.Set.class,
21 });
22
23 public static final TypeMap collectionClassesByNames = new TypeMap( new Class[] {
24 java.util.ArrayList.class,
25 java.util.LinkedList.class,
26 java.util.Vector.class,
27 java.util.HashMap.class,
28 java.util.LinkedHashMap.class,
29 java.util.TreeMap.class,
30 java.util.TreeSet.class,
31 java.util.HashSet.class,
32 java.util.LinkedHashSet.class
33 });
34
35 private CollectionUtil() {};
36
37 /**
38 * Returns the collection type if we recognize it by its short name.
39 *
40 * @param shortName String
41 * @return Class
42 */
43 public static Class getCollectionTypeFor(String shortName) {
44 Class cls = collectionClassesByNames.typeFor(shortName);
45 if (cls != null) return cls;
46
47 return collectionInterfacesByNames.typeFor(shortName);
48 }
49
50 /**
51 * Return whether we can identify the typeName as a java.util collection class
52 * or interface as specified.
53 *
54 * @param typeName String
55 * @param includeInterfaces boolean
56 * @return boolean
57 */
58 public static boolean isCollectionType(String typeName, boolean includeInterfaces) {
59
60 if (collectionClassesByNames.contains(typeName)) return true;
61
62 return includeInterfaces && collectionInterfacesByNames.contains(typeName);
63 }
64
65 /**
66 * Return whether we can identify the typeName as a java.util collection class
67 * or interface as specified.
68 *
69 * @param clazzType Class
70 * @param includeInterfaces boolean
71 * @return boolean
72 */
73 public static boolean isCollectionType(Class clazzType, boolean includeInterfaces) {
74
75 if (collectionClassesByNames.contains(clazzType)) {
76 return true;
77 }
78
79 return includeInterfaces && collectionInterfacesByNames.contains(clazzType);
80 }
81
82 /**
83 * Returns the items as a populated set.
84 *
85 * @param items Object[]
86 * @return Set
87 */
88 public static <T> Set<T> asSet(T[] items) {
89
90 Set<T> set = new HashSet<T>(items.length);
91 for (int i=0; i<items.length; i++) {
92 set.add(items[i]);
93 }
94 return set;
95 }
96
97 /**
98 * Creates and returns a map populated with the keyValuesSets where
99 * the value held by the tuples are they key and value in that order.
100 *
101 * @param keys K[]
102 * @param values V[]
103 * @return Map
104 */
105 public static <K, V> Map<K, V> mapFrom(K[] keys, V[] values) {
106 if (keys.length != values.length) {
107 throw new RuntimeException("mapFrom keys and values arrays have different sizes");
108 }
109 Map<K, V> map = new HashMap<K, V>(keys.length);
110 for (int i=0; i<keys.length; i++) {
111 map.put(keys[i], values[i]);
112 }
113 return map;
114 }
115
116 /**
117 * Returns a map based on the source but with the key & values swapped.
118 *
119 * @param source Map
120 * @return Map
121 */
122 public static <K, V> Map<V, K> invertedMapFrom(Map<K, V> source) {
123 Map<V, K> map = new HashMap<V, K>(source.size());
124 for (Map.Entry<K, V> entry: source.entrySet()) {
125 map.put(entry.getValue(), entry.getKey());
126 }
127 return map;
128 }
129
130 /**
131 * Returns true if the objects are array instances and each of their elements compares
132 * via equals as well.
133 *
134 * @param value Object
135 * @param otherValue Object
136 * @return boolean
137 */
138 public static final boolean arraysAreEqual(Object value, Object otherValue) {
139 if (value instanceof Object[]) {
140 if (otherValue instanceof Object[]) return valuesAreTransitivelyEqual((Object[])value, (Object[])otherValue);
141 return false;
142 }
143 return false;
144 }
145
146 /**
147 * Returns whether the arrays are equal by examining each of their elements, even if they are
148 * arrays themselves.
149 *
150 * @param thisArray Object[]
151 * @param thatArray Object[]
152 * @return boolean
153 */
154 public static final boolean valuesAreTransitivelyEqual(Object[] thisArray, Object[] thatArray) {
155 if (thisArray == thatArray) return true;
156 if ((thisArray == null) || (thatArray == null)) return false;
157 if (thisArray.length != thatArray.length) return false;
158 for (int i = 0; i < thisArray.length; i++) {
159 if (!areEqual(thisArray[i], thatArray[i])) return false;
160 }
161 return true;
162 }
163
164 /**
165 * A comprehensive isEqual method that handles nulls and arrays safely.
166 *
167 * @param value Object
168 * @param otherValue Object
169 * @return boolean
170 */
171 public static final boolean areEqual(Object value, Object otherValue) {
172 if (value == otherValue) return true;
173 if (value == null) return false;
174 if (otherValue == null) return false;
175
176 if (value.getClass().getComponentType() != null) return arraysAreEqual(value, otherValue);
177 return value.equals(otherValue);
178 }
179 }