/******************************************************************************
* Copyright (c) 2006, 2010 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
* is available at http://www.opensource.org/licenses/apache2.0.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* VMware Inc.
*****************************************************************************/
package org.eclipse.gemini.blueprint.service.importer.support.internal.collection;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.WeakHashMap;
/**
* Collection which can be increased or reduced at runtime while iterating.
* Iterators returned by this implementation are consistent - it is guaranteed
* that {@link Iterator#next()} will obey the result of the previously called
* {@link Iterator#hasNext()} even though the collection content has been
* modified.
*
* This collection is thread-safe with the condition that there is at most one
* writing thread at a point in time. There are no restrains on the number of
* readers.
*
* @author Costin Leau
*
*/
public class DynamicCollection<E> extends AbstractCollection<E> {
/**
* Dynamic <strong>consistent</strong> iterator. This iterator is not
* thread-safe with respect to iteration (it should not be shared against
* multiple threads) but it is thread safe with respect to the backing
* storage which might be modified during the iterator life cycle.
*
* @author Costin Leau
*/
protected class DynamicIterator implements Iterator<E> {
/**
* Cursor pointing to the element that has to be returned by
* {@link #next()} method. This element needs to be synchronized.
*/
protected volatile int cursor = 0;
/**
* Temporary object holder. Used in case the last element in the
* collection becomes empty after an iterator #hasNext() method was
* called but before #next() is invoked since otherwise the iterator
* needs to return an element but it cannot.
*
* Subclasses (such as lists) should implement their own strategy when
* it comes to assign a value to it to accommodate the collection
* semantics (ordered vs indexed).
*
* This particular field represents the tail of the collection, since no
* order or indexing is enforced and the iteration can only go forward.
*
*
* The field is assigned when an object is removed and resetted by calls
* to hasNext() or next().
*
* Thread-safety note: Since this object is affected by the storage
* shrinking it needs to be synchronized.
*/
protected volatile E tailGhost = null;
/**
* Lock protecting the cursor and tailGhost which might be affected by
* the backing collection shrinking.
*/
protected final Object lock = new Object();
// flag used for enforcing the iterator consistency:
// null - do not enforce anything
// true - should not throw exception
// false - should throw exception
/**
* Iterator variable - not thread-safe/synchronized since only one
* thread should use the iterator.
*/
protected Boolean hasNext = null;
/**
* Iterator variable - not thread-safe/synchronized since only one
* thread should use the iterator.
*/
protected boolean removalAllowed = false;
public boolean hasNext() {
synchronized (storage) {
synchronized (iteratorsLock) {
synchronized (lock) {
tailGhost = null;
return unsafeHasNext();
}
}
}
}
/**
* Updates the hasNext field.
*
* Internal unprotected method to avoid nested synchronization blocks.
* To execute this code, one needs the storage, iteratorsLock and
* iterator lock.
*
* @return
*/
protected boolean unsafeHasNext() {
hasNext = (cursor < storage.size() ? Boolean.TRUE : Boolean.FALSE);
return hasNext.booleanValue();
}
public E next() {
try {
removalAllowed = true;
// no enforcement
if (hasNext == null) {
synchronized (storage) {
synchronized (iteratorsLock) {
synchronized (lock) {
if (unsafeHasNext())
return storage.get(cursor++);
else
throw new NoSuchElementException();
}
}
}
}
// need to return an object no matter what
else if (hasNext.booleanValue()) {
synchronized (storage) {
synchronized (iteratorsLock) {
synchronized (lock) {
// if there is an element available, return it
if (unsafeHasNext()) {
return storage.get(cursor++);
}
else {
// otherwise return the last one seen
// return tailGhost;
return tailGhost;
}
}
}
}
}
// should throw exception no matter what
else {
throw new NoSuchElementException();
}
}
finally {
// no matter what, reset hasNext
hasNext = null;
// remove ghost object
synchronized (lock) {
tailGhost = null;
}
}
}
public void remove() {
// make sure the cursor is valid
if (removalAllowed) {
removalAllowed = false;
int cursorCopy;
synchronized (lock) {
cursorCopy = cursor;
}
DynamicCollection.this.remove(removalIndex(cursorCopy));
}
else
throw new IllegalStateException();
}
protected int removalIndex(int cursor) {
return cursor - 1;
}
/**
* Removes the object from the underlying collection. This operation is
* relevant to the iterators since it can occur in between
* hasNext()/next() calls. If no object is left, next() is forced to
* return null which can contradict the hasNext() contract. For such a
* case, the iterator is forced to temporarily recall the last available
* object.
*
* @param index
* @param o
*/
void removeObject(int index, E o) {
synchronized (lock) {
tailGhost = o;
}
}
}
/** Lock used by operations that require iterator updates (such as removal) */
/**
* If it interacts with the storage, the *storage* lock needs to be acquired
* first
*/
protected final Object iteratorsLock = new Object();
/** actual collection storage */
/** this list is not-synchronized by default */
protected final List<E> storage;
/** map of weak references to the list iterators */
/**
* should have been a list but there is no 'WeakReference'-based
* implementation in the JDK
*/
protected final Map<DynamicIterator, Object> iterators;
public DynamicCollection() {
this(16);
}
public DynamicCollection(int size) {
storage = new ArrayList<E>(size);
iterators = new WeakHashMap<DynamicIterator, Object>(4);
}
public DynamicCollection(Collection<? extends E> c) {
this(c.size());
addAll(c);
}
public Iterator<E> iterator() {
DynamicIterator iter = new DynamicIterator();
synchronized (iteratorsLock) {
iterators.put(iter, null);
}
return iter;
}
public void clear() {
synchronized (storage) {
storage.clear();
}
}
public int size() {
synchronized (storage) {
return storage.size();
}
}
public boolean add(E o) {
synchronized (storage) {
return storage.add(o);
}
}
public boolean addAll(Collection<? extends E> c) {
synchronized (storage) {
return storage.addAll(c);
}
}
public boolean contains(Object o) {
synchronized (storage) {
return storage.contains(o);
}
}
public boolean containsAll(Collection<?> c) {
synchronized (storage) {
return storage.containsAll(c);
}
}
public boolean isEmpty() {
synchronized (storage) {
return storage.isEmpty();
}
}
public boolean remove(Object o) {
synchronized (storage) {
int index = storage.indexOf(o);
if (index == -1)
return false;
remove(index);
return true;
}
}
// remove an object from the list using the given index
// this is required for cases where the underlying storage (a list) might
// contain duplicates.
protected E remove(int index) {
E o = null;
// first acquire storage lock
synchronized (storage) {
// then the iterator
synchronized (iteratorsLock) {
// update storage
o = storage.remove(index);
// update iterators
for (Iterator<Map.Entry<DynamicIterator, Object>> iter = iterators.entrySet().iterator(); iter.hasNext();) {
Map.Entry<DynamicIterator, Object> entry = iter.next();
DynamicIterator dynamicIterator = entry.getKey();
synchronized (dynamicIterator.lock) {
if (index < dynamicIterator.cursor) {
dynamicIterator.cursor--;
}
// set ghost object (if objects disappear in between hasNext()/next() calls)
else {
dynamicIterator.removeObject(index, o);
}
}
}
}
}
return o;
}
// extra-collection method used by list or sorted set.
// adds an object to the indicated position forcing an update on the
// iterators.
protected void add(int index, E o) {
// update iterators (since items are not added at the end
// anymore)
// first acquire storage lock
synchronized (storage) {
synchronized (iteratorsLock) {
// update storage
storage.add(index, o);
for (Iterator<Map.Entry<DynamicIterator, Object>> iter = iterators.entrySet().iterator(); iter.hasNext();) {
Map.Entry<DynamicIterator, Object> entry = iter.next();
DynamicIterator dynamicIterator = entry.getKey();
synchronized (dynamicIterator.lock) {
if (index < dynamicIterator.cursor)
dynamicIterator.cursor++;
}
}
}
}
}
public Object[] toArray() {
synchronized (storage) {
return storage.toArray();
}
}
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
synchronized (storage) {
return storage.toArray((T[]) new Object[storage.size()]);
}
}
public String toString() {
synchronized (storage) {
return storage.toString();
}
}
/**
* Hook used by wrapping collections to determine the position of the object
* being removed while iterating.
*
* @param o
* @return
*/
protected int indexOf(Object o) {
synchronized (storage) {
return storage.indexOf(o);
}
}
}