View Javadoc

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