View Javadoc

1   /*
2    *  Copyright 2001-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  package net.sf.collections15;
17  
18  import java.lang.reflect.Array;
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.Enumeration;
22  import java.util.HashMap;
23  import java.util.HashSet;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.ListIterator;
27  import java.util.Map;
28  import java.util.Set;
29  
30  import net.sf.collections15.collection.PredicatedCollection;
31  import net.sf.collections15.collection.SynchronizedCollection;
32  import net.sf.collections15.collection.TransformedCollection;
33  import net.sf.collections15.collection.UnmodifiableBoundedCollection;
34  import net.sf.collections15.collection.UnmodifiableCollection;
35  
36  
37  /***
38   * Provides utility methods and decorators for {@link Collection} instances.
39   *
40   * @since Commons Collections 1.0
41   * @version $Revision: 1.1 $ $Date: 2005/05/25 21:25:42 $
42   * 
43   * @author Rodney Waldhoff
44   * @author Paul Jack
45   * @author Stephen Colebourne
46   * @author Steve Downey
47   * @author Herve Quiroz
48   * @author Peter KoBek
49   * @author Matthew Hawthorne
50   * @author Janek Bogucki
51   * @author Phil Steitz
52   * @author Steven Melzer
53   * @author Jon Schewe
54   */
55  public class CollectionUtils {
56  
57  	/*** Constant to avoid repeated object creation */
58      private static Integer INTEGER_ONE = new Integer(1);
59  
60      /***
61       * An empty unmodifiable collection.
62       * The JDK provides empty Set and List implementations which could be used for
63       * this purpose. However they could be cast to Set or List which might be
64       * undesirable. This implementation only implements Collection.
65       */
66      public static final Collection EMPTY_COLLECTION = UnmodifiableCollection.decorate(new ArrayList());
67  
68      /***
69       * <code>CollectionUtils</code> should not normally be instantiated.
70       */
71      public CollectionUtils() {
72      }
73  
74      /***
75       * Returns a {@link Collection} containing the union
76       * of the given {@link Collection}s.
77       * <p>
78       * The cardinality of each element in the returned {@link Collection}
79       * will be equal to the maximum of the cardinality of that element
80       * in the two given {@link Collection}s.
81       *
82       * @param a  the first collection, must not be null
83       * @param b  the second collection, must not be null
84       * @return  the union of the two collections
85       * @see Collection#addAll
86       */
87      public static Collection union(final Collection a, final Collection b) {
88          ArrayList list = new ArrayList();
89          Map mapa = getCardinalityMap(a);
90          Map mapb = getCardinalityMap(b);
91          Set elts = new HashSet(a);
92          elts.addAll(b);
93          Iterator it = elts.iterator();
94          while(it.hasNext()) {
95              Object obj = it.next();
96              for(int i=0,m=Math.max(getFreq(obj,mapa),getFreq(obj,mapb));i<m;i++) {
97                  list.add(obj);
98              }
99          }
100         return list;
101     }
102 
103     /***
104      * Returns a {@link Collection} containing the intersection
105      * of the given {@link Collection}s.
106      * <p>
107      * The cardinality of each element in the returned {@link Collection}
108      * will be equal to the minimum of the cardinality of that element
109      * in the two given {@link Collection}s.
110      *
111      * @param a  the first collection, must not be null
112      * @param b  the second collection, must not be null
113      * @return the intersection of the two collections
114      * @see Collection#retainAll
115      * @see #containsAny
116      */
117     public static <T> Collection<T> intersection(final Collection<T> a, final Collection<T> b) {
118         ArrayList list = new ArrayList();
119         Map mapa = getCardinalityMap(a);
120         Map mapb = getCardinalityMap(b);
121         Set elts = new HashSet(a);
122         elts.addAll(b);
123         Iterator it = elts.iterator();
124         while(it.hasNext()) {
125             Object obj = it.next();
126             for(int i=0,m=Math.min(getFreq(obj,mapa),getFreq(obj,mapb));i<m;i++) {
127                 list.add(obj);
128             }
129         }
130         return list;
131     }
132 
133     /***
134      * Returns a {@link Collection} containing the exclusive disjunction
135      * (symmetric difference) of the given {@link Collection}s.
136      * <p>
137      * The cardinality of each element <i>e</i> in the returned {@link Collection}
138      * will be equal to
139      * <tt>max(cardinality(<i>e</i>,<i>a</i>),cardinality(<i>e</i>,<i>b</i>)) - min(cardinality(<i>e</i>,<i>a</i>),cardinality(<i>e</i>,<i>b</i>))</tt>.
140      * <p>
141      * This is equivalent to
142      * <tt>{@link #subtract subtract}({@link #union union(a,b)},{@link #intersection intersection(a,b)})</tt>
143      * or
144      * <tt>{@link #union union}({@link #subtract subtract(a,b)},{@link #subtract subtract(b,a)})</tt>.
145      *
146      * @param a  the first collection, must not be null
147      * @param b  the second collection, must not be null
148      * @return the symmetric difference of the two collections
149      */
150     public static Collection disjunction(final Collection a, final Collection b) {
151         ArrayList list = new ArrayList();
152         Map mapa = getCardinalityMap(a);
153         Map mapb = getCardinalityMap(b);
154         Set elts = new HashSet(a);
155         elts.addAll(b);
156         Iterator it = elts.iterator();
157         while(it.hasNext()) {
158             Object obj = it.next();
159             for(int i=0,m=((Math.max(getFreq(obj,mapa),getFreq(obj,mapb)))-(Math.min(getFreq(obj,mapa),getFreq(obj,mapb))));i<m;i++) {
160                 list.add(obj);
161             }
162         }
163         return list;
164     }
165 
166     /***
167      * Returns a new {@link Collection} containing <tt><i>a</i> - <i>b</i></tt>.
168      * The cardinality of each element <i>e</i> in the returned {@link Collection}
169      * will be the cardinality of <i>e</i> in <i>a</i> minus the cardinality
170      * of <i>e</i> in <i>b</i>, or zero, whichever is greater.
171      *
172      * @param a  the collection to subtract from, must not be null
173      * @param b  the collection to subtract, must not be null
174      * @return a new collection with the results
175      * @see Collection#removeAll
176      */
177     public static Collection subtract(final Collection a, final Collection b) {
178         ArrayList list = new ArrayList( a );
179         for (Iterator it = b.iterator(); it.hasNext();) {
180             list.remove(it.next());
181         }
182         return list;
183     }
184 
185     /***
186      * Returns <code>true</code> iff at least one element is in both collections.
187      * <p>
188      * In other words, this method returns <code>true</code> iff the
189      * {@link #intersection} of <i>coll1</i> and <i>coll2</i> is not empty.
190      * 
191      * @param coll1  the first collection, must not be null
192      * @param coll2  the first collection, must not be null
193      * @return <code>true</code> iff the intersection of the collections is non-empty
194      * @since 2.1
195      * @see #intersection
196      */
197     public static boolean containsAny(final Collection coll1, final Collection coll2) {
198         if (coll1.size() < coll2.size()) {
199             for (Iterator it = coll1.iterator(); it.hasNext();) {
200                 if (coll2.contains(it.next())) {
201                     return true;
202                 }
203             }
204         } else {
205             for (Iterator it = coll2.iterator(); it.hasNext();) {
206                 if (coll1.contains(it.next())) {
207                     return true;
208                 }
209             }
210         }
211         return false;
212     }
213 
214     /***
215      * Returns a {@link Map} mapping each unique element in the given
216      * {@link Collection} to an {@link Integer} representing the number
217      * of occurrences of that element in the {@link Collection}.
218      * <p>
219      * Only those elements present in the collection will appear as
220      * keys in the map.
221      * 
222      * @param coll  the collection to get the cardinality map for, must not be null
223      * @return the populated cardinality map
224      */
225     public static Map getCardinalityMap(final Collection coll) {
226         Map count = new HashMap();
227         for (Iterator it = coll.iterator(); it.hasNext();) {
228             Object obj = it.next();
229             Integer c = (Integer) (count.get(obj));
230             if (c == null) {
231                 count.put(obj,INTEGER_ONE);
232             } else {
233                 count.put(obj,new Integer(c.intValue() + 1));
234             }
235         }
236         return count;
237     }
238 
239     /***
240      * Returns <tt>true</tt> iff <i>a</i> is a sub-collection of <i>b</i>,
241      * that is, iff the cardinality of <i>e</i> in <i>a</i> is less
242      * than or equal to the cardinality of <i>e</i> in <i>b</i>,
243      * for each element <i>e</i> in <i>a</i>.
244      *
245      * @param a  the first (sub?) collection, must not be null
246      * @param b  the second (super?) collection, must not be null
247      * @return <code>true</code> iff <i>a</i> is a sub-collection of <i>b</i>
248      * @see #isProperSubCollection
249      * @see Collection#containsAll
250      */
251     public static boolean isSubCollection(final Collection a, final Collection b) {
252         Map mapa = getCardinalityMap(a);
253         Map mapb = getCardinalityMap(b);
254         Iterator it = a.iterator();
255         while (it.hasNext()) {
256             Object obj = it.next();
257             if (getFreq(obj, mapa) > getFreq(obj, mapb)) {
258                 return false;
259             }
260         }
261         return true;
262     }
263 
264     /***
265      * Returns <tt>true</tt> iff <i>a</i> is a <i>proper</i> sub-collection of <i>b</i>,
266      * that is, iff the cardinality of <i>e</i> in <i>a</i> is less
267      * than or equal to the cardinality of <i>e</i> in <i>b</i>,
268      * for each element <i>e</i> in <i>a</i>, and there is at least one
269      * element <i>f</i> such that the cardinality of <i>f</i> in <i>b</i>
270      * is strictly greater than the cardinality of <i>f</i> in <i>a</i>.
271      * <p>
272      * The implementation assumes
273      * <ul>
274      *    <li><code>a.size()</code> and <code>b.size()</code> represent the 
275      *    total cardinality of <i>a</i> and <i>b</i>, resp. </li>
276      *    <li><code>a.size() < Integer.MAXVALUE</code></li>
277      * </ul>
278      *
279      * @param a  the first (sub?) collection, must not be null
280      * @param b  the second (super?) collection, must not be null
281      * @return <code>true</code> iff <i>a</i> is a <i>proper</i> sub-collection of <i>b</i>
282      * @see #isSubCollection
283      * @see Collection#containsAll
284      */
285     public static boolean isProperSubCollection(final Collection a, final Collection b) {
286         return (a.size() < b.size()) && CollectionUtils.isSubCollection(a,b);
287     }
288 
289     /***
290      * Returns <tt>true</tt> iff the given {@link Collection}s contain
291      * exactly the same elements with exactly the same cardinalities.
292      * <p>
293      * That is, iff the cardinality of <i>e</i> in <i>a</i> is
294      * equal to the cardinality of <i>e</i> in <i>b</i>,
295      * for each element <i>e</i> in <i>a</i> or <i>b</i>.
296      *
297      * @param a  the first collection, must not be null
298      * @param b  the second collection, must not be null
299      * @return <code>true</code> iff the collections contain the same elements with the same cardinalities.
300      */
301     public static boolean isEqualCollection(final Collection a, final Collection b) {
302         if(a.size() != b.size()) {
303             return false;
304         } else {
305             Map mapa = getCardinalityMap(a);
306             Map mapb = getCardinalityMap(b);
307             if(mapa.size() != mapb.size()) {
308                 return false;
309             } else {
310                 Iterator it = mapa.keySet().iterator();
311                 while(it.hasNext()) {
312                     Object obj = it.next();
313                     if(getFreq(obj,mapa) != getFreq(obj,mapb)) {
314                         return false;
315                     }
316                 }
317                 return true;
318             }
319         }
320     }
321 
322     /***
323      * Returns the number of occurrences of <i>obj</i> in <i>coll</i>.
324      *
325      * @param obj  the object to find the cardinality of
326      * @param coll  the collection to search
327      * @return the the number of occurrences of obj in coll
328      */
329     public static int cardinality(Object obj, final Collection coll) {
330         if (coll instanceof Set) {
331             return (coll.contains(obj) ? 1 : 0);
332         }
333         if (coll instanceof Bag) {
334             return ((Bag) coll).getCount(obj);
335         }
336         int count = 0;
337         if (obj == null) {
338             for (Iterator it = coll.iterator();it.hasNext();) {
339                 if (it.next() == null) {
340                     count++;
341                 }
342             }
343         } else {
344             for (Iterator it = coll.iterator();it.hasNext();) {
345                 if (obj.equals(it.next())) {
346                     count++;
347                 }
348             }
349         }
350         return count;
351     }
352 
353     /*** 
354      * Finds the first element in the given collection which matches the given predicate.
355      * <p>
356      * If the input collection or predicate is null, or no element of the collection 
357      * matches the predicate, null is returned.
358      *
359      * @param collection  the collection to search, may be null
360      * @param predicate  the predicate to use, may be null
361      * @return the first element of the collection which matches the predicate or null if none could be found
362      */
363     public static Object find(Collection collection, Predicate predicate) {
364         if (collection != null && predicate != null) {
365             for (Iterator iter = collection.iterator(); iter.hasNext();) {
366                 Object item = iter.next();
367                 if (predicate.evaluate(item)) {
368                     return item;
369                 }
370             }
371         }
372         return null;
373     }
374     
375     /*** 
376      * Executes the given closure on each element in the collection.
377      * <p>
378      * If the input collection or closure is null, there is no change made.
379      * 
380      * @param collection  the collection to get the input from, may be null
381      * @param closure  the closure to perform, may be null
382      */
383     public static void forAllDo(Collection collection, Closure closure) {
384         if (collection != null && closure != null) {
385             for (Iterator it = collection.iterator(); it.hasNext();) {
386                 closure.execute(it.next());
387             }
388         }
389     }
390 
391     /*** 
392      * Filter the collection by applying a Predicate to each element. If the
393      * predicate returns false, remove the element.
394      * <p>
395      * If the input collection or predicate is null, there is no change made.
396      * 
397      * @param collection  the collection to get the input from, may be null
398      * @param predicate  the predicate to use as a filter, may be null
399      */
400     public static void filter(Collection collection, Predicate predicate) {
401         if (collection != null && predicate != null) {
402             for (Iterator it = collection.iterator(); it.hasNext();) {
403                 if (predicate.evaluate(it.next()) == false) {
404                     it.remove();
405                 }
406             }
407         }
408     }
409 
410     /*** 
411      * Transform the collection by applying a Transformer to each element.
412      * <p>
413      * If the input collection or transformer is null, there is no change made.
414      * <p>
415      * This routine is best for Lists, for which set() is used to do the 
416      * transformations "in place."  For other Collections, clear() and addAll()
417      * are used to replace elements.  
418      * <p>
419      * If the input collection controls its input, such as a Set, and the
420      * Transformer creates duplicates (or are otherwise invalid), the 
421      * collection may reduce in size due to calling this method.
422      * 
423      * @param collection  the collection to get the input from, may be null
424      * @param transformer  the transformer to perform, may be null
425      */
426     public static void transform(Collection collection, Transformer transformer) {
427         if (collection != null && transformer != null) {
428             if (collection instanceof List) {
429                 List list = (List) collection;
430                 for (ListIterator it = list.listIterator(); it.hasNext();) {
431                     it.set(transformer.transform(it.next()));
432                 }
433             } else {
434                 Collection resultCollection = collect(collection, transformer);
435                 collection.clear();
436                 collection.addAll(resultCollection);
437             }
438         }
439     }
440 
441     /*** 
442      * Counts the number of elements in the input collection that match the predicate.
443      * <p>
444      * A <code>null</code> collection or predicate matches no elements.
445      * 
446      * @param inputCollection  the collection to get the input from, may be null
447      * @param predicate  the predicate to use, may be null
448      * @return the number of matches for the predicate in the collection
449      */
450     public static int countMatches(Collection inputCollection, Predicate predicate) {
451         int count = 0;
452         if (inputCollection != null && predicate != null) {
453             for (Iterator it = inputCollection.iterator(); it.hasNext();) {
454                 if (predicate.evaluate(it.next())) {
455                     count++;
456                 }
457             }
458         }
459         return count;
460     }
461 
462     /*** 
463      * Answers true if a predicate is true for at least one element of a collection.
464      * <p>
465      * A <code>null</code> collection or predicate returns false.
466      * 
467      * @param collection the collection to get the input from, may be null
468      * @param predicate the predicate to use, may be null
469      * @return true if at least one element of the collection matches the predicate
470      */
471     public static boolean exists(Collection collection, Predicate predicate) {
472         if (collection != null && predicate != null) {
473             for (Iterator it = collection.iterator(); it.hasNext();) {
474                 if (predicate.evaluate(it.next())) {
475                     return true;
476                 }
477             }
478         }
479         return false;
480     }
481 
482     /*** 
483      * Selects all elements from input collection which match the given predicate
484      * into an output collection.
485      * <p>
486      * A <code>null</code> predicate matches no elements.
487      * 
488      * @param inputCollection  the collection to get the input from, may not be null
489      * @param predicate  the predicate to use, may be null
490      * @return the elements matching the predicate (new list)
491      * @throws NullPointerException if the input collection is null
492      */
493     public static Collection select(Collection inputCollection, Predicate predicate) {
494         ArrayList answer = new ArrayList(inputCollection.size());
495         select(inputCollection, predicate, answer);
496         return answer;
497     }
498 
499     /*** 
500      * Selects all elements from input collection which match the given predicate
501      * and adds them to outputCollection.
502      * <p>
503      * If the input collection or predicate is null, there is no change to the 
504      * output collection.
505      * 
506      * @param inputCollection  the collection to get the input from, may be null
507      * @param predicate  the predicate to use, may be null
508      * @param outputCollection  the collection to output into, may not be null
509      */
510     public static void select(Collection inputCollection, Predicate predicate, Collection outputCollection) {
511         if (inputCollection != null && predicate != null) {
512             for (Iterator iter = inputCollection.iterator(); iter.hasNext();) {
513                 Object item = iter.next();
514                 if (predicate.evaluate(item)) {
515                     outputCollection.add(item);
516                 }
517             }
518         }
519     }
520     
521     /***
522      * Selects all elements from inputCollection which don't match the given predicate
523      * into an output collection.
524      * <p>
525      * If the input predicate is <code>null</code>, the result is an empty list.
526      * 
527      * @param inputCollection  the collection to get the input from, may not be null
528      * @param predicate  the predicate to use, may be null
529      * @return the elements <b>not</b> matching the predicate (new list)
530      * @throws NullPointerException if the input collection is null
531      */
532     public static Collection selectRejected(Collection inputCollection, Predicate predicate) {
533         ArrayList answer = new ArrayList(inputCollection.size());
534         selectRejected(inputCollection, predicate, answer);
535         return answer;
536     }
537     
538     /*** 
539      * Selects all elements from inputCollection which don't match the given predicate
540      * and adds them to outputCollection.
541      * <p>
542      * If the input predicate is <code>null</code>, no elements are added to <code>outputCollection</code>.
543      * 
544      * @param inputCollection  the collection to get the input from, may be null
545      * @param predicate  the predicate to use, may be null
546      * @param outputCollection  the collection to output into, may not be null
547      */
548     public static void selectRejected(Collection inputCollection, Predicate predicate, Collection outputCollection) {
549         if (inputCollection != null && predicate != null) {
550             for (Iterator iter = inputCollection.iterator(); iter.hasNext();) {
551                 Object item = iter.next();
552                 if (predicate.evaluate(item) == false) {
553                     outputCollection.add(item);
554                 }
555             }
556         }
557     }
558     
559     /*** 
560      * Returns a new Collection consisting of the elements of inputCollection transformed
561      * by the given transformer.
562      * <p>
563      * If the input transformer is null, the result is an empty list.
564      * 
565      * @param inputCollection  the collection to get the input from, may not be null
566      * @param transformer  the transformer to use, may be null
567      * @return the transformed result (new list)
568      * @throws NullPointerException if the input collection is null
569      */
570     public static Collection collect(Collection inputCollection, Transformer transformer) {
571         ArrayList answer = new ArrayList(inputCollection.size());
572         collect(inputCollection, transformer, answer);
573         return answer;
574     }
575     
576     /*** 
577      * Transforms all elements from the inputIterator with the given transformer 
578      * and adds them to the outputCollection.
579      * <p>
580      * If the input iterator or transformer is null, the result is an empty list.
581      * 
582      * @param inputIterator  the iterator to get the input from, may be null
583      * @param transformer  the transformer to use, may be null
584      * @return the transformed result (new list)
585      */
586     public static Collection collect(Iterator inputIterator, Transformer transformer) {
587         ArrayList answer = new ArrayList();
588         collect(inputIterator, transformer, answer);
589         return answer;
590     }
591     
592     /*** 
593      * Transforms all elements from inputCollection with the given transformer 
594      * and adds them to the outputCollection.
595      * <p>
596      * If the input collection or transformer is null, there is no change to the 
597      * output collection.
598      *
599      * @param inputCollection  the collection to get the input from, may be null
600      * @param transformer  the transformer to use, may be null
601      * @param outputCollection  the collection to output into, may not be null
602      * @return the outputCollection with the transformed input added
603      * @throws NullPointerException if the output collection is null
604      */
605     public static Collection collect(Collection inputCollection, final Transformer transformer, final Collection outputCollection) {
606         if (inputCollection != null) {
607             return collect(inputCollection.iterator(), transformer, outputCollection);
608         }
609         return outputCollection;
610     }
611 
612     /*** 
613      * Transforms all elements from the inputIterator with the given transformer 
614      * and adds them to the outputCollection.
615      * <p>
616      * If the input iterator or transformer is null, there is no change to the 
617      * output collection.
618      *
619      * @param inputIterator  the iterator to get the input from, may be null
620      * @param transformer  the transformer to use, may be null
621      * @param outputCollection  the collection to output into, may not be null
622      * @return the outputCollection with the transformed input added
623      * @throws NullPointerException if the output collection is null
624      */
625     public static Collection collect(Iterator inputIterator, final Transformer transformer, final Collection outputCollection) {
626         if (inputIterator != null && transformer != null) {
627             while (inputIterator.hasNext()) {
628                 Object item = inputIterator.next();
629                 Object value = transformer.transform(item);
630                 outputCollection.add(value);
631             }
632         }
633         return outputCollection;
634     }
635 
636     /***
637      * Adds all elements in the iteration to the given collection.
638      * 
639      * @param collection  the collection to add to
640      * @param iterator  the iterator of elements to add, may not be null
641      * @throws NullPointerException if the collection or iterator is null
642      */
643     public static void addAll(Collection collection, Iterator iterator) {
644         while (iterator.hasNext()) {
645             collection.add(iterator.next());
646         }
647     }
648     
649     /***
650      * Adds all elements in the enumeration to the given collection.
651      * 
652      * @param collection  the collection to add to
653      * @param enumeration  the enumeration of elements to add, may not be null
654      * @throws NullPointerException if the collection or enumeration is null
655      */
656     public static void addAll(Collection collection, Enumeration enumeration) {
657         while (enumeration.hasMoreElements()) {
658             collection.add(enumeration.nextElement());
659         }
660     }    
661     
662     /*** 
663      * Adds all elements in the array to the given collection.
664      * 
665      * @param collection  the collection to add to, may not be null
666      * @param elements  the array of elements to add, may not be null
667      * @throws NullPointerException if the collection or array is null
668      */
669     public static void addAll(Collection collection, Object[] elements) {
670         for (int i = 0, size = elements.length; i < size; i++) {
671             collection.add(elements[i]);
672         }
673     }    
674     
675     /***
676      * Given an Object, and an index, returns the nth value in the
677      * object.
678      * <ul>
679      * <li>If obj is a Map, returns the nth value from the <b>keySet</b> iterator, unless 
680      *     the Map contains an Integer key with integer value = idx, in which case the
681      *     corresponding map entry value is returned.  If idx exceeds the number of entries in
682      *     the map, an empty Iterator is returned.
683      * <li>If obj is a List or an array, returns the nth value, throwing IndexOutOfBoundsException,
684      *     ArrayIndexOutOfBoundsException, resp. if the nth value does not exist.
685      * <li>If obj is an iterator, enumeration or Collection, returns the nth value from the iterator,
686      *     returning an empty Iterator (resp. Enumeration) if the nth value does not exist.
687      * <li>Returns the original obj if it is null or not a Collection or Iterator.
688      * </ul>
689      * 
690      * @param obj  the object to get an index of, may be null
691      * @param idx  the index to get
692      * @throws IndexOutOfBoundsException
693      * @throws ArrayIndexOutOfBoundsException
694      *
695      * @deprecated use {@link #get(Object, int)} instead. Will be removed in v4.0
696      */
697     public static Object index(Object obj, int idx) {
698         return index(obj, new Integer(idx));
699     }
700     
701     /***
702      * Given an Object, and a key (index), returns the value associated with
703      * that key in the Object. The following checks are made:
704      * <ul>
705      * <li>If obj is a Map, use the index as a key to get a value. If no match continue.
706      * <li>Check key is an Integer. If not, return the object passed in.
707      * <li>If obj is a Map, get the nth value from the <b>keySet</b> iterator.
708      *     If the Map has fewer than n entries, return an empty Iterator.
709      * <li>If obj is a List or an array, get the nth value, throwing IndexOutOfBoundsException,
710      *     ArrayIndexOutOfBoundsException, resp. if the nth value does not exist.
711      * <li>If obj is an iterator, enumeration or Collection, get the nth value from the iterator,
712      *     returning an empty Iterator (resp. Enumeration) if the nth value does not exist.
713      * <li>Return the original obj.
714      * </ul>
715      * 
716      * @param obj  the object to get an index of
717      * @param index  the index to get
718      * @return the object at the specified index
719      * @throws IndexOutOfBoundsException
720      * @throws ArrayIndexOutOfBoundsException
721      *
722      * @deprecated use {@link #get(Object, int)} instead. Will be removed in v4.0
723      */
724     public static Object index(Object obj, Object index) {
725         if(obj instanceof Map) {
726             Map map = (Map)obj;
727             if(map.containsKey(index)) {
728                 return map.get(index);
729             }
730         }
731         int idx = -1;
732         if(index instanceof Integer) {
733             idx = ((Integer)index).intValue();
734         }
735         if(idx < 0) {
736             return obj;
737         } 
738         else if(obj instanceof Map) {
739             Map map = (Map)obj;
740             Iterator iterator = map.keySet().iterator();
741             return index(iterator, idx);
742         } 
743         else if(obj instanceof List) {
744             return ((List)obj).get(idx);
745         } 
746         else if(obj instanceof Object[]) {
747             return ((Object[])obj)[idx];
748         } 
749         else if(obj instanceof Enumeration) {
750             Enumeration it = (Enumeration)obj;
751             while(it.hasMoreElements()) {
752                 idx--;
753                 if(idx == -1) {
754                     return it.nextElement();
755                 } else {
756                     it.nextElement();
757                 }
758             }
759         } 
760         else if(obj instanceof Iterator) {
761             return index((Iterator)obj, idx);
762         }
763         else if(obj instanceof Collection) {
764             Iterator iterator = ((Collection)obj).iterator();
765             return index(iterator, idx);
766         }
767         return obj;
768     }
769 
770     private static Object index(Iterator iterator, int idx) {
771         while(iterator.hasNext()) {
772             idx--;
773             if(idx == -1) {
774                 return iterator.next();
775             } else {
776                 iterator.next();
777             }
778         }
779         return iterator;
780     }
781     
782     /***
783      * Returns the <code>index</code>-th value in <code>object</code>, throwing
784      * <code>IndexOutOfBoundsException</code> if there is no such element or 
785      * <code>IllegalArgumentException</code> if <code>object</code> is not an 
786      * instance of one of the supported types.
787      * <p>
788      * The supported types, and associated semantics are:
789      * <ul>
790      * <li> Map -- the value returned is the <code>Map.Entry</code> in position 
791      *      <code>index</code> in the map's <code>entrySet</code> iterator, 
792      *      if there is such an entry.</li>
793      * <li> List -- this method is equivalent to the list's get method.</li>
794      * <li> Array -- the <code>index</code>-th array entry is returned, 
795      *      if there is such an entry; otherwise an <code>IndexOutOfBoundsException</code>
796      *      is thrown.</li>
797      * <li> Collection -- the value returned is the <code>index</code>-th object 
798      *      returned by the collection's default iterator, if there is such an element.</li>
799      * <li> Iterator or Enumeration -- the value returned is the
800      *      <code>index</code>-th object in the Iterator/Enumeration, if there
801      *      is such an element.  The Iterator/Enumeration is advanced to 
802      *      <code>index</code> (or to the end, if <code>index</code> exceeds the 
803      *      number of entries) as a side effect of this method.</li>
804      * </ul>
805      * 
806      * @param object  the object to get a value from
807      * @param index  the index to get
808      * @return the object at the specified index
809      * @throws IndexOutOfBoundsException if the index is invalid
810      * @throws IllegalArgumentException if the object type is invalid
811      */
812     public static Object get(Object object, int index) {
813         if (index < 0) {
814             throw new IndexOutOfBoundsException("Index cannot be negative: " + index);
815         }
816         if (object instanceof Map) {
817             Map map = (Map) object;
818             Iterator iterator = map.entrySet().iterator();
819             return get(iterator, index);
820         } else if (object instanceof List) {
821             return ((List) object).get(index);
822         } else if (object instanceof Object[]) {
823             return ((Object[]) object)[index];
824         } else if (object instanceof Iterator) {
825             Iterator it = (Iterator) object;
826             while (it.hasNext()) {
827                 index--;
828                 if (index == -1) {
829                     return it.next();
830                 } else {
831                     it.next();
832                 }
833             }
834             throw new IndexOutOfBoundsException("Entry does not exist: " + index);
835         } else if (object instanceof Collection) {
836             Iterator iterator = ((Collection) object).iterator();
837             return get(iterator, index);
838         } else if (object instanceof Enumeration) {
839             Enumeration it = (Enumeration) object;
840             while (it.hasMoreElements()) {
841                 index--;
842                 if (index == -1) {
843                     return it.nextElement();
844                 } else {
845                     it.nextElement();
846                 }
847             }
848             throw new IndexOutOfBoundsException("Entry does not exist: " + index);
849         } else if (object == null) {
850             throw new IllegalArgumentException("Unsupported object type: null");
851         } else {
852             try {
853                 return Array.get(object, index);
854             } catch (IllegalArgumentException ex) {
855                 throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName());
856             }
857         }
858     }
859     
860     /*** 
861      * Gets the size of the collection/iterator specified.
862      * <p>
863      * This method can handles objects as follows
864      * <ul>
865      * <li>Collection - the collection size
866      * <li>Map - the map size
867      * <li>Array - the array size
868      * <li>Iterator - the number of elements remaining in the iterator
869      * <li>Enumeration - the number of elements remaining in the enumeration
870      * </ul>
871      * 
872      * @param object  the object to get the size of
873      * @return the size of the specified collection
874      * @throws IllegalArgumentException thrown if object is not recognised or null
875      * @since Commons Collections 3.1
876      */
877     public static int size(Object object) {
878         int total = 0;
879         if (object instanceof Map) {
880             total = ((Map) object).size();
881         } else if (object instanceof Collection) {
882             total = ((Collection) object).size();
883         } else if (object instanceof Object[]) {
884             total = ((Object[]) object).length;
885         } else if (object instanceof Iterator) {
886             Iterator it = (Iterator) object;
887             while (it.hasNext()) {
888                 total++;
889                 it.next();
890             }
891         } else if (object instanceof Enumeration) {
892             Enumeration it = (Enumeration) object;
893             while (it.hasMoreElements()) {
894                 total++;
895                 it.nextElement();
896             }
897         } else if (object == null) {
898             throw new IllegalArgumentException("Unsupported object type: null");
899         } else {
900             try {
901                 total = Array.getLength(object);
902             } catch (IllegalArgumentException ex) {
903                 throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName());
904             }
905         }
906         return total;
907     }
908     
909     /***
910      * Reverses the order of the given array.
911      * 
912      * @param array  the array to reverse
913      */
914     public static void reverseArray(Object[] array) {
915         int i = 0;
916         int j = array.length - 1;
917         Object tmp;
918 
919         while (j > i) {
920             tmp = array[j];
921             array[j] = array[i];
922             array[i] = tmp;
923             j--;
924             i++;
925         }
926     }
927 
928     private static final int getFreq(final Object obj, final Map freqMap) {
929         Integer count = (Integer) freqMap.get(obj);
930         if (count != null) {
931             return count.intValue();
932         }
933         return 0;
934     }
935 
936     /***
937      * Returns true if no more elements can be added to the Collection.
938      * <p>
939      * This method uses the {@link BoundedCollection} interface to determine the
940      * full status. If the collection does not implement this interface then
941      * false is returned.
942      * <p>
943      * The collection does not have to implement this interface directly.
944      * If the collection has been decorated using the decorators subpackage
945      * then these will be removed to access the BoundedCollection.
946      *
947      * @param coll  the collection to check
948      * @return true if the BoundedCollection is full
949      * @throws NullPointerException if the collection is null
950      */
951     public static boolean isFull(Collection coll) {
952         if (coll == null) {
953             throw new NullPointerException("The collection must not be null");
954         }
955         if (coll instanceof BoundedCollection) {
956             return ((BoundedCollection) coll).isFull();
957         }
958         try {
959             BoundedCollection bcoll = UnmodifiableBoundedCollection.decorateUsing(coll);
960             return bcoll.isFull();
961             
962         } catch (IllegalArgumentException ex) {
963             return false;
964         }
965     }
966 
967     /***
968      * Get the maximum number of elements that the Collection can contain.
969      * <p>
970      * This method uses the {@link BoundedCollection} interface to determine the
971      * maximum size. If the collection does not implement this interface then
972      * -1 is returned.
973      * <p>
974      * The collection does not have to implement this interface directly.
975      * If the collection has been decorated using the decorators subpackage
976      * then these will be removed to access the BoundedCollection.
977      *
978      * @param coll  the collection to check
979      * @return the maximum size of the BoundedCollection, -1 if no maximum size
980      * @throws NullPointerException if the collection is null
981      */
982     public static int maxSize(Collection coll) {
983         if (coll == null) {
984             throw new NullPointerException("The collection must not be null");
985         }
986         if (coll instanceof BoundedCollection) {
987             return ((BoundedCollection) coll).maxSize();
988         }
989         try {
990             BoundedCollection bcoll = UnmodifiableBoundedCollection.decorateUsing(coll);
991             return bcoll.maxSize();
992             
993         } catch (IllegalArgumentException ex) {
994             return -1;
995         }
996     }
997 
998     //-----------------------------------------------------------------------
999     /***
1000      * Returns a synchronized collection backed by the given collection.
1001      * <p>
1002      * You must manually synchronize on the returned buffer's iterator to 
1003      * avoid non-deterministic behavior:
1004      *  
1005      * <pre>
1006      * Collection c = CollectionUtils.synchronizedCollection(myCollection);
1007      * synchronized (c) {
1008      *     Iterator i = c.iterator();
1009      *     while (i.hasNext()) {
1010      *         process (i.next());
1011      *     }
1012      * }
1013      * </pre>
1014      * 
1015      * This method uses the implementation in the decorators subpackage.
1016      * 
1017      * @param collection  the collection to synchronize, must not be null
1018      * @return a synchronized collection backed by the given collection
1019      * @throws IllegalArgumentException  if the collection is null
1020      */
1021     public static Collection synchronizedCollection(Collection collection) {
1022         return SynchronizedCollection.decorate(collection);
1023     }
1024 
1025     /***
1026      * Returns an unmodifiable collection backed by the given collection.
1027      * <p>
1028      * This method uses the implementation in the decorators subpackage.
1029      *
1030      * @param collection  the collection to make unmodifiable, must not be null
1031      * @return an unmodifiable collection backed by the given collection
1032      * @throws IllegalArgumentException  if the collection is null
1033      */
1034     public static Collection unmodifiableCollection(Collection collection) {
1035         return UnmodifiableCollection.decorate(collection);
1036     }
1037 
1038     /***
1039      * Returns a predicated (validating) collection backed by the given collection.
1040      * <p>
1041      * Only objects that pass the test in the given predicate can be added to the collection.
1042      * Trying to add an invalid object results in an IllegalArgumentException.
1043      * It is important not to use the original collection after invoking this method,
1044      * as it is a backdoor for adding invalid objects.
1045      *
1046      * @param collection  the collection to predicate, must not be null
1047      * @param predicate  the predicate for the collection, must not be null
1048      * @return a predicated collection backed by the given collection
1049      * @throws IllegalArgumentException  if the Collection is null
1050      */
1051     public static Collection predicatedCollection(Collection collection, Predicate predicate) {
1052         return PredicatedCollection.decorate(collection, predicate);
1053     }
1054 
1055     /***
1056      * Returns a typed collection backed by the given collection.
1057      * <p>
1058      * Only objects of the specified type can be added to the collection.
1059      * 
1060      * @param collection  the collection to limit to a specific type, must not be null
1061      * @param type  the type of objects which may be added to the collection
1062      * @return a typed collection backed by the specified collection
1063      */
1064     public static <T> Collection<T> typedCollection(Collection<T> collection, Class<? extends T> type) {
1065 //        return TypedCollection.decorate(collection, type);
1066 		return collection;
1067     }
1068     
1069     /***
1070      * Returns a transformed bag backed by the given collection.
1071      * <p>
1072      * Each object is passed through the transformer as it is added to the
1073      * Collection. It is important not to use the original collection after invoking this 
1074      * method, as it is a backdoor for adding untransformed objects.
1075      *
1076      * @param collection  the collection to predicate, must not be null
1077      * @param transformer  the transformer for the collection, must not be null
1078      * @return a transformed collection backed by the given collection
1079      * @throws IllegalArgumentException  if the Collection or Transformer is null
1080      */
1081     public static Collection transformedCollection(Collection collection, Transformer transformer) {
1082         return TransformedCollection.decorate(collection, transformer);
1083     }
1084     
1085 }