1 package org.codehaus.plexus.util;
2
3 /*
4 * Copyright The Codehaus Foundation.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.NoSuchElementException;
27 import java.util.Set;
28
29 /**
30 * @author <a href="mailto:olamy@codehaus.org">olamy</a>
31 *
32 */
33 public class CollectionUtils {
34 // ----------------------------------------------------------------------
35 // Static methods that can probably be moved to a real util class.
36 // ----------------------------------------------------------------------
37
38 /**
39 * Take a dominant and recessive Map and merge the key:value pairs where the recessive Map may add key:value pairs
40 * to the dominant Map but may not override any existing key:value pairs. If we have two Maps, a dominant and
41 * recessive, and their respective keys are as follows: dominantMapKeys = { a, b, c, d, e, f } recessiveMapKeys = {
42 * a, b, c, x, y, z } Then the result should be the following: resultantKeys = { a, b, c, d, e, f, x, y, z }
43 *
44 * @param dominantMap Dominant Map.
45 * @param recessiveMap Recessive Map.
46 * @param <K> type
47 * @param <V> type
48 * @return The result map with combined dominant and recessive values.
49 */
50 public static <K, V> Map<K, V> mergeMaps(Map<K, V> dominantMap, Map<K, V> recessiveMap) {
51
52 if (dominantMap == null && recessiveMap == null) {
53 return null;
54 }
55
56 if (dominantMap != null && recessiveMap == null) {
57 return dominantMap;
58 }
59
60 if (dominantMap == null) {
61 return recessiveMap;
62 }
63
64 Map<K, V> result = new HashMap<>();
65
66 // Grab the keys from the dominant and recessive maps.
67 Set<K> dominantMapKeys = dominantMap.keySet();
68 Set<K> recessiveMapKeys = recessiveMap.keySet();
69
70 // Create the set of keys that will be contributed by the
71 // recessive Map by subtracting the intersection of keys
72 // from the recessive Map's keys.
73 Collection<K> contributingRecessiveKeys = CollectionUtils.subtract(
74 recessiveMapKeys, CollectionUtils.intersection(dominantMapKeys, recessiveMapKeys));
75
76 result.putAll(dominantMap);
77
78 // Now take the keys we just found and extract the values from
79 // the recessiveMap and put the key:value pairs into the dominantMap.
80 for (K key : contributingRecessiveKeys) {
81 result.put(key, recessiveMap.get(key));
82 }
83
84 return result;
85 }
86
87 /**
88 * Take a series of <code>Map</code>s and merge them where the ordering of the array from 0..n is the dominant
89 * order.
90 *
91 * @param maps An array of Maps to merge.
92 * @param <K> type
93 * @param <V> type
94 * @return Map The result Map produced after the merging process.
95 */
96 public static <K, V> Map<K, V> mergeMaps(Map<K, V>[] maps) {
97 Map<K, V> result;
98
99 if (maps.length == 0) {
100 result = null;
101 } else if (maps.length == 1) {
102 result = maps[0];
103 } else {
104 result = mergeMaps(maps[0], maps[1]);
105
106 for (int i = 2; i < maps.length; i++) {
107 result = mergeMaps(result, maps[i]);
108 }
109 }
110
111 return result;
112 }
113
114 /**
115 * <p>
116 * Returns a {@link Collection} containing the intersection of the given {@link Collection}s.
117 * </p>
118 * The cardinality of each element in the returned {@link Collection} will be equal to the minimum of the
119 * cardinality of that element in the two given {@link Collection}s.
120 *
121 * @param a The first collection
122 * @param b The second collection
123 * @param <E> the type
124 * @see Collection#retainAll
125 * @return The intersection of a and b, never null
126 */
127 public static <E> Collection<E> intersection(final Collection<E> a, final Collection<E> b) {
128 ArrayList<E> list = new ArrayList<>();
129 Map<E, Integer> mapa = getCardinalityMap(a);
130 Map<E, Integer> mapb = getCardinalityMap(b);
131 Set<E> elts = new HashSet<>(a);
132 elts.addAll(b);
133 for (E obj : elts) {
134 for (int i = 0, m = Math.min(getFreq(obj, mapa), getFreq(obj, mapb)); i < m; i++) {
135 list.add(obj);
136 }
137 }
138 return list;
139 }
140
141 /**
142 * Returns a {@link Collection} containing <code>a - b</code>. The cardinality of each element <i>e</i> in
143 * the returned {@link Collection} will be the cardinality of <i>e</i> in <i>a</i> minus the cardinality of <i>e</i>
144 * in <i>b</i>, or zero, whichever is greater.
145 *
146 * @param a The start collection
147 * @param b The collection that will be subtracted
148 * @param <T> the type
149 * @see Collection#removeAll
150 * @return The result of the subtraction
151 */
152 public static <T> Collection<T> subtract(final Collection<T> a, final Collection<T> b) {
153 ArrayList<T> list = new ArrayList<>(a);
154 for (T aB : b) {
155 list.remove(aB);
156 }
157 return list;
158 }
159
160 /**
161 * Returns a {@link Map} mapping each unique element in the given {@link Collection} to an {@link Integer}
162 * representing the number of occurrences of that element in the {@link Collection}. An entry that maps to
163 * <code>null</code> indicates that the element does not appear in the given {@link Collection}.
164 *
165 * @param col The collection to count cardinalities for
166 * @param <E> the type
167 * @return A map of counts, indexed on each element in the collection
168 */
169 public static <E> Map<E, Integer> getCardinalityMap(final Collection<E> col) {
170 HashMap<E, Integer> count = new HashMap<>();
171 for (E obj : col) {
172 Integer c = count.get(obj);
173 if (null == c) {
174 count.put(obj, 1);
175 } else {
176 count.put(obj, c + 1);
177 }
178 }
179 return count;
180 }
181
182 public static <E> List<E> iteratorToList(Iterator<E> it) {
183 if (it == null) {
184 throw new NullPointerException("it cannot be null.");
185 }
186
187 List<E> list = new ArrayList<E>();
188
189 while (it.hasNext()) {
190 list.add(it.next());
191 }
192
193 return list;
194 }
195
196 // ----------------------------------------------------------------------
197 //
198 // ----------------------------------------------------------------------
199
200 private static <E> int getFreq(final E obj, final Map<E, Integer> freqMap) {
201 try {
202 Integer o = freqMap.get(obj);
203 if (o != null) // minimize NullPointerExceptions
204 {
205 return o;
206 }
207 } catch (NullPointerException | NoSuchElementException ignore) {
208 }
209 return 0;
210 }
211 }