blob: 7b5d440d0d56cd1df5ac0a90d4d8bb5c91d1db24 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2014, 2021 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.jcommons.collections;
import static org.eclipse.statet.jcommons.lang.NullDefaultLocation.FIELD;
import static org.eclipse.statet.jcommons.lang.NullDefaultLocation.PARAMETER;
import static org.eclipse.statet.jcommons.lang.NullDefaultLocation.RETURN_TYPE;
import static org.eclipse.statet.jcommons.lang.NullDefaultLocation.TYPE_ARGUMENT;
import static org.eclipse.statet.jcommons.lang.NullDefaultLocation.TYPE_BOUND;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import java.util.Spliterator;
import org.eclipse.statet.internal.jcommons.collections.ArrayUtils;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
/**
* Thread safe set based on immutable identity lists.
*
* @param <E>
*/
@NonNullByDefault({ PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT })
public final class CopyOnWriteIdentityListSet<E> extends AbstractSet<E> implements IdentitySet<E> {
private static int indexOf(final Object[] array, final int end, final @Nullable Object e) {
for (int i= 0; i < end; i++) {
if (e == array[i]) {
return i;
}
}
return -1;
}
private volatile ImIdentityList<E> list;
public CopyOnWriteIdentityListSet() {
this.list= ImCollections.newIdentityList();
}
public CopyOnWriteIdentityListSet(final Set<E> initialSet) {
this.list= ImCollections.toIdentityList(initialSet);
}
@Override
public synchronized boolean add(final E element) {
if (!this.list.contains(element)) {
this.list= ImCollections.addElement(this.list, element);
return true;
}
return false;
}
@Override
public synchronized boolean remove(final @Nullable Object element) {
final ImIdentityList<E> l= ImCollections.removeElement(this.list, element);
if (l != this.list) {
this.list= l;
return true;
}
return false;
}
@Override
@SuppressWarnings("unchecked")
public synchronized boolean addAll(final Collection<? extends E> c) {
if (c.isEmpty() || this.list.containsAll(c)) {
return false;
}
final ImIdentityList<E> l= this.list;
final Object[] toAdd= c.toArray();
int end= toAdd.length;
for (int i= 0; i < end; ) {
if (l.contains(toAdd[i]) || indexOf(toAdd, i, toAdd[i]) >= 0) {
System.arraycopy(toAdd, i + 1, toAdd, i, end - i - 1);
end--;
}
else {
i++;
}
}
if (end > 0) {
this.list= ImCollections.concatList(l,
ImCollections.newIdentityList((E[])toAdd, 0, end) );
return true;
}
return false;
}
@Override
@SuppressWarnings("unchecked")
public synchronized boolean retainAll(final Collection<?> c) {
final Object[] array= this.list.toArray();
final Object[] toRetain= c.toArray();
int end= array.length;
for (int i= 0; i < end; ) {
if (indexOf(toRetain, toRetain.length, array[i]) >= 0) {
i++;
}
else {
System.arraycopy(array, i + 1, array, i, end - i - 1);
end--;
}
}
if (end < array.length) {
this.list= ImCollections.newIdentityList((E[])array, 0, end);
return true;
}
return false;
}
@Override
public synchronized boolean removeAll(final Collection<?> c) {
if (c.isEmpty()) {
return false;
}
final Iterator<?> iter= c.iterator();
int idx= -1;
do {
if (iter.hasNext()) {
idx= this.list.indexOf(iter.next());
}
else {
return false;
}
} while (idx == -1);
int n = this.list.size() - 1;
if (n == 0) {
this.list= ImCollections.emptyIdentityList();
return true;
}
else {
final E[] a= ArrayUtils.copyRemoveElement(this.list, n, idx);
while (iter.hasNext()) {
if ((idx= indexOf(a, n, iter.next())) >= 0) {
System.arraycopy(a, idx + 1, a, idx, n - idx - 1);
n--;
}
}
this.list= ImCollections.newIdentityList(a, 0, n);
return true;
}
}
@Override
public synchronized void clear() {
if (!this.list.isEmpty()) {
this.list= ImCollections.newIdentityList();
}
}
@Override
public int size() {
return this.list.size();
}
@Override
public boolean isEmpty() {
return this.list.isEmpty();
}
@Override
public boolean contains(final @Nullable Object o) {
return this.list.contains(o);
}
@Override
public boolean containsAll(final Collection<?> c) {
return this.list.containsAll(c);
}
@Override
public Iterator<E> iterator() {
return this.list.iterator();
}
@Override
public Spliterator<E> spliterator() {
return this.list.spliterator();
}
@Override
public Object[] toArray() {
return this.list.toArray();
}
@Override
public <T> T[] toArray(final T[] a) {
return this.list.toArray(a);
}
/**
* Returns a current snapshot of the set.
*
* @return
*/
public ImIdentityList<E> toList() {
return this.list;
}
/**
* Returns a current snapshot of the list and clears the list.
*
* @return
*/
public synchronized ImIdentityList<E> clearToList() {
final ImIdentityList<E> list= this.list;
if (!list.isEmpty()) {
this.list= ImCollections.newIdentityList();
}
return list;
}
@Override
public boolean equals(final @Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Set) {
final Set<?> other= (Set<?>) obj;
final ImList<E> l= this.list;
return (l.size() == other.size()
&& l.containsAll(other) );
}
return false;
}
}