package com.sri.ai.util.collect; import java.util.Collection; import java.util.Iterator; import com.sri.ai.util.base.NullaryFunction; /** * A {@link Collection} implementation meant to behave as a copy of a given base B, which must be a Collection type. * However, it only performs the copy upon modification, using the base Collection for reading meanwhile. * When the copy is performed, the default constructor of a given Collection implementation will be used. * IMPORTANT: this collection will reflect any changes made to the base Collection while it is not copied, * so this is best used on Collections not meant to change after being used as a base. */ public class CopyOnWriteCollection<E, B extends Collection<E>> implements Collection<E> { protected B baseCollection; private boolean ownsBase; private NullaryFunction<B> maker; public CopyOnWriteCollection(B baseCollection, NullaryFunction<B> maker) { this.baseCollection = baseCollection; this.ownsBase = false; this.maker = maker; } /** * Returns the base collection being reflected, which will <i>not</i> be * the originally given one if a copy has already been performed. * @return the base collection. */ public Collection<E> getBaseCollection() { return baseCollection; } private void copy() { if ( ! ownsBase) { B newBaseCollection = maker.apply(); newBaseCollection.addAll(baseCollection); baseCollection = newBaseCollection; ownsBase = true; } } @Override public boolean add(E e) { copy(); return baseCollection.add(e); } @Override public boolean addAll(Collection<? extends E> c) { copy(); return baseCollection.addAll(c); } @Override public void clear() { copy(); baseCollection.clear(); } @Override public boolean contains(Object o) { return baseCollection.contains(o); } @Override public boolean containsAll(Collection<?> c) { return baseCollection.containsAll(c); } @Override public boolean isEmpty() { return baseCollection.isEmpty(); } @Override public Iterator<E> iterator() { final Iterator<E> baseIterator = baseCollection.iterator(); Iterator<E> result = new Iterator<E>() { @Override public boolean hasNext() { return baseIterator.hasNext(); } @Override public E next() { return baseIterator.next(); } @Override public void remove() { // overriding for the cases in which base collection does support 'remove'. throw new UnsupportedOperationException("Iterator's remove method not supported for CopyOnWriteCollection instances."); } }; return result; } @Override public boolean remove(Object o) { copy(); return baseCollection.remove(o); } @Override public boolean removeAll(Collection<?> c) { copy(); return baseCollection.removeAll(c); } @Override public boolean retainAll(Collection<?> c) { copy(); return baseCollection.retainAll(c); } @Override public int size() { return baseCollection.size(); } @Override public Object[] toArray() { //no need to copy since Collection.toArray returns a freshly instantiated array. return baseCollection.toArray(); } @Override public <T> T[] toArray(T[] a) { return baseCollection.toArray(a); } @Override public boolean equals(Object another) { if (another instanceof CopyOnWriteCollection) { another = ((CopyOnWriteCollection) another).baseCollection; } return baseCollection.equals(another); } @Override public int hashCode() { return baseCollection.hashCode(); } @Override public String toString() { return baseCollection.toString(); } }