1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.collections.collection;
18
19 import java.io.Serializable;
20 import java.lang.reflect.Array;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.List;
26
27 import org.apache.commons.collections.iterators.EmptyIterator;
28 import org.apache.commons.collections.iterators.IteratorChain;
29 import org.apache.commons.collections.list.UnmodifiableList;
30
31 /**
32 * Decorates a collection of other collections to provide a single unified view.
33 * <p>
34 * Changes made to this collection will actually be made on the decorated collection.
35 * Add and remove operations require the use of a pluggable strategy. If no
36 * strategy is provided then add and remove are unsupported.
37 *
38 * @param <E> the type of the elements in the collection
39 * @since 3.0
40 * @version $Id: CompositeCollection.java 1429905 2013-01-07 17:15:14Z ggregory $
41 */
42 public class CompositeCollection<E> implements Collection<E>, Serializable {
43
44 /** Serialization version */
45 private static final long serialVersionUID = 8417515734108306801L;
46
47 /** CollectionMutator to handle changes to the collection */
48 protected CollectionMutator<E> mutator;
49
50 /** Collections in the composite */
51 protected List<Collection<E>> all = new ArrayList<Collection<E>>();
52
53 /**
54 * Create an empty CompositeCollection.
55 */
56 public CompositeCollection() {
57 super();
58 }
59
60 /**
61 * Create a Composite Collection with one collection.
62 *
63 * @param compositeCollection the Collection to be appended to the composite
64 */
65 public CompositeCollection(final Collection<E> compositeCollection) {
66 super();
67 addComposited(compositeCollection);
68 }
69
70 /**
71 * Create a Composite Collection with two collections.
72 *
73 * @param compositeCollection1 the Collection to be appended to the composite
74 * @param compositeCollection2 the Collection to be appended to the composite
75 */
76 public CompositeCollection(final Collection<E> compositeCollection1, final Collection<E> compositeCollection2) {
77 super();
78 addComposited(compositeCollection1, compositeCollection2);
79 }
80
81 /**
82 * Create a Composite Collection with an array of collections.
83 *
84 * @param compositeCollections the collections to composite
85 */
86 public CompositeCollection(final Collection<E>[] compositeCollections) {
87 super();
88 addComposited(compositeCollections);
89 }
90
91 // /**
92 // * Create a Composite Collection extracting the collections from an iterable.
93 // *
94 // * @param compositeCollections the collections to composite
95 // */
96 // public CompositeCollection(Iterable<Collection<E>> compositeCollections) {
97 // super();
98 // addComposited(compositeCollections);
99 // }
100
101 //-----------------------------------------------------------------------
102 /**
103 * Gets the size of this composite collection.
104 * <p>
105 * This implementation calls <code>size()</code> on each collection.
106 *
107 * @return total number of elements in all contained containers
108 */
109 public int size() {
110 int size = 0;
111 for (final Collection<E> item : all) {
112 size += item.size();
113 }
114 return size;
115 }
116
117 /**
118 * Checks whether this composite collection is empty.
119 * <p>
120 * This implementation calls <code>isEmpty()</code> on each collection.
121 *
122 * @return true if all of the contained collections are empty
123 */
124 public boolean isEmpty() {
125 for (final Collection<? extends E> item : all) {
126 if (item.isEmpty() == false) {
127 return false;
128 }
129 }
130 return true;
131 }
132
133 /**
134 * Checks whether this composite collection contains the object.
135 * <p>
136 * This implementation calls <code>contains()</code> on each collection.
137 *
138 * @param obj the object to search for
139 * @return true if obj is contained in any of the contained collections
140 */
141 public boolean contains(final Object obj) {
142 for (final Collection<? extends E> item : all) {
143 if (item.contains(obj)) {
144 return true;
145 }
146 }
147 return false;
148 }
149
150 /**
151 * Gets an iterator over all the collections in this composite.
152 * <p>
153 * This implementation uses an <code>IteratorChain</code>.
154 *
155 * @return an <code>IteratorChain</code> instance which supports
156 * <code>remove()</code>. Iteration occurs over contained collections in
157 * the order they were added, but this behavior should not be relied upon.
158 * @see IteratorChain
159 */
160 public Iterator<E> iterator() {
161 if (all.isEmpty()) {
162 return EmptyIterator.<E>emptyIterator();
163 }
164 final IteratorChain<E> chain = new IteratorChain<E>();
165 for (final Collection<? extends E> item : all) {
166 chain.addIterator(item.iterator());
167 }
168 return chain;
169 }
170
171 /**
172 * Returns an array containing all of the elements in this composite.
173 *
174 * @return an object array of all the elements in the collection
175 */
176 public Object[] toArray() {
177 final Object[] result = new Object[size()];
178 int i = 0;
179 for (final Iterator<E> it = iterator(); it.hasNext(); i++) {
180 result[i] = it.next();
181 }
182 return result;
183 }
184
185 /**
186 * Returns an object array, populating the supplied array if possible.
187 * See <code>Collection</code> interface for full details.
188 *
189 * @param <T> the type of the elements in the collection
190 * @param array the array to use, populating if possible
191 * @return an array of all the elements in the collection
192 */
193 @SuppressWarnings("unchecked")
194 public <T> T[] toArray(final T[] array) {
195 final int size = size();
196 Object[] result = null;
197 if (array.length >= size) {
198 result = array;
199 } else {
200 result = (Object[]) Array.newInstance(array.getClass().getComponentType(), size);
201 }
202
203 int offset = 0;
204 for (final Collection<? extends E> item : all) {
205 for (final E e : item) {
206 result[offset++] = e;
207 }
208 }
209 if (result.length > size) {
210 result[size] = null;
211 }
212 return (T[]) result;
213 }
214
215 /**
216 * Adds an object to the collection, throwing UnsupportedOperationException
217 * unless a CollectionMutator strategy is specified.
218 *
219 * @param obj the object to add
220 * @return {@code true} if the collection was modified
221 * @throws UnsupportedOperationException if CollectionMutator hasn't been set
222 * @throws UnsupportedOperationException if add is unsupported
223 * @throws ClassCastException if the object cannot be added due to its type
224 * @throws NullPointerException if the object cannot be added because its null
225 * @throws IllegalArgumentException if the object cannot be added
226 */
227 public boolean add(final E obj) {
228 if (mutator == null) {
229 throw new UnsupportedOperationException(
230 "add() is not supported on CompositeCollection without a CollectionMutator strategy");
231 }
232 return mutator.add(this, all, obj);
233 }
234
235 /**
236 * Removes an object from the collection, throwing UnsupportedOperationException
237 * unless a CollectionMutator strategy is specified.
238 *
239 * @param obj the object being removed
240 * @return true if the collection is changed
241 * @throws UnsupportedOperationException if removed is unsupported
242 * @throws ClassCastException if the object cannot be removed due to its type
243 * @throws NullPointerException if the object cannot be removed because its null
244 * @throws IllegalArgumentException if the object cannot be removed
245 */
246 public boolean remove(final Object obj) {
247 if (mutator == null) {
248 throw new UnsupportedOperationException(
249 "remove() is not supported on CompositeCollection without a CollectionMutator strategy");
250 }
251 return mutator.remove(this, all, obj);
252 }
253
254 /**
255 * Checks whether this composite contains all the elements in the specified collection.
256 * <p>
257 * This implementation calls <code>contains()</code> for each element in the
258 * specified collection.
259 *
260 * @param coll the collection to check for
261 * @return true if all elements contained
262 */
263 public boolean containsAll(final Collection<?> coll) {
264 for (final Object item : coll) {
265 if (contains(item) == false) {
266 return false;
267 }
268 }
269 return true;
270 }
271
272 /**
273 * Adds a collection of elements to this collection, throwing
274 * UnsupportedOperationException unless a CollectionMutator strategy is specified.
275 *
276 * @param coll the collection to add
277 * @return true if the collection was modified
278 * @throws UnsupportedOperationException if CollectionMutator hasn't been set
279 * @throws UnsupportedOperationException if add is unsupported
280 * @throws ClassCastException if the object cannot be added due to its type
281 * @throws NullPointerException if the object cannot be added because its null
282 * @throws IllegalArgumentException if the object cannot be added
283 */
284 public boolean addAll(final Collection<? extends E> coll) {
285 if (mutator == null) {
286 throw new UnsupportedOperationException(
287 "addAll() is not supported on CompositeCollection without a CollectionMutator strategy");
288 }
289 return mutator.addAll(this, all, coll);
290 }
291
292 /**
293 * Removes the elements in the specified collection from this composite collection.
294 * <p>
295 * This implementation calls <code>removeAll</code> on each collection.
296 *
297 * @param coll the collection to remove
298 * @return true if the collection was modified
299 * @throws UnsupportedOperationException if removeAll is unsupported
300 */
301 public boolean removeAll(final Collection<?> coll) {
302 if (coll.size() == 0) {
303 return false;
304 }
305 boolean changed = false;
306 for (final Collection<? extends E> item : all) {
307 changed |= item.removeAll(coll);
308 }
309 return changed;
310 }
311
312 /**
313 * Retains all the elements in the specified collection in this composite collection,
314 * removing all others.
315 * <p>
316 * This implementation calls <code>retainAll()</code> on each collection.
317 *
318 * @param coll the collection to remove
319 * @return true if the collection was modified
320 * @throws UnsupportedOperationException if retainAll is unsupported
321 */
322 public boolean retainAll(final Collection<?> coll) {
323 boolean changed = false;
324 for (final Collection<? extends E> item : all) {
325 changed |= item.retainAll(coll);
326 }
327 return changed;
328 }
329
330 /**
331 * Removes all of the elements from this collection .
332 * <p>
333 * This implementation calls <code>clear()</code> on each collection.
334 *
335 * @throws UnsupportedOperationException if clear is unsupported
336 */
337 public void clear() {
338 for (final Collection<? extends E> coll : all) {
339 coll.clear();
340 }
341 }
342
343 //-----------------------------------------------------------------------
344 /**
345 * Specify a CollectionMutator strategy instance to handle changes.
346 *
347 * @param mutator the mutator to use
348 */
349 public void setMutator(final CollectionMutator<E> mutator) {
350 this.mutator = mutator;
351 }
352
353 /**
354 * Add these Collections to the list of collections in this composite
355 *
356 * @param compositeCollection the Collection to be appended to the composite
357 */
358 public void addComposited(final Collection<E> compositeCollection) {
359 all.add(compositeCollection);
360 }
361
362 /**
363 * Add these Collections to the list of collections in this composite
364 *
365 * @param compositeCollection1 the Collection to be appended to the composite
366 * @param compositeCollection2 the Collection to be appended to the composite
367 */
368 public void addComposited(final Collection<E> compositeCollection1, final Collection<E> compositeCollection2) {
369 all.add(compositeCollection1);
370 all.add(compositeCollection2);
371 }
372
373 /**
374 * Add these Collections to the list of collections in this composite
375 *
376 * @param compositeCollections the Collections to be appended to the composite
377 */
378 public void addComposited(final Collection<E>[] compositeCollections) {
379 all.addAll(Arrays.asList(compositeCollections));
380 }
381
382 // /**
383 // * Add these Collections to the list of collections in this composite
384 // *
385 // * @param compositeCollections the Collections to be appended to the composite
386 // */
387 // public void addComposited(Iterable<Collection<E>> compositeCollections) {
388 // for (Collection<E> item : compositeCollections) {
389 // all.add(item);
390 // }
391 // }
392
393 /**
394 * Removes a collection from the those being decorated in this composite.
395 *
396 * @param coll collection to be removed
397 */
398 public void removeComposited(final Collection<E> coll) {
399 all.remove(coll);
400 }
401
402 //-----------------------------------------------------------------------
403 /**
404 * Returns a new collection containing all of the elements
405 *
406 * @return A new ArrayList containing all of the elements in this composite.
407 * The new collection is <i>not</i> backed by this composite.
408 */
409 public Collection<E> toCollection() {
410 return new ArrayList<E>(this);
411 }
412
413 /**
414 * Gets the collections being decorated.
415 *
416 * @return Unmodifiable list of all collections in this composite.
417 */
418 public List<? extends Collection<E>> getCollections() {
419 return UnmodifiableList.unmodifiableList(all);
420 }
421
422 /**
423 * Get the collection mutator to be used for this CompositeCollection.
424 * @return CollectionMutator<E>
425 */
426 protected CollectionMutator<E> getMutator() {
427 return mutator;
428 }
429
430 //-----------------------------------------------------------------------
431 /**
432 * Pluggable strategy to handle changes to the composite.
433 *
434 * @param <E> the element being held in the collection
435 */
436 public interface CollectionMutator<E> extends Serializable {
437
438 /**
439 * Called when an object is to be added to the composite.
440 *
441 * @param composite the CompositeCollection being changed
442 * @param collections all of the Collection instances in this CompositeCollection
443 * @param obj the object being added
444 * @return true if the collection is changed
445 * @throws UnsupportedOperationException if add is unsupported
446 * @throws ClassCastException if the object cannot be added due to its type
447 * @throws NullPointerException if the object cannot be added because its null
448 * @throws IllegalArgumentException if the object cannot be added
449 */
450 public boolean add(CompositeCollection<E> composite, List<Collection<E>> collections, E obj);
451
452 /**
453 * Called when a collection is to be added to the composite.
454 *
455 * @param composite the CompositeCollection being changed
456 * @param collections all of the Collection instances in this CompositeCollection
457 * @param coll the collection being added
458 * @return true if the collection is changed
459 * @throws UnsupportedOperationException if add is unsupported
460 * @throws ClassCastException if the object cannot be added due to its type
461 * @throws NullPointerException if the object cannot be added because its null
462 * @throws IllegalArgumentException if the object cannot be added
463 */
464 public boolean addAll(CompositeCollection<E> composite,
465 List<Collection<E>> collections,
466 Collection<? extends E> coll);
467
468 /**
469 * Called when an object is to be removed to the composite.
470 *
471 * @param composite the CompositeCollection being changed
472 * @param collections all of the Collection instances in this CompositeCollection
473 * @param obj the object being removed
474 * @return true if the collection is changed
475 * @throws UnsupportedOperationException if removed is unsupported
476 * @throws ClassCastException if the object cannot be removed due to its type
477 * @throws NullPointerException if the object cannot be removed because its null
478 * @throws IllegalArgumentException if the object cannot be removed
479 */
480 public boolean remove(CompositeCollection<E> composite, List<Collection<E>> collections, Object obj);
481
482 }
483
484 }
485