org.apache.commons.collections4.collection.CompositeCollection Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.collections4.collection;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.iterators.EmptyIterator;
import org.apache.commons.collections4.iterators.IteratorChain;
import org.apache.commons.collections4.list.UnmodifiableList;
/**
* Decorates a collection of other collections to provide a single unified view.
*
* Changes made to this collection will actually be made on the decorated collection.
* Add and remove operations require the use of a pluggable strategy. If no
* strategy is provided then add and remove are unsupported.
*
* @param the type of the elements in the collection
* @since 3.0
*/
public class CompositeCollection implements Collection, Serializable {
/** Serialization version */
private static final long serialVersionUID = 8417515734108306801L;
/** CollectionMutator to handle changes to the collection */
private CollectionMutator mutator;
/** Collections in the composite */
private final List> all = new ArrayList<>();
/**
* Create an empty CompositeCollection.
*/
public CompositeCollection() {
super();
}
/**
* Create a Composite Collection with one collection.
*
* @param compositeCollection the Collection to be appended to the composite
*/
public CompositeCollection(final Collection compositeCollection) {
super();
addComposited(compositeCollection);
}
/**
* Create a Composite Collection with two collections.
*
* @param compositeCollection1 the Collection to be appended to the composite
* @param compositeCollection2 the Collection to be appended to the composite
*/
public CompositeCollection(final Collection compositeCollection1,
final Collection compositeCollection2) {
super();
addComposited(compositeCollection1, compositeCollection2);
}
/**
* Create a Composite Collection with an array of collections.
*
* @param compositeCollections the collections to composite
*/
public CompositeCollection(final Collection... compositeCollections) {
super();
addComposited(compositeCollections);
}
//-----------------------------------------------------------------------
/**
* Gets the size of this composite collection.
*
* This implementation calls size()
on each collection.
*
* @return total number of elements in all contained containers
*/
@Override
public int size() {
int size = 0;
for (final Collection item : all) {
size += item.size();
}
return size;
}
/**
* Checks whether this composite collection is empty.
*
* This implementation calls isEmpty()
on each collection.
*
* @return true if all of the contained collections are empty
*/
@Override
public boolean isEmpty() {
for (final Collection item : all) {
if (item.isEmpty() == false) {
return false;
}
}
return true;
}
/**
* Checks whether this composite collection contains the object.
*
* This implementation calls contains()
on each collection.
*
* @param obj the object to search for
* @return true if obj is contained in any of the contained collections
*/
@Override
public boolean contains(final Object obj) {
for (final Collection item : all) {
if (item.contains(obj)) {
return true;
}
}
return false;
}
/**
* Gets an iterator over all the collections in this composite.
*
* This implementation uses an IteratorChain
.
*
* @return an IteratorChain
instance which supports
* remove()
. Iteration occurs over contained collections in
* the order they were added, but this behavior should not be relied upon.
* @see IteratorChain
*/
@Override
public Iterator iterator() {
if (all.isEmpty()) {
return EmptyIterator.emptyIterator();
}
final IteratorChain chain = new IteratorChain<>();
for (final Collection item : all) {
chain.addIterator(item.iterator());
}
return chain;
}
/**
* Returns an array containing all of the elements in this composite.
*
* @return an object array of all the elements in the collection
*/
@Override
public Object[] toArray() {
final Object[] result = new Object[size()];
int i = 0;
for (final Iterator it = iterator(); it.hasNext(); i++) {
result[i] = it.next();
}
return result;
}
/**
* Returns an object array, populating the supplied array if possible.
* See Collection
interface for full details.
*
* @param the type of the elements in the collection
* @param array the array to use, populating if possible
* @return an array of all the elements in the collection
*/
@Override
@SuppressWarnings("unchecked")
public T[] toArray(final T[] array) {
final int size = size();
Object[] result = null;
if (array.length >= size) {
result = array;
} else {
result = (Object[]) Array.newInstance(array.getClass().getComponentType(), size);
}
int offset = 0;
for (final Collection item : all) {
for (final E e : item) {
result[offset++] = e;
}
}
if (result.length > size) {
result[size] = null;
}
return (T[]) result;
}
/**
* Adds an object to the collection, throwing UnsupportedOperationException
* unless a CollectionMutator strategy is specified.
*
* @param obj the object to add
* @return {@code true} if the collection was modified
* @throws UnsupportedOperationException if CollectionMutator hasn't been set
* @throws UnsupportedOperationException if add is unsupported
* @throws ClassCastException if the object cannot be added due to its type
* @throws NullPointerException if the object cannot be added because its null
* @throws IllegalArgumentException if the object cannot be added
*/
@Override
public boolean add(final E obj) {
if (mutator == null) {
throw new UnsupportedOperationException(
"add() is not supported on CompositeCollection without a CollectionMutator strategy");
}
return mutator.add(this, all, obj);
}
/**
* Removes an object from the collection, throwing UnsupportedOperationException
* unless a CollectionMutator strategy is specified.
*
* @param obj the object being removed
* @return true if the collection is changed
* @throws UnsupportedOperationException if removed is unsupported
* @throws ClassCastException if the object cannot be removed due to its type
* @throws NullPointerException if the object cannot be removed because its null
* @throws IllegalArgumentException if the object cannot be removed
*/
@Override
public boolean remove(final Object obj) {
if (mutator == null) {
throw new UnsupportedOperationException(
"remove() is not supported on CompositeCollection without a CollectionMutator strategy");
}
return mutator.remove(this, all, obj);
}
/**
* Checks whether this composite contains all the elements in the specified collection.
*
* This implementation calls contains()
for each element in the
* specified collection.
*
* @param coll the collection to check for
* @return true if all elements contained
*/
@Override
public boolean containsAll(final Collection> coll) {
if (coll == null) {
return false;
}
for (final Object item : coll) {
if (contains(item) == false) {
return false;
}
}
return true;
}
/**
* Adds a collection of elements to this collection, throwing
* UnsupportedOperationException unless a CollectionMutator strategy is specified.
*
* @param coll the collection to add
* @return true if the collection was modified
* @throws UnsupportedOperationException if CollectionMutator hasn't been set
* @throws UnsupportedOperationException if add is unsupported
* @throws ClassCastException if the object cannot be added due to its type
* @throws NullPointerException if the object cannot be added because its null
* @throws IllegalArgumentException if the object cannot be added
*/
@Override
public boolean addAll(final Collection extends E> coll) {
if (mutator == null) {
throw new UnsupportedOperationException(
"addAll() is not supported on CompositeCollection without a CollectionMutator strategy");
}
return mutator.addAll(this, all, coll);
}
/**
* Removes the elements in the specified collection from this composite collection.
*
* This implementation calls removeAll
on each collection.
*
* @param coll the collection to remove
* @return true if the collection was modified
* @throws UnsupportedOperationException if removeAll is unsupported
*/
@Override
public boolean removeAll(final Collection> coll) {
if (CollectionUtils.isEmpty(coll)) {
return false;
}
boolean changed = false;
for (final Collection item : all) {
changed |= item.removeAll(coll);
}
return changed;
}
/**
* Removes all of the elements of this collection that satisfy the given predicate from this composite collection.
*
* This implementation calls removeIf
on each collection.
*
* @param filter a predicate which returns true for elements to be removed
* @return true if the collection was modified
* @throws UnsupportedOperationException if removeIf is unsupported
* @since 4.4
*/
@Override
public boolean removeIf(final Predicate super E> filter) {
if (Objects.isNull(filter)) {
return false;
}
boolean changed = false;
for (final Collection item : all) {
changed |= item.removeIf(filter);
}
return changed;
}
/**
* Retains all the elements in the specified collection in this composite collection,
* removing all others.
*
* This implementation calls retainAll()
on each collection.
*
* @param coll the collection to remove
* @return true if the collection was modified
* @throws UnsupportedOperationException if retainAll is unsupported
*/
@Override
public boolean retainAll(final Collection> coll) {
boolean changed = false;
if (coll != null) {
for (final Collection item : all) {
changed |= item.retainAll(coll);
}
}
return changed;
}
/**
* Removes all of the elements from this collection .
*
* This implementation calls clear()
on each collection.
*
* @throws UnsupportedOperationException if clear is unsupported
*/
@Override
public void clear() {
for (final Collection coll : all) {
coll.clear();
}
}
//-----------------------------------------------------------------------
/**
* Specify a CollectionMutator strategy instance to handle changes.
*
* @param mutator the mutator to use
*/
public void setMutator(final CollectionMutator mutator) {
this.mutator = mutator;
}
/**
* Add these Collections to the list of collections in this composite
*
* @param compositeCollection the Collection to be appended to the composite
*/
public void addComposited(final Collection compositeCollection) {
if (compositeCollection != null) {
all.add(compositeCollection);
}
}
/**
* Add these Collections to the list of collections in this composite
*
* @param compositeCollection1 the Collection to be appended to the composite
* @param compositeCollection2 the Collection to be appended to the composite
*/
public void addComposited(final Collection compositeCollection1,
final Collection compositeCollection2) {
if (compositeCollection1 != null) {
all.add(compositeCollection1);
}
if (compositeCollection2 != null) {
all.add(compositeCollection2);
}
}
/**
* Add these Collections to the list of collections in this composite
*
* @param compositeCollections the Collections to be appended to the composite
*/
public void addComposited(final Collection... compositeCollections) {
for (Collection compositeCollection : compositeCollections) {
if (compositeCollection != null) {
all.add(compositeCollection);
}
}
}
/**
* Removes a collection from the those being decorated in this composite.
*
* @param coll collection to be removed
*/
public void removeComposited(final Collection coll) {
all.remove(coll);
}
//-----------------------------------------------------------------------
/**
* Returns a new collection containing all of the elements
*
* @return A new ArrayList containing all of the elements in this composite.
* The new collection is not backed by this composite.
*/
public Collection toCollection() {
return new ArrayList<>(this);
}
/**
* Gets the collections being decorated.
*
* @return Unmodifiable list of all collections in this composite.
*/
public List> getCollections() {
return UnmodifiableList.unmodifiableList(all);
}
/**
* Get the collection mutator to be used for this CompositeCollection.
* @return CollectionMutator<E>
*/
protected CollectionMutator getMutator() {
return mutator;
}
//-----------------------------------------------------------------------
/**
* Pluggable strategy to handle changes to the composite.
*
* @param the element being held in the collection
*/
public interface CollectionMutator extends Serializable {
/**
* Called when an object is to be added to the composite.
*
* @param composite the CompositeCollection being changed
* @param collections all of the Collection instances in this CompositeCollection
* @param obj the object being added
* @return true if the collection is changed
* @throws UnsupportedOperationException if add is unsupported
* @throws ClassCastException if the object cannot be added due to its type
* @throws NullPointerException if the object cannot be added because its null
* @throws IllegalArgumentException if the object cannot be added
*/
boolean add(CompositeCollection composite, List> collections, E obj);
/**
* Called when a collection is to be added to the composite.
*
* @param composite the CompositeCollection being changed
* @param collections all of the Collection instances in this CompositeCollection
* @param coll the collection being added
* @return true if the collection is changed
* @throws UnsupportedOperationException if add is unsupported
* @throws ClassCastException if the object cannot be added due to its type
* @throws NullPointerException if the object cannot be added because its null
* @throws IllegalArgumentException if the object cannot be added
*/
boolean addAll(CompositeCollection composite,
List> collections,
Collection extends E> coll);
/**
* Called when an object is to be removed to the composite.
*
* @param composite the CompositeCollection being changed
* @param collections all of the Collection instances in this CompositeCollection
* @param obj the object being removed
* @return true if the collection is changed
* @throws UnsupportedOperationException if removed is unsupported
* @throws ClassCastException if the object cannot be removed due to its type
* @throws NullPointerException if the object cannot be removed because its null
* @throws IllegalArgumentException if the object cannot be removed
*/
boolean remove(CompositeCollection composite,
List> collections,
Object obj);
}
}