blob: 4d400a93ff229e67ecf3f035d701bd07ac1894d1 [file] [log] [blame]
package org.checkerframework.dataflow.util;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.PolyNull;
import org.checkerframework.javacutil.BugInCF;
/**
* Base class for arbitrary-size sets that very efficient (more efficient than HashSet) for 0 and 1
* elements.
*
* <p>Does not support storing {@code null}.
*
* <p>This class exists because it has multiple subclasses (currently {@link MostlySingleton} and
* {@link IdentityMostlySingleton}).
*/
public abstract class AbstractMostlySingleton<T extends Object> implements Set<T> {
/** The possible states of this set. */
public enum State {
/** An empty set. */
EMPTY,
/** A singleton set. */
SINGLETON,
/** A set of arbitrary size. */
ANY
}
/** The current state. */
protected State state;
/** The current value, non-null when the state is SINGLETON. */
protected @Nullable T value;
/** The wrapped set, non-null when the state is ANY. */
protected @Nullable Set<T> set;
/** Create an AbstractMostlySingleton. */
protected AbstractMostlySingleton(State s) {
this.state = s;
this.value = null;
}
/** Create an AbstractMostlySingleton. */
protected AbstractMostlySingleton(State s, T v) {
this.state = s;
this.value = v;
}
@Override
public int size() {
switch (state) {
case EMPTY:
return 0;
case SINGLETON:
return 1;
case ANY:
assert set != null : "@AssumeAssertion(nullness): set initialized before";
return set.size();
default:
throw new BugInCF("Unhandled state " + state);
}
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public Iterator<T> iterator() {
switch (state) {
case EMPTY:
return Collections.emptyIterator();
case SINGLETON:
return new Iterator<T>() {
private boolean hasNext = true;
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public T next() {
if (hasNext) {
hasNext = false;
assert value != null : "@AssumeAssertion(nullness): previous add is non-null";
return value;
}
throw new NoSuchElementException();
}
@Override
public void remove() {
state = State.EMPTY;
value = null;
}
};
case ANY:
assert set != null : "@AssumeAssertion(nullness): set initialized before";
return set.iterator();
default:
throw new BugInCF("Unhandled state " + state);
}
}
@Override
public String toString() {
switch (state) {
case EMPTY:
return "[]";
case SINGLETON:
return "[" + value + "]";
case ANY:
assert set != null : "@AssumeAssertion(nullness): set initialized before";
return set.toString();
default:
throw new BugInCF("Unhandled state " + state);
}
}
@Override
public boolean addAll(Collection<? extends T> c) {
boolean res = false;
for (T elem : c) {
res |= add(elem);
}
return res;
}
@Override
public Object[] toArray() {
throw new UnsupportedOperationException();
}
@Override
public <S> @Nullable S[] toArray(@PolyNull S[] a) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(@Nullable Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean containsAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
}