001: /*
002: * Copyright 2005 JBoss Inc
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.drools.util;
018:
019: import java.lang.reflect.Array;
020: import java.util.ArrayList;
021: import java.util.Arrays;
022: import java.util.Collection;
023: import java.util.Collections;
024: import java.util.Iterator;
025:
026: /**
027: * Decorates a collection of other collections to provide a single unified view.
028: * <p>
029: * Changes made to this collection will actually be made on the decorated
030: * collection. Add and remove operations require the use of a pluggable
031: * strategy. If no strategy is provided then add and remove are unsupported.
032: *
033: * @since Commons Collections 3.0
034: * @version $Revision: 1.1 $ $Date: 2005/07/26 01:06:32 $
035: *
036: * @author Brian McCallister
037: * @author Stephen Colebourne
038: * @author Phil Steitz
039: */
040: public class CompositeCollection implements Collection {
041:
042: /** CollectionMutator to handle changes to the collection */
043: protected CollectionMutator mutator;
044:
045: /** Collections in the composite */
046: protected Collection[] all;
047:
048: /**
049: * Create an empty CompositeCollection.
050: */
051: public CompositeCollection() {
052: super ();
053: this .all = new Collection[0];
054: }
055:
056: /**
057: * Create a Composite Collection with only coll composited.
058: *
059: * @param coll
060: * a collection to decorate
061: */
062: public CompositeCollection(final Collection coll) {
063: this ();
064: this .addComposited(coll);
065: }
066:
067: /**
068: * Create a CompositeCollection with colls as the initial list of composited
069: * collections.
070: *
071: * @param colls
072: * an array of collections to decorate
073: */
074: public CompositeCollection(final Collection[] colls) {
075: this ();
076: this .addComposited(colls);
077: }
078:
079: // -----------------------------------------------------------------------
080: /**
081: * Gets the size of this composite collection.
082: * <p>
083: * This implementation calls <code>size()</code> on each collection.
084: *
085: * @return total number of elements in all contained containers
086: */
087: public int size() {
088: int size = 0;
089: for (int i = this .all.length - 1; i >= 0; i--) {
090: size += this .all[i].size();
091: }
092: return size;
093: }
094:
095: /**
096: * Checks whether this composite collection is empty.
097: * <p>
098: * This implementation calls <code>isEmpty()</code> on each collection.
099: *
100: * @return true if all of the contained collections are empty
101: */
102: public boolean isEmpty() {
103: for (int i = this .all.length - 1; i >= 0; i--) {
104: if (this .all[i].isEmpty() == false) {
105: return false;
106: }
107: }
108: return true;
109: }
110:
111: /**
112: * Checks whether this composite collection contains the object.
113: * <p>
114: * This implementation calls <code>contains()</code> on each collection.
115: *
116: * @param obj
117: * the object to search for
118: * @return true if obj is contained in any of the contained collections
119: */
120: public boolean contains(final Object obj) {
121: for (int i = this .all.length - 1; i >= 0; i--) {
122: if (this .all[i].contains(obj)) {
123: return true;
124: }
125: }
126: return false;
127: }
128:
129: /**
130: * Gets an iterator over all the collections in this composite.
131: * <p>
132: * This implementation uses an <code>IteratorChain</code>.
133: *
134: * @return an <code>IteratorChain</code> instance which supports
135: * <code>remove()</code>. Iteration occurs over contained
136: * collections in the order they were added, but this behavior
137: * should not be relied upon.
138: * @see IteratorChain
139: */
140: public Iterator iterator() {
141: if (this .all.length == 0) {
142: return Collections.EMPTY_LIST.iterator();
143: }
144: final IteratorChain chain = new IteratorChain();
145: for (int i = 0; i < this .all.length; ++i) {
146: chain.addIterator(this .all[i].iterator());
147: }
148: return chain;
149: }
150:
151: /**
152: * Returns an array containing all of the elements in this composite.
153: *
154: * @return an object array of all the elements in the collection
155: */
156: public Object[] toArray() {
157: final Object[] result = new Object[this .size()];
158: int i = 0;
159: for (final Iterator it = this .iterator(); it.hasNext(); i++) {
160: result[i] = it.next();
161: }
162: return result;
163: }
164:
165: /**
166: * Returns an object array, populating the supplied array if possible. See
167: * <code>Collection</code> interface for full details.
168: *
169: * @param array
170: * the array to use, populating if possible
171: * @return an array of all the elements in the collection
172: */
173: public Object[] toArray(final Object[] array) {
174: final int size = this .size();
175: Object[] result;
176: if (array.length >= size) {
177: result = array;
178: } else {
179: result = (Object[]) Array.newInstance(array.getClass()
180: .getComponentType(), size);
181: }
182:
183: int offset = 0;
184: for (int i = 0; i < this .all.length; ++i) {
185: for (final Iterator it = this .all[i].iterator(); it
186: .hasNext();) {
187: result[offset++] = it.next();
188: }
189: }
190: if (result.length > size) {
191: result[size] = null;
192: }
193: return result;
194: }
195:
196: /**
197: * Adds an object to the collection, throwing UnsupportedOperationException
198: * unless a CollectionMutator strategy is specified.
199: *
200: * @param obj
201: * the object to add
202: * @return true if the collection was modified
203: * @throws UnsupportedOperationException
204: * if CollectionMutator hasn't been set
205: * @throws UnsupportedOperationException
206: * if add is unsupported
207: * @throws ClassCastException
208: * if the object cannot be added due to its type
209: * @throws NullPointerException
210: * if the object cannot be added because its null
211: * @throws IllegalArgumentException
212: * if the object cannot be added
213: */
214: public boolean add(final Object obj) {
215: if (this .mutator == null) {
216: throw new UnsupportedOperationException(
217: "add() is not supported on CompositeCollection without a CollectionMutator strategy");
218: }
219: return this .mutator.add(this , this .all, obj);
220: }
221:
222: /**
223: * Removes an object from the collection, throwing
224: * UnsupportedOperationException unless a CollectionMutator strategy is
225: * specified.
226: *
227: * @param obj
228: * the object being removed
229: * @return true if the collection is changed
230: * @throws UnsupportedOperationException
231: * if removed is unsupported
232: * @throws ClassCastException
233: * if the object cannot be removed due to its type
234: * @throws NullPointerException
235: * if the object cannot be removed because its null
236: * @throws IllegalArgumentException
237: * if the object cannot be removed
238: */
239: public boolean remove(final Object obj) {
240: if (this .mutator == null) {
241: throw new UnsupportedOperationException(
242: "remove() is not supported on CompositeCollection without a CollectionMutator strategy");
243: }
244: return this .mutator.remove(this , this .all, obj);
245: }
246:
247: /**
248: * Checks whether this composite contains all the elements in the specified
249: * collection.
250: * <p>
251: * This implementation calls <code>contains()</code> for each element in
252: * the specified collection.
253: *
254: * @param coll
255: * the collection to check for
256: * @return true if all elements contained
257: */
258: public boolean containsAll(final Collection coll) {
259: for (final Iterator it = coll.iterator(); it.hasNext();) {
260: if (this .contains(it.next()) == false) {
261: return false;
262: }
263: }
264: return true;
265: }
266:
267: /**
268: * Adds a collection of elements to this collection, throwing
269: * UnsupportedOperationException unless a CollectionMutator strategy is
270: * specified.
271: *
272: * @param coll
273: * the collection to add
274: * @return true if the collection was modified
275: * @throws UnsupportedOperationException
276: * if CollectionMutator hasn't been set
277: * @throws UnsupportedOperationException
278: * if add is unsupported
279: * @throws ClassCastException
280: * if the object cannot be added due to its type
281: * @throws NullPointerException
282: * if the object cannot be added because its null
283: * @throws IllegalArgumentException
284: * if the object cannot be added
285: */
286: public boolean addAll(final Collection coll) {
287: if (this .mutator == null) {
288: throw new UnsupportedOperationException(
289: "addAll() is not supported on CompositeCollection without a CollectionMutator strategy");
290: }
291: return this .mutator.addAll(this , this .all, coll);
292: }
293:
294: /**
295: * Removes the elements in the specified collection from this composite
296: * collection.
297: * <p>
298: * This implementation calls <code>removeAll</code> on each collection.
299: *
300: * @param coll
301: * the collection to remove
302: * @return true if the collection was modified
303: * @throws UnsupportedOperationException
304: * if removeAll is unsupported
305: */
306: public boolean removeAll(final Collection coll) {
307: if (coll.isEmpty()) {
308: return false;
309: }
310:
311: boolean changed = false;
312: for (int i = this .all.length - 1; i >= 0; i--) {
313: changed |= this .all[i].removeAll(coll);
314: }
315: return changed;
316: }
317:
318: /**
319: * Retains all the elements in the specified collection in this composite
320: * collection, removing all others.
321: * <p>
322: * This implementation calls <code>retainAll()</code> on each collection.
323: *
324: * @param coll
325: * the collection to remove
326: * @return true if the collection was modified
327: * @throws UnsupportedOperationException
328: * if retainAll is unsupported
329: */
330: public boolean retainAll(final Collection coll) {
331: boolean changed = false;
332: for (int i = this .all.length - 1; i >= 0; i--) {
333: changed = (this .all[i].retainAll(coll) || changed);
334: }
335: return changed;
336: }
337:
338: /**
339: * Removes all of the elements from this collection .
340: * <p>
341: * This implementation calls <code>clear()</code> on each collection.
342: *
343: * @throws UnsupportedOperationException
344: * if clear is unsupported
345: */
346: public void clear() {
347: for (int i = 0; i < this .all.length; ++i) {
348: this .all[i].clear();
349: }
350: }
351:
352: // -----------------------------------------------------------------------
353: /**
354: * Specify a CollectionMutator strategy instance to handle changes.
355: *
356: * @param mutator
357: * the mutator to use
358: */
359: public void setMutator(final CollectionMutator mutator) {
360: this .mutator = mutator;
361: }
362:
363: /**
364: * Add these Collections to the list of collections in this composite
365: *
366: * @param comps
367: * Collections to be appended to the composite
368: */
369: public void addComposited(final Collection[] comps) {
370: final ArrayList list = new ArrayList(Arrays.asList(this .all));
371: list.addAll(Arrays.asList(comps));
372: this .all = (Collection[]) list.toArray(new Collection[list
373: .size()]);
374: }
375:
376: /**
377: * Add an additional collection to this composite.
378: *
379: * @param c
380: * the collection to add
381: */
382: public void addComposited(final Collection c) {
383: this .addComposited(new Collection[] { c });
384: }
385:
386: /**
387: * Add two additional collections to this composite.
388: *
389: * @param c
390: * the first collection to add
391: * @param d
392: * the second collection to add
393: */
394: public void addComposited(final Collection c, final Collection d) {
395: this .addComposited(new Collection[] { c, d });
396: }
397:
398: /**
399: * Removes a collection from the those being decorated in this composite.
400: *
401: * @param coll
402: * collection to be removed
403: */
404: public void removeComposited(final Collection coll) {
405: final ArrayList list = new ArrayList(this .all.length);
406: list.addAll(Arrays.asList(this .all));
407: list.remove(coll);
408: this .all = (Collection[]) list.toArray(new Collection[list
409: .size()]);
410: }
411:
412: /**
413: * Returns a new collection containing all of the elements
414: *
415: * @return A new ArrayList containing all of the elements in this composite.
416: * The new collection is <i>not </i> backed by this composite.
417: */
418: public Collection toCollection() {
419: return new ArrayList(this );
420: }
421:
422: /**
423: * Gets the collections being decorated.
424: *
425: * @return Unmodifiable collection of all collections in this composite.
426: */
427: public Collection getCollections() {
428: return Collections.unmodifiableList(Arrays.asList(this .all));
429: }
430:
431: // -----------------------------------------------------------------------
432: /**
433: * Pluggable strategy to handle changes to the composite.
434: */
435: public interface CollectionMutator {
436:
437: /**
438: * Called when an object is to be added to the composite.
439: *
440: * @param composite
441: * the CompositeCollection being changed
442: * @param collections
443: * all of the Collection instances in this
444: * CompositeCollection
445: * @param obj
446: * the object being added
447: * @return true if the collection is changed
448: * @throws UnsupportedOperationException
449: * if add is unsupported
450: * @throws ClassCastException
451: * if the object cannot be added due to its type
452: * @throws NullPointerException
453: * if the object cannot be added because its null
454: * @throws IllegalArgumentException
455: * if the object cannot be added
456: */
457: public boolean add(CompositeCollection composite,
458: Collection[] collections, Object obj);
459:
460: /**
461: * Called when a collection is to be added to the composite.
462: *
463: * @param composite
464: * the CompositeCollection being changed
465: * @param collections
466: * all of the Collection instances in this
467: * CompositeCollection
468: * @param coll
469: * the collection being added
470: * @return true if the collection is changed
471: * @throws UnsupportedOperationException
472: * if add is unsupported
473: * @throws ClassCastException
474: * if the object cannot be added due to its type
475: * @throws NullPointerException
476: * if the object cannot be added because its null
477: * @throws IllegalArgumentException
478: * if the object cannot be added
479: */
480: public boolean addAll(CompositeCollection composite,
481: Collection[] collections, Collection coll);
482:
483: /**
484: * Called when an object is to be removed to the composite.
485: *
486: * @param composite
487: * the CompositeCollection being changed
488: * @param collections
489: * all of the Collection instances in this
490: * CompositeCollection
491: * @param obj
492: * the object being removed
493: * @return true if the collection is changed
494: * @throws UnsupportedOperationException
495: * if removed is unsupported
496: * @throws ClassCastException
497: * if the object cannot be removed due to its type
498: * @throws NullPointerException
499: * if the object cannot be removed because its null
500: * @throws IllegalArgumentException
501: * if the object cannot be removed
502: */
503: public boolean remove(CompositeCollection composite,
504: Collection[] collections, Object obj);
505:
506: }
507:
508: }
|