/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed 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.springframework.integration.dispatcher;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* Special Set that maintains the following semantics:
* All elements that are un-ordered (do not implement {@link Ordered} interface or annotated
* {@link Order} annotation) will be stored in the order in which they were added.
* However, for all {@link Ordered} elements a
* {@link Comparator} (instantiated by default) for this implementation of {@link Set}, will be
* used. Those elements will have precedence over un-ordered elements. If elements have the same
* order but themselves do not equal to one another the more recent addition will be placed to the
* right of (appended next to) the existing element with the same order, thus preserving the order
* of the insertion while maintaining the order of insertion for the un-ordered elements.
* <p>
* The class is package-protected and only intended for use by the AbstractDispatcher. It
* <em>must</em> enforce safe concurrent access for all usage by the dispatcher.
*
* @author Oleg Zhurakousky
* @author Mark Fisher
* @author Diego Belfer
* @author Gary Russell
* @since 1.0.3
*/
@SuppressWarnings({"unchecked"})
class OrderedAwareCopyOnWriteArraySet<E> implements Set<E> {
private final OrderComparator comparator = new OrderComparator();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final ReadLock readLock = this.rwl.readLock();
private final WriteLock writeLock = this.rwl.writeLock();
private final CopyOnWriteArraySet<E> elements;
private final Set<E> unmodifiableElements;
OrderedAwareCopyOnWriteArraySet() {
this.elements = new CopyOnWriteArraySet<E>();
this.unmodifiableElements = Collections.unmodifiableSet(this.elements);
}
public Set<E> asUnmodifiableSet() {
return this.unmodifiableElements;
}
/**
* Every time an Ordered element is added via this method this
* Set will be re-sorted, otherwise the element is simply added
* to the end. Added element must not be null.
*/
@Override
public boolean add(E o) {
Assert.notNull(o, "Can not add NULL object");
this.writeLock.lock();
try {
boolean present = false;
if (o instanceof Ordered) {
present = this.addOrderedElement((Ordered) o);
}
else {
present = this.elements.add(o);
}
return present;
}
finally {
this.writeLock.unlock();
}
}
/**
* Adds all elements in this Collection.
*/
@Override
public boolean addAll(Collection<? extends E> c) {
Assert.notNull(c, "Can not merge with NULL set");
this.writeLock.lock();
try {
for (E object : c) {
this.add(object);
}
return true;
}
finally {
this.writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean remove(Object o) {
this.writeLock.lock();
try {
boolean removed = this.elements.remove(o);
//unmodifiableElements = Collections.unmodifiableSet(this);
return removed;
}
finally {
this.writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean removeAll(Collection<?> c) {
if (CollectionUtils.isEmpty(c)) {
return false;
}
this.writeLock.lock();
try {
return this.elements.removeAll(c);
}
finally {
this.writeLock.unlock();
}
}
@Override
public <T> T[] toArray(T[] a) {
this.readLock.lock();
try {
return this.elements.toArray(a);
}
finally {
this.readLock.unlock();
}
}
@Override
public String toString() {
this.readLock.lock();
try {
return StringUtils.collectionToCommaDelimitedString(this.elements);
}
finally {
this.readLock.unlock();
}
}
@SuppressWarnings("rawtypes")
private boolean addOrderedElement(Ordered adding) {
boolean added = false;
E[] tempUnorderedElements = (E[]) this.elements.toArray();
if (this.elements.contains(adding)) {
return false;
}
this.elements.clear();
if (tempUnorderedElements.length == 0) {
added = this.elements.add((E) adding);
}
else {
Set tempSet = new LinkedHashSet();
for (E current : tempUnorderedElements) {
if (current instanceof Ordered) {
if (this.comparator.compare(adding, current) < 0) {
added = this.elements.add((E) adding);
this.elements.add(current);
}
else {
this.elements.add(current);
}
}
else {
tempSet.add(current);
}
}
if (!added) {
added = this.elements.add((E) adding);
}
for (Object object : tempSet) {
this.elements.add((E) object);
}
}
return added;
}
@Override
public Iterator<E> iterator() {
return this.elements.iterator();
}
@Override
public int size() {
return this.elements.size();
}
@Override
public boolean isEmpty() {
return this.elements.isEmpty();
}
@Override
public boolean contains(Object o) {
return this.elements.contains(o);
}
@Override
public Object[] toArray() {
return this.elements.toArray();
}
@Override
public boolean containsAll(Collection<?> c) {
return this.elements.containsAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return this.elements.retainAll(c);
}
@Override
public void clear() {
this.elements.clear();
}
}