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