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 // ----------------------------------------------------------------------
36 // Static methods that can probably be moved to a real util class.
37 // ----------------------------------------------------------------------
38
39 /**
40 * Take a dominant and recessive Map and merge the key:value pairs where the recessive Map may add key:value pairs
41 * to the dominant Map but may not override any existing key:value pairs. If we have two Maps, a dominant and
42 * recessive, and their respective keys are as follows: dominantMapKeys = { a, b, c, d, e, f } recessiveMapKeys = {
43 * a, b, c, x, y, z } Then the result should be the following: resultantKeys = { a, b, c, d, e, f, x, y, z }
44 *
45 * @param dominantMap Dominant Map.
46 * @param recessiveMap Recessive Map.
47 * @param <K> type
48 * @param <V> type
49 * @return The result map with combined dominant and recessive values.
50 */
51 public static <K, V> Map<K, V> mergeMaps( Map<K, V> dominantMap, Map<K, V> recessiveMap )
52 {
53
54 if ( dominantMap == null && recessiveMap == null )
55 {
56 return null;
57 }
58
59 if ( dominantMap != null && recessiveMap == null )
60 {
61 return dominantMap;
62 }
63
64 if ( dominantMap == null )
65 {
66 return recessiveMap;
67 }
68
69 Map<K, V> result = new HashMap<>();
70
71 // Grab the keys from the dominant and recessive maps.
72 Set<K> dominantMapKeys = dominantMap.keySet();
73 Set<K> recessiveMapKeys = recessiveMap.keySet();
74
75 // Create the set of keys that will be contributed by the
76 // recessive Map by subtracting the intersection of keys
77 // from the recessive Map's keys.
78 Collection<K> contributingRecessiveKeys =
79 CollectionUtils.subtract( recessiveMapKeys,
80 CollectionUtils.intersection( dominantMapKeys, recessiveMapKeys ) );
81
82 result.putAll( dominantMap );
83
84 // Now take the keys we just found and extract the values from
85 // the recessiveMap and put the key:value pairs into the dominantMap.
86 for ( K key : contributingRecessiveKeys )
87 {
88 result.put( key, recessiveMap.get( key ) );
89 }
90
91 return result;
92 }
93
94 /**
95 * Take a series of <code>Map</code>s and merge them where the ordering of the array from 0..n is the dominant
96 * order.
97 *
98 * @param maps An array of Maps to merge.
99 * @param <K> type
100 * @param <V> type
101 * @return Map The result Map produced after the merging process.
102 */
103 public static <K, V> Map<K, V> mergeMaps( Map<K, V>[] maps )
104 {
105 Map<K, V> result;
106
107 if ( maps.length == 0 )
108 {
109 result = null;
110 }
111 else if ( maps.length == 1 )
112 {
113 result = maps[0];
114 }
115 else
116 {
117 result = mergeMaps( maps[0], maps[1] );
118
119 for ( int i = 2; i < maps.length; i++ )
120 {
121 result = mergeMaps( result, maps[i] );
122 }
123 }
124
125 return result;
126 }
127
128 /**
129 * <p>
130 * Returns a {@link Collection} containing the intersection of the given {@link Collection}s.
131 * </p>
132 * The cardinality of each element in the returned {@link Collection} will be equal to the minimum of the
133 * cardinality of that element in the two given {@link Collection}s.
134 *
135 * @param a The first collection
136 * @param b The second collection
137 * @param <E> the type
138 * @see Collection#retainAll
139 * @return The intersection of a and b, never null
140 */
141 public static <E> Collection<E> intersection( final Collection<E> a, final Collection<E> b )
142 {
143 ArrayList<E> list = new ArrayList<>();
144 Map<E, Integer> mapa = getCardinalityMap( a );
145 Map<E, Integer> mapb = getCardinalityMap( b );
146 Set<E> elts = new HashSet<>( a );
147 elts.addAll( b );
148 for ( E obj : elts )
149 {
150 for ( int i = 0, m = Math.min( getFreq( obj, mapa ), getFreq( obj, mapb ) ); i < m; i++ )
151 {
152 list.add( obj );
153 }
154 }
155 return list;
156 }
157
158 /**
159 * Returns a {@link Collection} containing <code>a - b</code>. The cardinality of each element <i>e</i> in
160 * the returned {@link Collection} will be the cardinality of <i>e</i> in <i>a</i> minus the cardinality of <i>e</i>
161 * in <i>b</i>, or zero, whichever is greater.
162 *
163 * @param a The start collection
164 * @param b The collection that will be subtracted
165 * @param <T> the type
166 * @see Collection#removeAll
167 * @return The result of the subtraction
168 */
169 public static <T> Collection<T> subtract( final Collection<T> a, final Collection<T> b )
170 {
171 ArrayList<T> list = new ArrayList<>( a );
172 for ( T aB : b )
173 {
174 list.remove( aB );
175 }
176 return list;
177 }
178
179 /**
180 * Returns a {@link Map} mapping each unique element in the given {@link Collection} to an {@link Integer}
181 * representing the number of occurrences of that element in the {@link Collection}. An entry that maps to
182 * <code>null</code> indicates that the element does not appear in the given {@link Collection}.
183 *
184 * @param col The collection to count cardinalities for
185 * @param <E> the type
186 * @return A map of counts, indexed on each element in the collection
187 */
188 public static <E> Map<E, Integer> getCardinalityMap( final Collection<E> col )
189 {
190 HashMap<E, Integer> count = new HashMap<>();
191 for ( E obj : col )
192 {
193 Integer c = count.get( obj );
194 if ( null == c )
195 {
196 count.put( obj, 1 );
197 }
198 else
199 {
200 count.put( obj, c + 1 );
201 }
202 }
203 return count;
204 }
205
206 public static <E> List<E> iteratorToList( Iterator<E> it )
207 {
208 if ( it == null )
209 {
210 throw new NullPointerException( "it cannot be null." );
211 }
212
213 List<E> list = new ArrayList<E>();
214
215 while ( it.hasNext() )
216 {
217 list.add( it.next() );
218 }
219
220 return list;
221 }
222
223 // ----------------------------------------------------------------------
224 //
225 // ----------------------------------------------------------------------
226
227 private static <E> int getFreq( final E obj, final Map<E, Integer> freqMap )
228 {
229 try
230 {
231 Integer o = freqMap.get( obj );
232 if ( o != null ) // minimize NullPointerExceptions
233 {
234 return o;
235 }
236 }
237 catch ( NullPointerException | NoSuchElementException ignore )
238 {
239 }
240 return 0;
241 }
242 }