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