blob: ddfb4e474eadf1068dcb2c5b3d6c61148d5dc65b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2019 EclipseSource Muenchen GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* nicole.behlen - initial API and implementation
******************************************************************************/
package org.eclipse.emf.ecp.view.internal.validation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* An extension of {@link ConcurrentLinkedQueue} that additionally contains a set to avoid unnecessary
* duplicated entries in cases of non-concurrent additions.
* Duplication may still occur if multiple threads enqueue the same objects.
*
* @author nicole.behlen
*
* @param <E> the generic type of the elements to be queued.
*/
public class ConcurrentLinkedSetQueue<E> extends ConcurrentLinkedQueue<E> {
private static final long serialVersionUID = 290189755090394151L;
private final Set<Object> concurrentSet = Collections.newSetFromMap(new ConcurrentHashMap<Object, Boolean>());
@Override
public void clear() {
super.clear();
concurrentSet.clear();
}
@Override
public boolean removeAll(Collection<?> c) {
final boolean result = super.removeAll(c);
concurrentSet.removeAll(c);
return result;
}
@Override
public boolean retainAll(Collection<?> c) {
final boolean result = super.retainAll(c);
concurrentSet.retainAll(c);
return result;
}
/**
* Adds the object to the tail of this queue, if not already contained.
*
* @param e adds if not already contained the object to the tail of this queue
* @return <code>false</code> if object is already contained in queue, <code>true</code> otherwise.
* @throws NullPointerException - if the specified element is null
*/
@Override
public boolean offer(E e) {
if (e == null) {
throw new NullPointerException();
}
if (!concurrentSet.contains(e)) {
concurrentSet.add(e);
return super.offer(e);
}
return false;
}
@Override
public E poll() {
final E next = super.poll();
if (next != null) {
concurrentSet.remove(next);
}
return next;
}
@Override
public boolean remove(Object o) {
if (o != null) {
concurrentSet.remove(o);
}
return super.remove(o);
}
@Override
public boolean addAll(Collection<? extends E> c) {
if (c == this) {
// As historically specified in AbstractQueue#addAll
throw new IllegalArgumentException();
}
final List<E> newColl = new ArrayList<>(c);
newColl.removeAll(concurrentSet);
concurrentSet.addAll(newColl);
return super.addAll(newColl);
}
@Override
public Iterator<E> iterator() {
final Iterator<E> iter = super.iterator();
return new IterWithRemove<>(iter);
}
/**
*
* Iterator over the elements of this queue in proper sequence, that removes
* the elements from the {@link ConcurrentLinkedSetQueue#concurrentSet}.
*
* @author nicole.behlen
*
* @param <I> the generic type of the elements to iterate.
*/
private class IterWithRemove<I> implements Iterator<I> {
private final Iterator<I> delegate;
private I lastReturnedElement;
/**
* Constructor setting the original iterator.
*/
IterWithRemove(Iterator<I> delegate) {
this.delegate = delegate;
}
@Override
public boolean hasNext() {
return this.delegate.hasNext();
}
@Override
public I next() {
if (this.delegate.hasNext()) {
lastReturnedElement = this.delegate.next();
return lastReturnedElement;
}
lastReturnedElement = null;
throw new NoSuchElementException();
}
@Override
public void remove() {
if (lastReturnedElement != null) {
concurrentSet.remove(lastReturnedElement);
lastReturnedElement = null;
}
delegate.remove();
}
}
}