001: /*
002: * Copyright 2001-2004 The Apache Software Foundation
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: package org.apache.commons.collections.collection;
017:
018: import java.lang.reflect.Array;
019: import java.util.ArrayList;
020: import java.util.Arrays;
021: import java.util.Collection;
022: import java.util.Iterator;
023:
024: import org.apache.commons.collections.iterators.EmptyIterator;
025: import org.apache.commons.collections.iterators.IteratorChain;
026: import org.apache.commons.collections.list.UnmodifiableList;
027:
028: /**
029: * Decorates a collection of other collections to provide a single unified view.
030: * <p>
031: * Changes made to this collection will actually be made on the decorated collection.
032: * Add and remove operations require the use of a pluggable strategy. If no
033: * strategy is provided then add and remove are unsupported.
034: *
035: * @since Commons Collections 3.0
036: * @version $Revision: 155406 $ $Date: 2005-02-26 12:55:26 +0000 (Sat, 26 Feb 2005) $
037: *
038: * @author Brian McCallister
039: * @author Stephen Colebourne
040: * @author Phil Steitz
041: */
042: public class CompositeCollection implements Collection {
043:
044: /** CollectionMutator to handle changes to the collection */
045: protected CollectionMutator mutator;
046:
047: /** Collections in the composite */
048: protected Collection[] all;
049:
050: /**
051: * Create an empty CompositeCollection.
052: */
053: public CompositeCollection() {
054: super ();
055: this .all = new Collection[0];
056: }
057:
058: /**
059: * Create a Composite Collection with only coll composited.
060: *
061: * @param coll a collection to decorate
062: */
063: public CompositeCollection(Collection coll) {
064: this ();
065: this .addComposited(coll);
066: }
067:
068: /**
069: * Create a CompositeCollection with colls as the initial list of
070: * composited collections.
071: *
072: * @param colls an array of collections to decorate
073: */
074: public CompositeCollection(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 the object to search for
117: * @return true if obj is contained in any of the contained collections
118: */
119: public boolean contains(Object obj) {
120: for (int i = this .all.length - 1; i >= 0; i--) {
121: if (this .all[i].contains(obj)) {
122: return true;
123: }
124: }
125: return false;
126: }
127:
128: /**
129: * Gets an iterator over all the collections in this composite.
130: * <p>
131: * This implementation uses an <code>IteratorChain</code>.
132: *
133: * @return an <code>IteratorChain</code> instance which supports
134: * <code>remove()</code>. Iteration occurs over contained collections in
135: * the order they were added, but this behavior should not be relied upon.
136: * @see IteratorChain
137: */
138: public Iterator iterator() {
139: if (this .all.length == 0) {
140: return EmptyIterator.INSTANCE;
141: }
142: IteratorChain chain = new IteratorChain();
143: for (int i = 0; i < this .all.length; ++i) {
144: chain.addIterator(this .all[i].iterator());
145: }
146: return chain;
147: }
148:
149: /**
150: * Returns an array containing all of the elements in this composite.
151: *
152: * @return an object array of all the elements in the collection
153: */
154: public Object[] toArray() {
155: final Object[] result = new Object[this .size()];
156: int i = 0;
157: for (Iterator it = this .iterator(); it.hasNext(); i++) {
158: result[i] = it.next();
159: }
160: return result;
161: }
162:
163: /**
164: * Returns an object array, populating the supplied array if possible.
165: * See <code>Collection</code> interface for full details.
166: *
167: * @param array the array to use, populating if possible
168: * @return an array of all the elements in the collection
169: */
170: public Object[] toArray(Object[] array) {
171: int size = this .size();
172: Object[] result = null;
173: if (array.length >= size) {
174: result = array;
175: } else {
176: result = (Object[]) Array.newInstance(array.getClass()
177: .getComponentType(), size);
178: }
179:
180: int offset = 0;
181: for (int i = 0; i < this .all.length; ++i) {
182: for (Iterator it = this .all[i].iterator(); it.hasNext();) {
183: result[offset++] = it.next();
184: }
185: }
186: if (result.length > size) {
187: result[size] = null;
188: }
189: return result;
190: }
191:
192: /**
193: * Adds an object to the collection, throwing UnsupportedOperationException
194: * unless a CollectionMutator strategy is specified.
195: *
196: * @param obj the object to add
197: * @return true if the collection was modified
198: * @throws UnsupportedOperationException if CollectionMutator hasn't been set
199: * @throws UnsupportedOperationException if add is unsupported
200: * @throws ClassCastException if the object cannot be added due to its type
201: * @throws NullPointerException if the object cannot be added because its null
202: * @throws IllegalArgumentException if the object cannot be added
203: */
204: public boolean add(Object obj) {
205: if (this .mutator == null) {
206: throw new UnsupportedOperationException(
207: "add() is not supported on CompositeCollection without a CollectionMutator strategy");
208: }
209: return this .mutator.add(this , this .all, obj);
210: }
211:
212: /**
213: * Removes an object from the collection, throwing UnsupportedOperationException
214: * unless a CollectionMutator strategy is specified.
215: *
216: * @param obj the object being removed
217: * @return true if the collection is changed
218: * @throws UnsupportedOperationException if removed is unsupported
219: * @throws ClassCastException if the object cannot be removed due to its type
220: * @throws NullPointerException if the object cannot be removed because its null
221: * @throws IllegalArgumentException if the object cannot be removed
222: */
223: public boolean remove(Object obj) {
224: if (this .mutator == null) {
225: throw new UnsupportedOperationException(
226: "remove() is not supported on CompositeCollection without a CollectionMutator strategy");
227: }
228: return this .mutator.remove(this , this .all, obj);
229: }
230:
231: /**
232: * Checks whether this composite contains all the elements in the specified collection.
233: * <p>
234: * This implementation calls <code>contains()</code> for each element in the
235: * specified collection.
236: *
237: * @param coll the collection to check for
238: * @return true if all elements contained
239: */
240: public boolean containsAll(Collection coll) {
241: for (Iterator it = coll.iterator(); it.hasNext();) {
242: if (this .contains(it.next()) == false) {
243: return false;
244: }
245: }
246: return true;
247: }
248:
249: /**
250: * Adds a collection of elements to this collection, throwing
251: * UnsupportedOperationException unless a CollectionMutator strategy is specified.
252: *
253: * @param coll the collection to add
254: * @return true if the collection was modified
255: * @throws UnsupportedOperationException if CollectionMutator hasn't been set
256: * @throws UnsupportedOperationException if add is unsupported
257: * @throws ClassCastException if the object cannot be added due to its type
258: * @throws NullPointerException if the object cannot be added because its null
259: * @throws IllegalArgumentException if the object cannot be added
260: */
261: public boolean addAll(Collection coll) {
262: if (this .mutator == null) {
263: throw new UnsupportedOperationException(
264: "addAll() is not supported on CompositeCollection without a CollectionMutator strategy");
265: }
266: return this .mutator.addAll(this , this .all, coll);
267: }
268:
269: /**
270: * Removes the elements in the specified collection from this composite collection.
271: * <p>
272: * This implementation calls <code>removeAll</code> on each collection.
273: *
274: * @param coll the collection to remove
275: * @return true if the collection was modified
276: * @throws UnsupportedOperationException if removeAll is unsupported
277: */
278: public boolean removeAll(Collection coll) {
279: if (coll.size() == 0) {
280: return false;
281: }
282: boolean changed = false;
283: for (int i = this .all.length - 1; i >= 0; i--) {
284: changed = (this .all[i].removeAll(coll) || changed);
285: }
286: return changed;
287: }
288:
289: /**
290: * Retains all the elements in the specified collection in this composite collection,
291: * removing all others.
292: * <p>
293: * This implementation calls <code>retainAll()</code> on each collection.
294: *
295: * @param coll the collection to remove
296: * @return true if the collection was modified
297: * @throws UnsupportedOperationException if retainAll is unsupported
298: */
299: public boolean retainAll(final Collection coll) {
300: boolean changed = false;
301: for (int i = this .all.length - 1; i >= 0; i--) {
302: changed = (this .all[i].retainAll(coll) || changed);
303: }
304: return changed;
305: }
306:
307: /**
308: * Removes all of the elements from this collection .
309: * <p>
310: * This implementation calls <code>clear()</code> on each collection.
311: *
312: * @throws UnsupportedOperationException if clear is unsupported
313: */
314: public void clear() {
315: for (int i = 0; i < this .all.length; ++i) {
316: this .all[i].clear();
317: }
318: }
319:
320: //-----------------------------------------------------------------------
321: /**
322: * Specify a CollectionMutator strategy instance to handle changes.
323: *
324: * @param mutator the mutator to use
325: */
326: public void setMutator(CollectionMutator mutator) {
327: this .mutator = mutator;
328: }
329:
330: /**
331: * Add these Collections to the list of collections in this composite
332: *
333: * @param comps Collections to be appended to the composite
334: */
335: public void addComposited(Collection[] comps) {
336: ArrayList list = new ArrayList(Arrays.asList(this .all));
337: list.addAll(Arrays.asList(comps));
338: all = (Collection[]) list.toArray(new Collection[list.size()]);
339: }
340:
341: /**
342: * Add an additional collection to this composite.
343: *
344: * @param c the collection to add
345: */
346: public void addComposited(Collection c) {
347: this .addComposited(new Collection[] { c });
348: }
349:
350: /**
351: * Add two additional collections to this composite.
352: *
353: * @param c the first collection to add
354: * @param d the second collection to add
355: */
356: public void addComposited(Collection c, Collection d) {
357: this .addComposited(new Collection[] { c, d });
358: }
359:
360: /**
361: * Removes a collection from the those being decorated in this composite.
362: *
363: * @param coll collection to be removed
364: */
365: public void removeComposited(Collection coll) {
366: ArrayList list = new ArrayList(this .all.length);
367: list.addAll(Arrays.asList(this .all));
368: list.remove(coll);
369: this .all = (Collection[]) list.toArray(new Collection[list
370: .size()]);
371: }
372:
373: /**
374: * Returns a new collection containing all of the elements
375: *
376: * @return A new ArrayList containing all of the elements in this composite.
377: * The new collection is <i>not</i> backed by this composite.
378: */
379: public Collection toCollection() {
380: return new ArrayList(this );
381: }
382:
383: /**
384: * Gets the collections being decorated.
385: *
386: * @return Unmodifiable collection of all collections in this composite.
387: */
388: public Collection getCollections() {
389: return UnmodifiableList.decorate(Arrays.asList(this .all));
390: }
391:
392: //-----------------------------------------------------------------------
393: /**
394: * Pluggable strategy to handle changes to the composite.
395: */
396: public interface CollectionMutator {
397:
398: /**
399: * Called when an object is to be added to the composite.
400: *
401: * @param composite the CompositeCollection being changed
402: * @param collections all of the Collection instances in this CompositeCollection
403: * @param obj the object being added
404: * @return true if the collection is changed
405: * @throws UnsupportedOperationException if add is unsupported
406: * @throws ClassCastException if the object cannot be added due to its type
407: * @throws NullPointerException if the object cannot be added because its null
408: * @throws IllegalArgumentException if the object cannot be added
409: */
410: public boolean add(CompositeCollection composite,
411: Collection[] collections, Object obj);
412:
413: /**
414: * Called when a collection is to be added to the composite.
415: *
416: * @param composite the CompositeCollection being changed
417: * @param collections all of the Collection instances in this CompositeCollection
418: * @param coll the collection being added
419: * @return true if the collection is changed
420: * @throws UnsupportedOperationException if add is unsupported
421: * @throws ClassCastException if the object cannot be added due to its type
422: * @throws NullPointerException if the object cannot be added because its null
423: * @throws IllegalArgumentException if the object cannot be added
424: */
425: public boolean addAll(CompositeCollection composite,
426: Collection[] collections, Collection coll);
427:
428: /**
429: * Called when an object is to be removed to the composite.
430: *
431: * @param composite the CompositeCollection being changed
432: * @param collections all of the Collection instances in this CompositeCollection
433: * @param obj the object being removed
434: * @return true if the collection is changed
435: * @throws UnsupportedOperationException if removed is unsupported
436: * @throws ClassCastException if the object cannot be removed due to its type
437: * @throws NullPointerException if the object cannot be removed because its null
438: * @throws IllegalArgumentException if the object cannot be removed
439: */
440: public boolean remove(CompositeCollection composite,
441: Collection[] collections, Object obj);
442:
443: }
444:
445: }
|