blob: 6badd313eaa71e438f62ee621039839c8ea09aae [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* 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 java.util;
import static javaemul.internal.InternalPreconditions.checkArgument;
import static javaemul.internal.InternalPreconditions.checkElement;
import static javaemul.internal.InternalPreconditions.checkNotNull;
import static javaemul.internal.InternalPreconditions.checkState;
import javaemul.internal.ArrayHelper;
import javaemul.internal.annotations.SpecializeMethod;
/**
* A {@link java.util.Set} of {@link Enum}s. <a
* href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/EnumSet.html">[Sun
* docs]</a>
*
* @param <E> enumeration type
*/
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> {
/**
* Implemented via sparse array since the set size is finite. Iteration takes
* linear time with respect to the set of the enum rather than the number of
* items in the set.
*
* Note: Implemented as a subclass instead of a concrete final EnumSet class.
* This is because declaring an EnumSet.add(E) causes hosted mode to bind to
* the tighter method rather than the bridge method; but the tighter method
* isn't available in the real JRE.
*/
static final class EnumSetImpl<E extends Enum<E>> extends EnumSet<E> {
private class IteratorImpl implements Iterator<E> {
/*
* i is the index of the item that will be returned on the next call to
* next() last is the index of the item that was returned on the previous
* call to next(), -1 if no such item exists.
*/
int i = -1, last = -1;
IteratorImpl() {
findNext();
}
@Override
public boolean hasNext() {
return i < capacity();
}
@Override
public E next() {
checkElement(hasNext());
last = i;
findNext();
return set[last];
}
@Override
public void remove() {
checkState(last != -1);
assert (set[last] != null);
set[last] = null;
--size;
last = -1;
}
private void findNext() {
++i;
for (int c = capacity(); i < c; ++i) {
if (set[i] != null) {
return;
}
}
}
}
/**
* All enums; reference to the class's copy; must not be modified.
*/
private final E[] all;
/**
* Live enums in the set.
*/
private E[] set;
/**
* Count of enums in the set.
*/
private int size;
/**
* Constructs a set taking ownership of the specified set. The size must
* accurately reflect the number of non-null items in set.
*/
public EnumSetImpl(E[] all, E[] set, int size) {
this.all = all;
this.set = set;
this.size = size;
}
@Override
public boolean add(E e) {
checkNotNull(e);
int ordinal = e.ordinal();
if (set[ordinal] == null) {
set[ordinal] = e;
++size;
return true;
}
return false;
}
@Override
public EnumSet<E> clone() {
E[] clonedSet = ArrayHelper.clone(set);
return new EnumSetImpl<E>(all, clonedSet, size);
}
@SpecializeMethod(params = Enum.class, target = "containsEnum")
@Override
public boolean contains(Object o) {
return (o instanceof Enum) && containsEnum((Enum) o);
}
private boolean containsEnum(Enum e) {
return e != null && set[e.ordinal()] == e;
}
@Override
public Iterator<E> iterator() {
return new IteratorImpl();
}
@SpecializeMethod(params = Enum.class, target = "removeEnum")
@Override
public boolean remove(Object o) {
return (o instanceof Enum) && removeEnum((Enum) o);
}
private boolean removeEnum(Enum e) {
if (e != null && set[e.ordinal()] == e) {
set[e.ordinal()] = null;
--size;
return true;
}
return false;
}
@Override
public int size() {
return size;
}
@Override
int capacity() {
return all.length;
}
}
public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) {
E[] all = elementType.getEnumConstants();
E[] set = ArrayHelper.clone(all);
return new EnumSetImpl<E>(all, set, all.length);
}
public static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> other) {
EnumSetImpl<E> s = (EnumSetImpl<E>) other;
E[] all = s.all;
E[] oldSet = s.set;
E[] newSet = ArrayHelper.createFrom(oldSet, oldSet.length);
for (int i = 0, c = oldSet.length; i < c; ++i) {
if (oldSet[i] == null) {
newSet[i] = all[i];
}
}
return new EnumSetImpl<E>(all, newSet, all.length - s.size);
}
public static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c) {
if (c instanceof EnumSet) {
return copyOf((EnumSet<E>) c);
}
checkArgument(!c.isEmpty(), "Collection is empty");
Iterator<E> iterator = c.iterator();
E first = iterator.next();
EnumSet<E> set = of(first);
while (iterator.hasNext()) {
E e = iterator.next();
set.add(e);
}
return set;
}
public static <E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s) {
return s.clone();
}
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
E[] all = elementType.getEnumConstants();
return new EnumSetImpl<E>(all, ArrayHelper.createFrom(all, all.length), 0);
}
public static <E extends Enum<E>> EnumSet<E> of(E first) {
EnumSet<E> set = noneOf(first.getDeclaringClass());
set.add(first);
return set;
}
public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest) {
EnumSet<E> set = of(first);
Collections.addAll(set, rest);
return set;
}
public static <E extends Enum<E>> EnumSet<E> range(E from, E to) {
checkArgument(from.compareTo(to) <= 0, from + " > " + to);
E[] all = from.getDeclaringClass().getEnumConstants();
E[] set = ArrayHelper.createFrom(all, all.length);
// Inclusive
int start = from.ordinal();
int end = to.ordinal() + 1;
for (int i = start; i < end; ++i) {
set[i] = all[i];
}
return new EnumSetImpl<E>(all, set, end - start);
}
/**
* Single implementation only.
*/
EnumSet() {
}
public abstract EnumSet<E> clone();
abstract int capacity();
}