package de.invesdwin.util.collections.concurrent; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.lang3.BooleanUtils; import de.invesdwin.util.collections.ADelegateCollection; import de.invesdwin.util.collections.iterable.EmptyCloseableIterator; import de.invesdwin.util.collections.iterable.ICloseableIterable; import de.invesdwin.util.collections.iterable.ICloseableIterator; import de.invesdwin.util.error.FastNoSuchElementException; /** * You might want to use AFastIterableDelegateMap for a faster alternative to this implementation for read heavy * situations. */ @ThreadSafe public abstract class AConcurrentModificationDelegateSet<E> extends ADelegateCollection<E> implements ICloseableIterable<E>, Set<E> { @GuardedBy("this") private final AtomicLong openIterators = new AtomicLong(0L); @GuardedBy("this") private final Map<E, Boolean> tasks_add = new LinkedHashMap<E, Boolean>(); @GuardedBy("this") private boolean empty = true; @Override protected abstract Set<E> newDelegate(); @Override public synchronized boolean add(final E e) { if (openIterators.get() == 0) { final boolean added = super.add(e); empty = false; return added; } else { return !contains(e) && BooleanUtils.isNotTrue(tasks_add.put(e, true)); } } @Override public boolean addAll(final Collection<? extends E> c) { boolean changed = false; for (final E e : c) { if (add(e)) { changed = true; } } return changed; } @SuppressWarnings("unchecked") @Override public synchronized boolean remove(final Object o) { if (openIterators.get() == 0) { final boolean removed = super.remove(o); empty = super.isEmpty(); return removed; } else { return contains(o) && BooleanUtils.isNotFalse(tasks_add.put((E) o, false)); } } @Override public void clear() { super.clear(); empty = true; } @Override public boolean removeAll(final Collection<?> c) { boolean changed = false; for (final Object e : c) { if (remove(e)) { changed = true; } } return changed; } @Override public synchronized boolean isEmpty() { return empty; } @Override public ICloseableIterator<E> iterator() { if (isEmpty()) { return EmptyCloseableIterator.getInstance(); } final Iterator<E> delegate = super.iterator(); synchronized (this) { openIterators.incrementAndGet(); } return new ICloseableIterator<E>() { private final boolean closed = false; @Override public void close() { if (closed) { return; } synchronized (AConcurrentModificationDelegateSet.this) { if (openIterators.decrementAndGet() == 0) { for (final Entry<E, Boolean> e : tasks_add.entrySet()) { if (e.getValue()) { AConcurrentModificationDelegateSet.this.getDelegate().add(e.getKey()); } else { AConcurrentModificationDelegateSet.this.getDelegate().remove(e.getKey()); } } tasks_add.clear(); } } } @Override public boolean hasNext() { final boolean hasNext = delegate.hasNext(); if (!hasNext) { close(); } return hasNext; } @Override public E next() { try { return delegate.next(); } catch (final NoSuchElementException e) { close(); throw FastNoSuchElementException.maybeReplace(e, "AConcurrentModificationDelegateSet: next threw"); } } }; } }