0001: /*
0002: * Copyright 2001-2005 The Apache Software Foundation
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: package org.apache.commons.collections;
0017:
0018: import java.lang.reflect.Array;
0019: import java.util.ArrayList;
0020: import java.util.Collection;
0021: import java.util.Enumeration;
0022: import java.util.HashMap;
0023: import java.util.HashSet;
0024: import java.util.Iterator;
0025: import java.util.List;
0026: import java.util.ListIterator;
0027: import java.util.Map;
0028: import java.util.Set;
0029:
0030: import org.apache.commons.collections.collection.PredicatedCollection;
0031: import org.apache.commons.collections.collection.SynchronizedCollection;
0032: import org.apache.commons.collections.collection.TransformedCollection;
0033: import org.apache.commons.collections.collection.TypedCollection;
0034: import org.apache.commons.collections.collection.UnmodifiableBoundedCollection;
0035: import org.apache.commons.collections.collection.UnmodifiableCollection;
0036:
0037: /**
0038: * Provides utility methods and decorators for {@link Collection} instances.
0039: *
0040: * @since Commons Collections 1.0
0041: * @version $Revision: 348013 $ $Date: 2005-11-21 23:24:45 +0000 (Mon, 21 Nov 2005) $
0042: *
0043: * @author Rodney Waldhoff
0044: * @author Paul Jack
0045: * @author Stephen Colebourne
0046: * @author Steve Downey
0047: * @author Herve Quiroz
0048: * @author Peter KoBek
0049: * @author Matthew Hawthorne
0050: * @author Janek Bogucki
0051: * @author Phil Steitz
0052: * @author Steven Melzer
0053: * @author Jon Schewe
0054: * @author Neil O'Toole
0055: * @author Stephen Smith
0056: */
0057: public class CollectionUtils {
0058:
0059: /** Constant to avoid repeated object creation */
0060: private static Integer INTEGER_ONE = new Integer(1);
0061:
0062: /**
0063: * An empty unmodifiable collection.
0064: * The JDK provides empty Set and List implementations which could be used for
0065: * this purpose. However they could be cast to Set or List which might be
0066: * undesirable. This implementation only implements Collection.
0067: */
0068: public static final Collection EMPTY_COLLECTION = UnmodifiableCollection
0069: .decorate(new ArrayList());
0070:
0071: /**
0072: * <code>CollectionUtils</code> should not normally be instantiated.
0073: */
0074: public CollectionUtils() {
0075: }
0076:
0077: /**
0078: * Returns a {@link Collection} containing the union
0079: * of the given {@link Collection}s.
0080: * <p>
0081: * The cardinality of each element in the returned {@link Collection}
0082: * will be equal to the maximum of the cardinality of that element
0083: * in the two given {@link Collection}s.
0084: *
0085: * @param a the first collection, must not be null
0086: * @param b the second collection, must not be null
0087: * @return the union of the two collections
0088: * @see Collection#addAll
0089: */
0090: public static Collection union(final Collection a,
0091: final Collection b) {
0092: ArrayList list = new ArrayList();
0093: Map mapa = getCardinalityMap(a);
0094: Map mapb = getCardinalityMap(b);
0095: Set elts = new HashSet(a);
0096: elts.addAll(b);
0097: Iterator it = elts.iterator();
0098: while (it.hasNext()) {
0099: Object obj = it.next();
0100: for (int i = 0, m = Math.max(getFreq(obj, mapa), getFreq(
0101: obj, mapb)); i < m; i++) {
0102: list.add(obj);
0103: }
0104: }
0105: return list;
0106: }
0107:
0108: /**
0109: * Returns a {@link Collection} containing the intersection
0110: * of the given {@link Collection}s.
0111: * <p>
0112: * The cardinality of each element in the returned {@link Collection}
0113: * will be equal to the minimum of the cardinality of that element
0114: * in the two given {@link Collection}s.
0115: *
0116: * @param a the first collection, must not be null
0117: * @param b the second collection, must not be null
0118: * @return the intersection of the two collections
0119: * @see Collection#retainAll
0120: * @see #containsAny
0121: */
0122: public static Collection intersection(final Collection a,
0123: final Collection b) {
0124: ArrayList list = new ArrayList();
0125: Map mapa = getCardinalityMap(a);
0126: Map mapb = getCardinalityMap(b);
0127: Set elts = new HashSet(a);
0128: elts.addAll(b);
0129: Iterator it = elts.iterator();
0130: while (it.hasNext()) {
0131: Object obj = it.next();
0132: for (int i = 0, m = Math.min(getFreq(obj, mapa), getFreq(
0133: obj, mapb)); i < m; i++) {
0134: list.add(obj);
0135: }
0136: }
0137: return list;
0138: }
0139:
0140: /**
0141: * Returns a {@link Collection} containing the exclusive disjunction
0142: * (symmetric difference) of the given {@link Collection}s.
0143: * <p>
0144: * The cardinality of each element <i>e</i> in the returned {@link Collection}
0145: * will be equal to
0146: * <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>.
0147: * <p>
0148: * This is equivalent to
0149: * <tt>{@link #subtract subtract}({@link #union union(a,b)},{@link #intersection intersection(a,b)})</tt>
0150: * or
0151: * <tt>{@link #union union}({@link #subtract subtract(a,b)},{@link #subtract subtract(b,a)})</tt>.
0152: *
0153: * @param a the first collection, must not be null
0154: * @param b the second collection, must not be null
0155: * @return the symmetric difference of the two collections
0156: */
0157: public static Collection disjunction(final Collection a,
0158: final Collection b) {
0159: ArrayList list = new ArrayList();
0160: Map mapa = getCardinalityMap(a);
0161: Map mapb = getCardinalityMap(b);
0162: Set elts = new HashSet(a);
0163: elts.addAll(b);
0164: Iterator it = elts.iterator();
0165: while (it.hasNext()) {
0166: Object obj = it.next();
0167: for (int i = 0, m = ((Math.max(getFreq(obj, mapa), getFreq(
0168: obj, mapb))) - (Math.min(getFreq(obj, mapa),
0169: getFreq(obj, mapb)))); i < m; i++) {
0170: list.add(obj);
0171: }
0172: }
0173: return list;
0174: }
0175:
0176: /**
0177: * Returns a new {@link Collection} containing <tt><i>a</i> - <i>b</i></tt>.
0178: * The cardinality of each element <i>e</i> in the returned {@link Collection}
0179: * will be the cardinality of <i>e</i> in <i>a</i> minus the cardinality
0180: * of <i>e</i> in <i>b</i>, or zero, whichever is greater.
0181: *
0182: * @param a the collection to subtract from, must not be null
0183: * @param b the collection to subtract, must not be null
0184: * @return a new collection with the results
0185: * @see Collection#removeAll
0186: */
0187: public static Collection subtract(final Collection a,
0188: final Collection b) {
0189: ArrayList list = new ArrayList(a);
0190: for (Iterator it = b.iterator(); it.hasNext();) {
0191: list.remove(it.next());
0192: }
0193: return list;
0194: }
0195:
0196: /**
0197: * Returns <code>true</code> iff at least one element is in both collections.
0198: * <p>
0199: * In other words, this method returns <code>true</code> iff the
0200: * {@link #intersection} of <i>coll1</i> and <i>coll2</i> is not empty.
0201: *
0202: * @param coll1 the first collection, must not be null
0203: * @param coll2 the first collection, must not be null
0204: * @return <code>true</code> iff the intersection of the collections is non-empty
0205: * @since 2.1
0206: * @see #intersection
0207: */
0208: public static boolean containsAny(final Collection coll1,
0209: final Collection coll2) {
0210: if (coll1.size() < coll2.size()) {
0211: for (Iterator it = coll1.iterator(); it.hasNext();) {
0212: if (coll2.contains(it.next())) {
0213: return true;
0214: }
0215: }
0216: } else {
0217: for (Iterator it = coll2.iterator(); it.hasNext();) {
0218: if (coll1.contains(it.next())) {
0219: return true;
0220: }
0221: }
0222: }
0223: return false;
0224: }
0225:
0226: /**
0227: * Returns a {@link Map} mapping each unique element in the given
0228: * {@link Collection} to an {@link Integer} representing the number
0229: * of occurrences of that element in the {@link Collection}.
0230: * <p>
0231: * Only those elements present in the collection will appear as
0232: * keys in the map.
0233: *
0234: * @param coll the collection to get the cardinality map for, must not be null
0235: * @return the populated cardinality map
0236: */
0237: public static Map getCardinalityMap(final Collection coll) {
0238: Map count = new HashMap();
0239: for (Iterator it = coll.iterator(); it.hasNext();) {
0240: Object obj = it.next();
0241: Integer c = (Integer) (count.get(obj));
0242: if (c == null) {
0243: count.put(obj, INTEGER_ONE);
0244: } else {
0245: count.put(obj, new Integer(c.intValue() + 1));
0246: }
0247: }
0248: return count;
0249: }
0250:
0251: /**
0252: * Returns <tt>true</tt> iff <i>a</i> is a sub-collection of <i>b</i>,
0253: * that is, iff the cardinality of <i>e</i> in <i>a</i> is less
0254: * than or equal to the cardinality of <i>e</i> in <i>b</i>,
0255: * for each element <i>e</i> in <i>a</i>.
0256: *
0257: * @param a the first (sub?) collection, must not be null
0258: * @param b the second (super?) collection, must not be null
0259: * @return <code>true</code> iff <i>a</i> is a sub-collection of <i>b</i>
0260: * @see #isProperSubCollection
0261: * @see Collection#containsAll
0262: */
0263: public static boolean isSubCollection(final Collection a,
0264: final Collection b) {
0265: Map mapa = getCardinalityMap(a);
0266: Map mapb = getCardinalityMap(b);
0267: Iterator it = a.iterator();
0268: while (it.hasNext()) {
0269: Object obj = it.next();
0270: if (getFreq(obj, mapa) > getFreq(obj, mapb)) {
0271: return false;
0272: }
0273: }
0274: return true;
0275: }
0276:
0277: /**
0278: * Returns <tt>true</tt> iff <i>a</i> is a <i>proper</i> sub-collection of <i>b</i>,
0279: * that is, iff the cardinality of <i>e</i> in <i>a</i> is less
0280: * than or equal to the cardinality of <i>e</i> in <i>b</i>,
0281: * for each element <i>e</i> in <i>a</i>, and there is at least one
0282: * element <i>f</i> such that the cardinality of <i>f</i> in <i>b</i>
0283: * is strictly greater than the cardinality of <i>f</i> in <i>a</i>.
0284: * <p>
0285: * The implementation assumes
0286: * <ul>
0287: * <li><code>a.size()</code> and <code>b.size()</code> represent the
0288: * total cardinality of <i>a</i> and <i>b</i>, resp. </li>
0289: * <li><code>a.size() < Integer.MAXVALUE</code></li>
0290: * </ul>
0291: *
0292: * @param a the first (sub?) collection, must not be null
0293: * @param b the second (super?) collection, must not be null
0294: * @return <code>true</code> iff <i>a</i> is a <i>proper</i> sub-collection of <i>b</i>
0295: * @see #isSubCollection
0296: * @see Collection#containsAll
0297: */
0298: public static boolean isProperSubCollection(final Collection a,
0299: final Collection b) {
0300: return (a.size() < b.size())
0301: && CollectionUtils.isSubCollection(a, b);
0302: }
0303:
0304: /**
0305: * Returns <tt>true</tt> iff the given {@link Collection}s contain
0306: * exactly the same elements with exactly the same cardinalities.
0307: * <p>
0308: * That is, iff the cardinality of <i>e</i> in <i>a</i> is
0309: * equal to the cardinality of <i>e</i> in <i>b</i>,
0310: * for each element <i>e</i> in <i>a</i> or <i>b</i>.
0311: *
0312: * @param a the first collection, must not be null
0313: * @param b the second collection, must not be null
0314: * @return <code>true</code> iff the collections contain the same elements with the same cardinalities.
0315: */
0316: public static boolean isEqualCollection(final Collection a,
0317: final Collection b) {
0318: if (a.size() != b.size()) {
0319: return false;
0320: } else {
0321: Map mapa = getCardinalityMap(a);
0322: Map mapb = getCardinalityMap(b);
0323: if (mapa.size() != mapb.size()) {
0324: return false;
0325: } else {
0326: Iterator it = mapa.keySet().iterator();
0327: while (it.hasNext()) {
0328: Object obj = it.next();
0329: if (getFreq(obj, mapa) != getFreq(obj, mapb)) {
0330: return false;
0331: }
0332: }
0333: return true;
0334: }
0335: }
0336: }
0337:
0338: /**
0339: * Returns the number of occurrences of <i>obj</i> in <i>coll</i>.
0340: *
0341: * @param obj the object to find the cardinality of
0342: * @param coll the collection to search
0343: * @return the the number of occurrences of obj in coll
0344: */
0345: public static int cardinality(Object obj, final Collection coll) {
0346: if (coll instanceof Set) {
0347: return (coll.contains(obj) ? 1 : 0);
0348: }
0349: if (coll instanceof Bag) {
0350: return ((Bag) coll).getCount(obj);
0351: }
0352: int count = 0;
0353: if (obj == null) {
0354: for (Iterator it = coll.iterator(); it.hasNext();) {
0355: if (it.next() == null) {
0356: count++;
0357: }
0358: }
0359: } else {
0360: for (Iterator it = coll.iterator(); it.hasNext();) {
0361: if (obj.equals(it.next())) {
0362: count++;
0363: }
0364: }
0365: }
0366: return count;
0367: }
0368:
0369: /**
0370: * Finds the first element in the given collection which matches the given predicate.
0371: * <p>
0372: * If the input collection or predicate is null, or no element of the collection
0373: * matches the predicate, null is returned.
0374: *
0375: * @param collection the collection to search, may be null
0376: * @param predicate the predicate to use, may be null
0377: * @return the first element of the collection which matches the predicate or null if none could be found
0378: */
0379: public static Object find(Collection collection, Predicate predicate) {
0380: if (collection != null && predicate != null) {
0381: for (Iterator iter = collection.iterator(); iter.hasNext();) {
0382: Object item = iter.next();
0383: if (predicate.evaluate(item)) {
0384: return item;
0385: }
0386: }
0387: }
0388: return null;
0389: }
0390:
0391: /**
0392: * Executes the given closure on each element in the collection.
0393: * <p>
0394: * If the input collection or closure is null, there is no change made.
0395: *
0396: * @param collection the collection to get the input from, may be null
0397: * @param closure the closure to perform, may be null
0398: */
0399: public static void forAllDo(Collection collection, Closure closure) {
0400: if (collection != null && closure != null) {
0401: for (Iterator it = collection.iterator(); it.hasNext();) {
0402: closure.execute(it.next());
0403: }
0404: }
0405: }
0406:
0407: /**
0408: * Filter the collection by applying a Predicate to each element. If the
0409: * predicate returns false, remove the element.
0410: * <p>
0411: * If the input collection or predicate is null, there is no change made.
0412: *
0413: * @param collection the collection to get the input from, may be null
0414: * @param predicate the predicate to use as a filter, may be null
0415: */
0416: public static void filter(Collection collection, Predicate predicate) {
0417: if (collection != null && predicate != null) {
0418: for (Iterator it = collection.iterator(); it.hasNext();) {
0419: if (predicate.evaluate(it.next()) == false) {
0420: it.remove();
0421: }
0422: }
0423: }
0424: }
0425:
0426: /**
0427: * Transform the collection by applying a Transformer to each element.
0428: * <p>
0429: * If the input collection or transformer is null, there is no change made.
0430: * <p>
0431: * This routine is best for Lists, for which set() is used to do the
0432: * transformations "in place." For other Collections, clear() and addAll()
0433: * are used to replace elements.
0434: * <p>
0435: * If the input collection controls its input, such as a Set, and the
0436: * Transformer creates duplicates (or are otherwise invalid), the
0437: * collection may reduce in size due to calling this method.
0438: *
0439: * @param collection the collection to get the input from, may be null
0440: * @param transformer the transformer to perform, may be null
0441: */
0442: public static void transform(Collection collection,
0443: Transformer transformer) {
0444: if (collection != null && transformer != null) {
0445: if (collection instanceof List) {
0446: List list = (List) collection;
0447: for (ListIterator it = list.listIterator(); it
0448: .hasNext();) {
0449: it.set(transformer.transform(it.next()));
0450: }
0451: } else {
0452: Collection resultCollection = collect(collection,
0453: transformer);
0454: collection.clear();
0455: collection.addAll(resultCollection);
0456: }
0457: }
0458: }
0459:
0460: /**
0461: * Counts the number of elements in the input collection that match the predicate.
0462: * <p>
0463: * A <code>null</code> collection or predicate matches no elements.
0464: *
0465: * @param inputCollection the collection to get the input from, may be null
0466: * @param predicate the predicate to use, may be null
0467: * @return the number of matches for the predicate in the collection
0468: */
0469: public static int countMatches(Collection inputCollection,
0470: Predicate predicate) {
0471: int count = 0;
0472: if (inputCollection != null && predicate != null) {
0473: for (Iterator it = inputCollection.iterator(); it.hasNext();) {
0474: if (predicate.evaluate(it.next())) {
0475: count++;
0476: }
0477: }
0478: }
0479: return count;
0480: }
0481:
0482: /**
0483: * Answers true if a predicate is true for at least one element of a collection.
0484: * <p>
0485: * A <code>null</code> collection or predicate returns false.
0486: *
0487: * @param collection the collection to get the input from, may be null
0488: * @param predicate the predicate to use, may be null
0489: * @return true if at least one element of the collection matches the predicate
0490: */
0491: public static boolean exists(Collection collection,
0492: Predicate predicate) {
0493: if (collection != null && predicate != null) {
0494: for (Iterator it = collection.iterator(); it.hasNext();) {
0495: if (predicate.evaluate(it.next())) {
0496: return true;
0497: }
0498: }
0499: }
0500: return false;
0501: }
0502:
0503: /**
0504: * Selects all elements from input collection which match the given predicate
0505: * into an output collection.
0506: * <p>
0507: * A <code>null</code> predicate matches no elements.
0508: *
0509: * @param inputCollection the collection to get the input from, may not be null
0510: * @param predicate the predicate to use, may be null
0511: * @return the elements matching the predicate (new list)
0512: * @throws NullPointerException if the input collection is null
0513: */
0514: public static Collection select(Collection inputCollection,
0515: Predicate predicate) {
0516: ArrayList answer = new ArrayList(inputCollection.size());
0517: select(inputCollection, predicate, answer);
0518: return answer;
0519: }
0520:
0521: /**
0522: * Selects all elements from input collection which match the given predicate
0523: * and adds them to outputCollection.
0524: * <p>
0525: * If the input collection or predicate is null, there is no change to the
0526: * output collection.
0527: *
0528: * @param inputCollection the collection to get the input from, may be null
0529: * @param predicate the predicate to use, may be null
0530: * @param outputCollection the collection to output into, may not be null
0531: */
0532: public static void select(Collection inputCollection,
0533: Predicate predicate, Collection outputCollection) {
0534: if (inputCollection != null && predicate != null) {
0535: for (Iterator iter = inputCollection.iterator(); iter
0536: .hasNext();) {
0537: Object item = iter.next();
0538: if (predicate.evaluate(item)) {
0539: outputCollection.add(item);
0540: }
0541: }
0542: }
0543: }
0544:
0545: /**
0546: * Selects all elements from inputCollection which don't match the given predicate
0547: * into an output collection.
0548: * <p>
0549: * If the input predicate is <code>null</code>, the result is an empty list.
0550: *
0551: * @param inputCollection the collection to get the input from, may not be null
0552: * @param predicate the predicate to use, may be null
0553: * @return the elements <b>not</b> matching the predicate (new list)
0554: * @throws NullPointerException if the input collection is null
0555: */
0556: public static Collection selectRejected(Collection inputCollection,
0557: Predicate predicate) {
0558: ArrayList answer = new ArrayList(inputCollection.size());
0559: selectRejected(inputCollection, predicate, answer);
0560: return answer;
0561: }
0562:
0563: /**
0564: * Selects all elements from inputCollection which don't match the given predicate
0565: * and adds them to outputCollection.
0566: * <p>
0567: * If the input predicate is <code>null</code>, no elements are added to <code>outputCollection</code>.
0568: *
0569: * @param inputCollection the collection to get the input from, may be null
0570: * @param predicate the predicate to use, may be null
0571: * @param outputCollection the collection to output into, may not be null
0572: */
0573: public static void selectRejected(Collection inputCollection,
0574: Predicate predicate, Collection outputCollection) {
0575: if (inputCollection != null && predicate != null) {
0576: for (Iterator iter = inputCollection.iterator(); iter
0577: .hasNext();) {
0578: Object item = iter.next();
0579: if (predicate.evaluate(item) == false) {
0580: outputCollection.add(item);
0581: }
0582: }
0583: }
0584: }
0585:
0586: /**
0587: * Returns a new Collection consisting of the elements of inputCollection transformed
0588: * by the given transformer.
0589: * <p>
0590: * If the input transformer is null, the result is an empty list.
0591: *
0592: * @param inputCollection the collection to get the input from, may not be null
0593: * @param transformer the transformer to use, may be null
0594: * @return the transformed result (new list)
0595: * @throws NullPointerException if the input collection is null
0596: */
0597: public static Collection collect(Collection inputCollection,
0598: Transformer transformer) {
0599: ArrayList answer = new ArrayList(inputCollection.size());
0600: collect(inputCollection, transformer, answer);
0601: return answer;
0602: }
0603:
0604: /**
0605: * Transforms all elements from the inputIterator with the given transformer
0606: * and adds them to the outputCollection.
0607: * <p>
0608: * If the input iterator or transformer is null, the result is an empty list.
0609: *
0610: * @param inputIterator the iterator to get the input from, may be null
0611: * @param transformer the transformer to use, may be null
0612: * @return the transformed result (new list)
0613: */
0614: public static Collection collect(Iterator inputIterator,
0615: Transformer transformer) {
0616: ArrayList answer = new ArrayList();
0617: collect(inputIterator, transformer, answer);
0618: return answer;
0619: }
0620:
0621: /**
0622: * Transforms all elements from inputCollection with the given transformer
0623: * and adds them to the outputCollection.
0624: * <p>
0625: * If the input collection or transformer is null, there is no change to the
0626: * output collection.
0627: *
0628: * @param inputCollection the collection to get the input from, may be null
0629: * @param transformer the transformer to use, may be null
0630: * @param outputCollection the collection to output into, may not be null
0631: * @return the outputCollection with the transformed input added
0632: * @throws NullPointerException if the output collection is null
0633: */
0634: public static Collection collect(Collection inputCollection,
0635: final Transformer transformer,
0636: final Collection outputCollection) {
0637: if (inputCollection != null) {
0638: return collect(inputCollection.iterator(), transformer,
0639: outputCollection);
0640: }
0641: return outputCollection;
0642: }
0643:
0644: /**
0645: * Transforms all elements from the inputIterator with the given transformer
0646: * and adds them to the outputCollection.
0647: * <p>
0648: * If the input iterator or transformer is null, there is no change to the
0649: * output collection.
0650: *
0651: * @param inputIterator the iterator to get the input from, may be null
0652: * @param transformer the transformer to use, may be null
0653: * @param outputCollection the collection to output into, may not be null
0654: * @return the outputCollection with the transformed input added
0655: * @throws NullPointerException if the output collection is null
0656: */
0657: public static Collection collect(Iterator inputIterator,
0658: final Transformer transformer,
0659: final Collection outputCollection) {
0660: if (inputIterator != null && transformer != null) {
0661: while (inputIterator.hasNext()) {
0662: Object item = inputIterator.next();
0663: Object value = transformer.transform(item);
0664: outputCollection.add(value);
0665: }
0666: }
0667: return outputCollection;
0668: }
0669:
0670: //-----------------------------------------------------------------------
0671: /**
0672: * Adds an element to the collection unless the element is null.
0673: *
0674: * @param collection the collection to add to, must not be null
0675: * @param object the object to add, if null it will not be added
0676: * @return true if the collection changed
0677: * @throws NullPointerException if the collection is null
0678: * @since Commons Collections 3.2
0679: */
0680: public static boolean addIgnoreNull(Collection collection,
0681: Object object) {
0682: return (object == null ? false : collection.add(object));
0683: }
0684:
0685: /**
0686: * Adds all elements in the iteration to the given collection.
0687: *
0688: * @param collection the collection to add to, must not be null
0689: * @param iterator the iterator of elements to add, must not be null
0690: * @throws NullPointerException if the collection or iterator is null
0691: */
0692: public static void addAll(Collection collection, Iterator iterator) {
0693: while (iterator.hasNext()) {
0694: collection.add(iterator.next());
0695: }
0696: }
0697:
0698: /**
0699: * Adds all elements in the enumeration to the given collection.
0700: *
0701: * @param collection the collection to add to, must not be null
0702: * @param enumeration the enumeration of elements to add, must not be null
0703: * @throws NullPointerException if the collection or enumeration is null
0704: */
0705: public static void addAll(Collection collection,
0706: Enumeration enumeration) {
0707: while (enumeration.hasMoreElements()) {
0708: collection.add(enumeration.nextElement());
0709: }
0710: }
0711:
0712: /**
0713: * Adds all elements in the array to the given collection.
0714: *
0715: * @param collection the collection to add to, must not be null
0716: * @param elements the array of elements to add, must not be null
0717: * @throws NullPointerException if the collection or array is null
0718: */
0719: public static void addAll(Collection collection, Object[] elements) {
0720: for (int i = 0, size = elements.length; i < size; i++) {
0721: collection.add(elements[i]);
0722: }
0723: }
0724:
0725: /**
0726: * Given an Object, and an index, returns the nth value in the
0727: * object.
0728: * <ul>
0729: * <li>If obj is a Map, returns the nth value from the <b>keySet</b> iterator, unless
0730: * the Map contains an Integer key with integer value = idx, in which case the
0731: * corresponding map entry value is returned. If idx exceeds the number of entries in
0732: * the map, an empty Iterator is returned.
0733: * <li>If obj is a List or an array, returns the nth value, throwing IndexOutOfBoundsException,
0734: * ArrayIndexOutOfBoundsException, resp. if the nth value does not exist.
0735: * <li>If obj is an iterator, enumeration or Collection, returns the nth value from the iterator,
0736: * returning an empty Iterator (resp. Enumeration) if the nth value does not exist.
0737: * <li>Returns the original obj if it is null or not a Collection or Iterator.
0738: * </ul>
0739: *
0740: * @param obj the object to get an index of, may be null
0741: * @param idx the index to get
0742: * @throws IndexOutOfBoundsException
0743: * @throws ArrayIndexOutOfBoundsException
0744: *
0745: * @deprecated use {@link #get(Object, int)} instead. Will be removed in v4.0
0746: */
0747: public static Object index(Object obj, int idx) {
0748: return index(obj, new Integer(idx));
0749: }
0750:
0751: /**
0752: * Given an Object, and a key (index), returns the value associated with
0753: * that key in the Object. The following checks are made:
0754: * <ul>
0755: * <li>If obj is a Map, use the index as a key to get a value. If no match continue.
0756: * <li>Check key is an Integer. If not, return the object passed in.
0757: * <li>If obj is a Map, get the nth value from the <b>keySet</b> iterator.
0758: * If the Map has fewer than n entries, return an empty Iterator.
0759: * <li>If obj is a List or an array, get the nth value, throwing IndexOutOfBoundsException,
0760: * ArrayIndexOutOfBoundsException, resp. if the nth value does not exist.
0761: * <li>If obj is an iterator, enumeration or Collection, get the nth value from the iterator,
0762: * returning an empty Iterator (resp. Enumeration) if the nth value does not exist.
0763: * <li>Return the original obj.
0764: * </ul>
0765: *
0766: * @param obj the object to get an index of
0767: * @param index the index to get
0768: * @return the object at the specified index
0769: * @throws IndexOutOfBoundsException
0770: * @throws ArrayIndexOutOfBoundsException
0771: *
0772: * @deprecated use {@link #get(Object, int)} instead. Will be removed in v4.0
0773: */
0774: public static Object index(Object obj, Object index) {
0775: if (obj instanceof Map) {
0776: Map map = (Map) obj;
0777: if (map.containsKey(index)) {
0778: return map.get(index);
0779: }
0780: }
0781: int idx = -1;
0782: if (index instanceof Integer) {
0783: idx = ((Integer) index).intValue();
0784: }
0785: if (idx < 0) {
0786: return obj;
0787: } else if (obj instanceof Map) {
0788: Map map = (Map) obj;
0789: Iterator iterator = map.keySet().iterator();
0790: return index(iterator, idx);
0791: } else if (obj instanceof List) {
0792: return ((List) obj).get(idx);
0793: } else if (obj instanceof Object[]) {
0794: return ((Object[]) obj)[idx];
0795: } else if (obj instanceof Enumeration) {
0796: Enumeration it = (Enumeration) obj;
0797: while (it.hasMoreElements()) {
0798: idx--;
0799: if (idx == -1) {
0800: return it.nextElement();
0801: } else {
0802: it.nextElement();
0803: }
0804: }
0805: } else if (obj instanceof Iterator) {
0806: return index((Iterator) obj, idx);
0807: } else if (obj instanceof Collection) {
0808: Iterator iterator = ((Collection) obj).iterator();
0809: return index(iterator, idx);
0810: }
0811: return obj;
0812: }
0813:
0814: private static Object index(Iterator iterator, int idx) {
0815: while (iterator.hasNext()) {
0816: idx--;
0817: if (idx == -1) {
0818: return iterator.next();
0819: } else {
0820: iterator.next();
0821: }
0822: }
0823: return iterator;
0824: }
0825:
0826: /**
0827: * Returns the <code>index</code>-th value in <code>object</code>, throwing
0828: * <code>IndexOutOfBoundsException</code> if there is no such element or
0829: * <code>IllegalArgumentException</code> if <code>object</code> is not an
0830: * instance of one of the supported types.
0831: * <p>
0832: * The supported types, and associated semantics are:
0833: * <ul>
0834: * <li> Map -- the value returned is the <code>Map.Entry</code> in position
0835: * <code>index</code> in the map's <code>entrySet</code> iterator,
0836: * if there is such an entry.</li>
0837: * <li> List -- this method is equivalent to the list's get method.</li>
0838: * <li> Array -- the <code>index</code>-th array entry is returned,
0839: * if there is such an entry; otherwise an <code>IndexOutOfBoundsException</code>
0840: * is thrown.</li>
0841: * <li> Collection -- the value returned is the <code>index</code>-th object
0842: * returned by the collection's default iterator, if there is such an element.</li>
0843: * <li> Iterator or Enumeration -- the value returned is the
0844: * <code>index</code>-th object in the Iterator/Enumeration, if there
0845: * is such an element. The Iterator/Enumeration is advanced to
0846: * <code>index</code> (or to the end, if <code>index</code> exceeds the
0847: * number of entries) as a side effect of this method.</li>
0848: * </ul>
0849: *
0850: * @param object the object to get a value from
0851: * @param index the index to get
0852: * @return the object at the specified index
0853: * @throws IndexOutOfBoundsException if the index is invalid
0854: * @throws IllegalArgumentException if the object type is invalid
0855: */
0856: public static Object get(Object object, int index) {
0857: if (index < 0) {
0858: throw new IndexOutOfBoundsException(
0859: "Index cannot be negative: " + index);
0860: }
0861: if (object instanceof Map) {
0862: Map map = (Map) object;
0863: Iterator iterator = map.entrySet().iterator();
0864: return get(iterator, index);
0865: } else if (object instanceof List) {
0866: return ((List) object).get(index);
0867: } else if (object instanceof Object[]) {
0868: return ((Object[]) object)[index];
0869: } else if (object instanceof Iterator) {
0870: Iterator it = (Iterator) object;
0871: while (it.hasNext()) {
0872: index--;
0873: if (index == -1) {
0874: return it.next();
0875: } else {
0876: it.next();
0877: }
0878: }
0879: throw new IndexOutOfBoundsException(
0880: "Entry does not exist: " + index);
0881: } else if (object instanceof Collection) {
0882: Iterator iterator = ((Collection) object).iterator();
0883: return get(iterator, index);
0884: } else if (object instanceof Enumeration) {
0885: Enumeration it = (Enumeration) object;
0886: while (it.hasMoreElements()) {
0887: index--;
0888: if (index == -1) {
0889: return it.nextElement();
0890: } else {
0891: it.nextElement();
0892: }
0893: }
0894: throw new IndexOutOfBoundsException(
0895: "Entry does not exist: " + index);
0896: } else if (object == null) {
0897: throw new IllegalArgumentException(
0898: "Unsupported object type: null");
0899: } else {
0900: try {
0901: return Array.get(object, index);
0902: } catch (IllegalArgumentException ex) {
0903: throw new IllegalArgumentException(
0904: "Unsupported object type: "
0905: + object.getClass().getName());
0906: }
0907: }
0908: }
0909:
0910: /**
0911: * Gets the size of the collection/iterator specified.
0912: * <p>
0913: * This method can handles objects as follows
0914: * <ul>
0915: * <li>Collection - the collection size
0916: * <li>Map - the map size
0917: * <li>Array - the array size
0918: * <li>Iterator - the number of elements remaining in the iterator
0919: * <li>Enumeration - the number of elements remaining in the enumeration
0920: * </ul>
0921: *
0922: * @param object the object to get the size of
0923: * @return the size of the specified collection
0924: * @throws IllegalArgumentException thrown if object is not recognised or null
0925: * @since Commons Collections 3.1
0926: */
0927: public static int size(Object object) {
0928: int total = 0;
0929: if (object instanceof Map) {
0930: total = ((Map) object).size();
0931: } else if (object instanceof Collection) {
0932: total = ((Collection) object).size();
0933: } else if (object instanceof Object[]) {
0934: total = ((Object[]) object).length;
0935: } else if (object instanceof Iterator) {
0936: Iterator it = (Iterator) object;
0937: while (it.hasNext()) {
0938: total++;
0939: it.next();
0940: }
0941: } else if (object instanceof Enumeration) {
0942: Enumeration it = (Enumeration) object;
0943: while (it.hasMoreElements()) {
0944: total++;
0945: it.nextElement();
0946: }
0947: } else if (object == null) {
0948: throw new IllegalArgumentException(
0949: "Unsupported object type: null");
0950: } else {
0951: try {
0952: total = Array.getLength(object);
0953: } catch (IllegalArgumentException ex) {
0954: throw new IllegalArgumentException(
0955: "Unsupported object type: "
0956: + object.getClass().getName());
0957: }
0958: }
0959: return total;
0960: }
0961:
0962: /**
0963: * Checks if the specified collection/array/iterator is empty.
0964: * <p>
0965: * This method can handles objects as follows
0966: * <ul>
0967: * <li>Collection - via collection isEmpty
0968: * <li>Map - via map isEmpty
0969: * <li>Array - using array size
0970: * <li>Iterator - via hasNext
0971: * <li>Enumeration - via hasMoreElements
0972: * </ul>
0973: * <p>
0974: * Note: This method is named to avoid clashing with
0975: * {@link #isEmpty(Collection)}.
0976: *
0977: * @param object the object to get the size of, not null
0978: * @return true if empty
0979: * @throws IllegalArgumentException thrown if object is not recognised or null
0980: * @since Commons Collections 3.2
0981: */
0982: public static boolean sizeIsEmpty(Object object) {
0983: if (object instanceof Collection) {
0984: return ((Collection) object).isEmpty();
0985: } else if (object instanceof Map) {
0986: return ((Map) object).isEmpty();
0987: } else if (object instanceof Object[]) {
0988: return ((Object[]) object).length == 0;
0989: } else if (object instanceof Iterator) {
0990: return ((Iterator) object).hasNext() == false;
0991: } else if (object instanceof Enumeration) {
0992: return ((Enumeration) object).hasMoreElements() == false;
0993: } else if (object == null) {
0994: throw new IllegalArgumentException(
0995: "Unsupported object type: null");
0996: } else {
0997: try {
0998: return Array.getLength(object) == 0;
0999: } catch (IllegalArgumentException ex) {
1000: throw new IllegalArgumentException(
1001: "Unsupported object type: "
1002: + object.getClass().getName());
1003: }
1004: }
1005: }
1006:
1007: //-----------------------------------------------------------------------
1008: /**
1009: * Null-safe check if the specified collection is empty.
1010: * <p>
1011: * Null returns true.
1012: *
1013: * @param coll the collection to check, may be null
1014: * @return true if empty or null
1015: * @since Commons Collections 3.2
1016: */
1017: public static boolean isEmpty(Collection coll) {
1018: return (coll == null || coll.isEmpty());
1019: }
1020:
1021: /**
1022: * Null-safe check if the specified collection is not empty.
1023: * <p>
1024: * Null returns false.
1025: *
1026: * @param coll the collection to check, may be null
1027: * @return true if non-null and non-empty
1028: * @since Commons Collections 3.2
1029: */
1030: public static boolean isNotEmpty(Collection coll) {
1031: return !CollectionUtils.isEmpty(coll);
1032: }
1033:
1034: //-----------------------------------------------------------------------
1035: /**
1036: * Reverses the order of the given array.
1037: *
1038: * @param array the array to reverse
1039: */
1040: public static void reverseArray(Object[] array) {
1041: int i = 0;
1042: int j = array.length - 1;
1043: Object tmp;
1044:
1045: while (j > i) {
1046: tmp = array[j];
1047: array[j] = array[i];
1048: array[i] = tmp;
1049: j--;
1050: i++;
1051: }
1052: }
1053:
1054: private static final int getFreq(final Object obj, final Map freqMap) {
1055: Integer count = (Integer) freqMap.get(obj);
1056: if (count != null) {
1057: return count.intValue();
1058: }
1059: return 0;
1060: }
1061:
1062: /**
1063: * Returns true if no more elements can be added to the Collection.
1064: * <p>
1065: * This method uses the {@link BoundedCollection} interface to determine the
1066: * full status. If the collection does not implement this interface then
1067: * false is returned.
1068: * <p>
1069: * The collection does not have to implement this interface directly.
1070: * If the collection has been decorated using the decorators subpackage
1071: * then these will be removed to access the BoundedCollection.
1072: *
1073: * @param coll the collection to check
1074: * @return true if the BoundedCollection is full
1075: * @throws NullPointerException if the collection is null
1076: */
1077: public static boolean isFull(Collection coll) {
1078: if (coll == null) {
1079: throw new NullPointerException(
1080: "The collection must not be null");
1081: }
1082: if (coll instanceof BoundedCollection) {
1083: return ((BoundedCollection) coll).isFull();
1084: }
1085: try {
1086: BoundedCollection bcoll = UnmodifiableBoundedCollection
1087: .decorateUsing(coll);
1088: return bcoll.isFull();
1089:
1090: } catch (IllegalArgumentException ex) {
1091: return false;
1092: }
1093: }
1094:
1095: /**
1096: * Get the maximum number of elements that the Collection can contain.
1097: * <p>
1098: * This method uses the {@link BoundedCollection} interface to determine the
1099: * maximum size. If the collection does not implement this interface then
1100: * -1 is returned.
1101: * <p>
1102: * The collection does not have to implement this interface directly.
1103: * If the collection has been decorated using the decorators subpackage
1104: * then these will be removed to access the BoundedCollection.
1105: *
1106: * @param coll the collection to check
1107: * @return the maximum size of the BoundedCollection, -1 if no maximum size
1108: * @throws NullPointerException if the collection is null
1109: */
1110: public static int maxSize(Collection coll) {
1111: if (coll == null) {
1112: throw new NullPointerException(
1113: "The collection must not be null");
1114: }
1115: if (coll instanceof BoundedCollection) {
1116: return ((BoundedCollection) coll).maxSize();
1117: }
1118: try {
1119: BoundedCollection bcoll = UnmodifiableBoundedCollection
1120: .decorateUsing(coll);
1121: return bcoll.maxSize();
1122:
1123: } catch (IllegalArgumentException ex) {
1124: return -1;
1125: }
1126: }
1127:
1128: //-----------------------------------------------------------------------
1129: /**
1130: * Returns a collection containing all the elements in <code>collection</code>
1131: * that are also in <code>retain</code>. The cardinality of an element <code>e</code>
1132: * in the returned collection is the same as the cardinality of <code>e</code>
1133: * in <code>collection</code> unless <code>retain</code> does not contain <code>e</code>, in which
1134: * case the cardinality is zero. This method is useful if you do not wish to modify
1135: * the collection <code>c</code> and thus cannot call <code>c.retainAll(retain);</code>.
1136: *
1137: * @param collection the collection whose contents are the target of the #retailAll operation
1138: * @param retain the collection containing the elements to be retained in the returned collection
1139: * @return a <code>Collection</code> containing all the elements of <code>collection</code>
1140: * that occur at least once in <code>retain</code>.
1141: * @throws NullPointerException if either parameter is null
1142: * @since Commons Collections 3.2
1143: */
1144: public static Collection retainAll(Collection collection,
1145: Collection retain) {
1146: return ListUtils.retainAll(collection, retain);
1147: }
1148:
1149: /**
1150: * Removes the elements in <code>remove</code> from <code>collection</code>. That is, this
1151: * method returns a collection containing all the elements in <code>c</code>
1152: * that are not in <code>remove</code>. The cardinality of an element <code>e</code>
1153: * in the returned collection is the same as the cardinality of <code>e</code>
1154: * in <code>collection</code> unless <code>remove</code> contains <code>e</code>, in which
1155: * case the cardinality is zero. This method is useful if you do not wish to modify
1156: * the collection <code>c</code> and thus cannot call <code>collection.removeAll(remove);</code>.
1157: *
1158: * @param collection the collection from which items are removed (in the returned collection)
1159: * @param remove the items to be removed from the returned <code>collection</code>
1160: * @return a <code>Collection</code> containing all the elements of <code>collection</code> except
1161: * any elements that also occur in <code>remove</code>.
1162: * @throws NullPointerException if either parameter is null
1163: * @since Commons Collections 3.2
1164: */
1165: public static Collection removeAll(Collection collection,
1166: Collection remove) {
1167: return ListUtils.retainAll(collection, remove);
1168: }
1169:
1170: //-----------------------------------------------------------------------
1171: /**
1172: * Returns a synchronized collection backed by the given collection.
1173: * <p>
1174: * You must manually synchronize on the returned buffer's iterator to
1175: * avoid non-deterministic behavior:
1176: *
1177: * <pre>
1178: * Collection c = CollectionUtils.synchronizedCollection(myCollection);
1179: * synchronized (c) {
1180: * Iterator i = c.iterator();
1181: * while (i.hasNext()) {
1182: * process (i.next());
1183: * }
1184: * }
1185: * </pre>
1186: *
1187: * This method uses the implementation in the decorators subpackage.
1188: *
1189: * @param collection the collection to synchronize, must not be null
1190: * @return a synchronized collection backed by the given collection
1191: * @throws IllegalArgumentException if the collection is null
1192: */
1193: public static Collection synchronizedCollection(
1194: Collection collection) {
1195: return SynchronizedCollection.decorate(collection);
1196: }
1197:
1198: /**
1199: * Returns an unmodifiable collection backed by the given collection.
1200: * <p>
1201: * This method uses the implementation in the decorators subpackage.
1202: *
1203: * @param collection the collection to make unmodifiable, must not be null
1204: * @return an unmodifiable collection backed by the given collection
1205: * @throws IllegalArgumentException if the collection is null
1206: */
1207: public static Collection unmodifiableCollection(
1208: Collection collection) {
1209: return UnmodifiableCollection.decorate(collection);
1210: }
1211:
1212: /**
1213: * Returns a predicated (validating) collection backed by the given collection.
1214: * <p>
1215: * Only objects that pass the test in the given predicate can be added to the collection.
1216: * Trying to add an invalid object results in an IllegalArgumentException.
1217: * It is important not to use the original collection after invoking this method,
1218: * as it is a backdoor for adding invalid objects.
1219: *
1220: * @param collection the collection to predicate, must not be null
1221: * @param predicate the predicate for the collection, must not be null
1222: * @return a predicated collection backed by the given collection
1223: * @throws IllegalArgumentException if the Collection is null
1224: */
1225: public static Collection predicatedCollection(
1226: Collection collection, Predicate predicate) {
1227: return PredicatedCollection.decorate(collection, predicate);
1228: }
1229:
1230: /**
1231: * Returns a typed collection backed by the given collection.
1232: * <p>
1233: * Only objects of the specified type can be added to the collection.
1234: *
1235: * @param collection the collection to limit to a specific type, must not be null
1236: * @param type the type of objects which may be added to the collection
1237: * @return a typed collection backed by the specified collection
1238: */
1239: public static Collection typedCollection(Collection collection,
1240: Class type) {
1241: return TypedCollection.decorate(collection, type);
1242: }
1243:
1244: /**
1245: * Returns a transformed bag backed by the given collection.
1246: * <p>
1247: * Each object is passed through the transformer as it is added to the
1248: * Collection. It is important not to use the original collection after invoking this
1249: * method, as it is a backdoor for adding untransformed objects.
1250: *
1251: * @param collection the collection to predicate, must not be null
1252: * @param transformer the transformer for the collection, must not be null
1253: * @return a transformed collection backed by the given collection
1254: * @throws IllegalArgumentException if the Collection or Transformer is null
1255: */
1256: public static Collection transformedCollection(
1257: Collection collection, Transformer transformer) {
1258: return TransformedCollection.decorate(collection, transformer);
1259: }
1260:
1261: }
|