Implementing EnumSet

This commit is contained in:
Alexey Andreev 2017-10-06 00:31:08 +03:00
parent 880529dbe2
commit dfaeb46c5e
5 changed files with 565 additions and 8 deletions

View File

@ -512,7 +512,7 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
@SuppressWarnings("unchecked")
public T[] getEnumConstants() {
return isEnum() ? (T[]) Platform.getEnumConstants(platformClass) : null;
return isEnum() ? (T[]) Platform.getEnumConstants(platformClass).clone() : null;
}
@SuppressWarnings("unchecked")

View File

@ -0,0 +1,117 @@
package org.teavm.classlib.java.util;
import java.io.Serializable;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
public abstract class TEnumSet<E extends Enum<E>> extends AbstractSet<E> implements Cloneable, Serializable {
public static <E extends Enum<E>> TEnumSet<E> noneOf(Class<E> elementType) {
return new TGenericEnumSet<>(elementType);
}
public static <E extends Enum<E>> TEnumSet<E> allOf(Class<E> elementType) {
int count = TGenericEnumSet.getConstants(elementType).length;
int[] bits = new int[((count - 1) / 32) + 1];
for (int i = 0; i < bits.length; ++i) {
bits[i] = ~0;
}
zeroHighBits(bits, count);
return new TGenericEnumSet<>(elementType, bits);
}
public static <E extends Enum<E>> TEnumSet<E> copyOf(TEnumSet<E> s) {
TGenericEnumSet<E> other = (TGenericEnumSet<E>) s;
return new TGenericEnumSet<>(other.cls, other.bits.clone());
}
public static <E extends Enum<E>> TEnumSet<E> copyOf(Collection<E> c) {
if (c instanceof TEnumSet<?>) {
return copyOf((TEnumSet<E>) c);
} else {
Iterator<E> iter = c.iterator();
if (!iter.hasNext()) {
throw new IllegalArgumentException();
}
E first = iter.next();
@SuppressWarnings("unchecked")
TEnumSet<E> result = noneOf(first.getDeclaringClass());
result.add(first);
while (iter.hasNext()) {
result.add(iter.next());
}
return result;
}
}
public static <E extends Enum<E>> TEnumSet<E> complementOf(TEnumSet<E> s) {
TGenericEnumSet<E> other = (TGenericEnumSet<E>) s;
int count = TGenericEnumSet.getConstants(other.cls).length;
int[] bits = new int[other.bits.length];
for (int i = 0; i < bits.length - 1; ++i) {
bits[i] = ~other.bits[i];
}
zeroHighBits(bits, count);
return new TGenericEnumSet<>(other.cls, bits);
}
public static <E extends Enum<E>> TEnumSet<E> of(E e) {
TEnumSet<E> result = TEnumSet.noneOf(e.getDeclaringClass());
result.fastAdd(e);
return result;
}
public static <E extends Enum<E>> TEnumSet<E> of(E e1, E e2) {
TEnumSet<E> result = TEnumSet.noneOf(e1.getDeclaringClass());
result.fastAdd(e1);
result.fastAdd(e2);
return result;
}
public static <E extends Enum<E>> TEnumSet<E> of(E e1, E e2, E e3) {
TEnumSet<E> result = TEnumSet.noneOf(e1.getDeclaringClass());
result.fastAdd(e1);
result.fastAdd(e2);
result.fastAdd(e3);
return result;
}
public static <E extends Enum<E>> TEnumSet<E> of(E e1, E e2, E e3, E e4) {
TEnumSet<E> result = TEnumSet.noneOf(e1.getDeclaringClass());
result.fastAdd(e1);
result.fastAdd(e2);
result.fastAdd(e3);
result.fastAdd(e4);
return result;
}
public static <E extends Enum<E>> TEnumSet<E> of(E e1, E e2, E e3, E e4, E e5) {
TEnumSet<E> result = TEnumSet.noneOf(e1.getDeclaringClass());
result.fastAdd(e1);
result.fastAdd(e2);
result.fastAdd(e3);
result.fastAdd(e4);
result.fastAdd(e5);
return result;
}
@SafeVarargs
public static <E extends Enum<E>> TEnumSet<E> of(E first, E... rest) {
TEnumSet<E> result = TEnumSet.noneOf(first.getDeclaringClass());
for (E e : rest) {
result.fastAdd(e);
}
return result;
}
@Override
public TEnumSet<E> clone() {
return copyOf(this);
}
abstract void fastAdd(E t);
private static void zeroHighBits(int[] bits, int count) {
bits[bits.length - 1] &= (~0) >>> (32 - count % 32);
}
}

View File

@ -0,0 +1,238 @@
/*
* Copyright 2017 Alexey Andreev.
*
* 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.teavm.classlib.java.util;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.teavm.classlib.java.lang.TClass;
import org.teavm.platform.Platform;
class TGenericEnumSet<E extends Enum<E>> extends TEnumSet<E> {
Class<E> cls;
int[] bits;
TGenericEnumSet(Class<E> cls) {
this.cls = cls;
int constantCount = getConstants(cls).length;
int bitCount = ((constantCount - 1) / 32) + 1;
this.bits = new int[bitCount];
}
TGenericEnumSet(Class<E> cls, int[] bits) {
this.cls = cls;
this.bits = bits;
}
static Enum<?>[] getConstants(Class<?> cls) {
return Platform.getEnumConstants(((TClass<?>) (Object) cls).getPlatformClass());
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
int index;
int indexToRemove = -1;
int count = size();
@Override
public boolean hasNext() {
return count > 0;
}
@Override
public E next() {
if (count == 0) {
throw new NoSuchElementException();
}
indexToRemove = index;
while (true) {
int next = Integer.numberOfTrailingZeros(bits[index / 32] >>> (index % 32));
if (next < 32) {
index += next;
--count;
@SuppressWarnings("unchecked")
E returnValue = (E) getConstants(cls)[index++];
return returnValue;
} else {
index = (index / 32 + 1) * 32;
}
}
}
@Override
public void remove() {
if (indexToRemove < 0) {
throw new IllegalStateException();
}
int bitNumber = indexToRemove / 32;
bits[bitNumber] &= ~(1 << (indexToRemove % 32));
indexToRemove = -1;
}
};
}
@Override
public int size() {
int result = 0;
for (int bit : bits) {
result += Integer.bitCount(bit);
}
return result;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof TGenericEnumSet)) {
return false;
}
TGenericEnumSet<?> other = (TGenericEnumSet<?>) o;
return cls == other.cls && Arrays.equals(bits, other.bits);
}
@Override
public int hashCode() {
return Arrays.hashCode(bits);
}
@Override
public boolean removeAll(Collection<?> c) {
if (c instanceof TGenericEnumSet<?>) {
TGenericEnumSet<?> other = (TGenericEnumSet<?>) c;
if (cls == other.cls) {
boolean changed = false;
for (int i = 0; i < bits.length; ++i) {
int inv = ~other.bits[i];
if ((bits[i] & inv) != bits[i]) {
changed = true;
bits[i] &= inv;
}
}
return changed;
}
}
return super.removeAll(c);
}
@Override
public boolean contains(Object o) {
if (!cls.isInstance(o)) {
return false;
}
int n = ((Enum<?>) o).ordinal();
int bitNumber = n / 32;
int bit = 1 << (n % 32);
return (bits[bitNumber] & bit) != 0;
}
@Override
void fastAdd(E t) {
int n = t.ordinal();
int bitNumber = n / 32;
bits[bitNumber] |= 1 << (n % 32);
}
@Override
public boolean add(E t) {
int n = t.ordinal();
int bitNumber = n / 32;
int bit = 1 << (n % 32);
if ((bits[bitNumber] & bit) == 0) {
bits[bitNumber] |= bit;
return true;
} else {
return false;
}
}
@Override
public boolean remove(Object o) {
if (!cls.isInstance(o)) {
return false;
}
int n = ((Enum<?>) o).ordinal();
int bitNumber = n / 32;
int bit = 1 << (n % 32);
if ((bits[bitNumber] & bit) != 0) {
bits[bitNumber] &= ~bit;
return true;
} else {
return false;
}
}
@Override
public boolean containsAll(Collection<?> c) {
if (c instanceof TGenericEnumSet<?>) {
TGenericEnumSet<?> other = (TGenericEnumSet<?>) c;
if (cls == other.cls) {
for (int i = 0; i < bits.length; ++i) {
if ((bits[i] | other.bits[i]) != bits[i]) {
return false;
}
}
return true;
}
}
return super.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
if (c instanceof TGenericEnumSet<?>) {
TGenericEnumSet<?> other = (TGenericEnumSet<?>) c;
if (cls == other.cls) {
boolean added = false;
for (int i = 0; i < bits.length; ++i) {
if ((bits[i] | other.bits[i]) != bits[i]) {
added = true;
bits[i] |= other.bits[i];
}
}
return added;
}
}
return super.addAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
if (c instanceof TGenericEnumSet<?>) {
TGenericEnumSet<?> other = (TGenericEnumSet<?>) c;
if (cls == other.cls) {
boolean changed = false;
for (int i = 0; i < bits.length; ++i) {
if ((bits[i] & other.bits[i]) != bits[i]) {
changed = true;
bits[i] &= other.bits[i];
}
}
return changed;
}
}
return super.retainAll(c);
}
@Override
public void clear() {
Arrays.fill(bits, 0);
}
}

View File

@ -25,12 +25,7 @@ import org.teavm.backend.javascript.spi.InjectorContext;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyPlugin;
import org.teavm.dependency.MethodDependency;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassReader;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
import org.teavm.model.*;
import org.teavm.platform.Platform;
import org.teavm.platform.PlatformClass;
import org.teavm.platform.PlatformRunnable;
@ -201,7 +196,11 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
writer.append("if").ws().append("(!cls.hasOwnProperty(c))").ws().append("{").indent().softNewLine();
writer.append("return null;").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("return cls[c]();").softNewLine();
writer.append("if").ws().append("(typeof cls[c]").ws().append("===").ws().append("\"function\")").ws()
.append("{").indent().softNewLine();
writer.append("cls[c]").ws().append("=").ws().append("cls[c]();").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("return cls[c];").softNewLine();
writer.outdent().append("};").softNewLine();
writer.append("return ").append(selfName).append("(").append(context.getParameterName(1))

View File

@ -0,0 +1,203 @@
/*
* Copyright 2017 Alexey Andreev.
*
* 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.teavm.classlib.java.util;
import static org.junit.Assert.*;
import java.util.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner;
@RunWith(TeaVMTestRunner.class)
public class EnumSetTest {
@Test
public void emptyCreated() {
EnumSet<L> set = EnumSet.noneOf(L.class);
assertEquals("Size", 0, set.size());
assertFalse("Iterator.hasNext must return false", set.iterator().hasNext());
assertFalse("Does not contain E1", set.contains(L.E1));
assertFalse("Does not contain E36", set.contains(L.E36));
try {
set.iterator().next();
fail("Iterator expected to throw exception");
} catch (NoSuchElementException e) {
// OK
}
}
@Test
public void allItemsCreated() {
EnumSet<L> set = EnumSet.allOf(L.class);
assertEquals("Size", 36, set.size());
assertTrue("Iterator.hasNext must return true", set.iterator().hasNext());
assertEquals("Iterator.next must return E1", L.E1, set.iterator().next());
assertTrue("Contains E1", set.contains(L.E1));
assertTrue("Contains E36", set.contains(L.E36));
}
@Test
public void itemAdded() {
EnumSet<L> set = EnumSet.noneOf(L.class);
assertTrue("Adding absent E2 must return true", set.add(L.E2));
assertEquals("Iterator must return E2", L.E2, set.iterator().next());
assertTrue("Set must contain E2", set.contains(L.E2));
assertEquals("Size must be 1 after first addition", 1, set.size());
assertFalse("Adding existing E2 must return false", set.add(L.E2));
assertEquals("Iterator must return E2 after repeated addition", L.E2, set.iterator().next());
assertTrue("Set must contain E2 after repeated addition", set.contains(L.E2));
assertEquals("Size must be 1 after repeated addition", 1, set.size());
assertTrue("Adding absent E4 must return true", set.add(L.E4));
assertTrue("Set must contain E4", set.contains(L.E4));
assertEquals("Size must be 2", 2, set.size());
assertTrue("Adding absent E33 must return true", set.add(L.E33));
assertTrue("Set must contain E4", set.contains(L.E33));
assertEquals("Size must be 3", 3, set.size());
}
@Test
public void iteratorWorks() {
EnumSet<L> set = EnumSet.noneOf(L.class);
set.add(L.E1);
set.add(L.E4);
set.add(L.E33);
set.add(L.E2);
List<L> items = new ArrayList<>();
Iterator<L> iter = set.iterator();
while (iter.hasNext()) {
items.add(iter.next());
}
try {
iter.next();
fail("Can't call Iterator.next after entire collection got iterated");
} catch (NoSuchElementException e) {
// OK
}
assertEquals(Arrays.asList(L.E1, L.E2, L.E4, L.E33), items);
try {
set.iterator().remove();
fail("Can't call Iterator.remove right after initialization");
} catch (IllegalStateException e) {
// OK
}
iter = EnumSet.copyOf(set).iterator();
iter.next();
iter.remove();
try {
iter.remove();
fail("Can't call Iterator.remove right after previous removal");
} catch (IllegalStateException e) {
// OK
}
iter = set.iterator();
iter.next();
iter.remove();
assertEquals(EnumSet.of(L.E2, L.E4, L.E33), set);
}
@Test
public void removeAll() {
EnumSet<L> original = EnumSet.of(L.E2, L.E3, L.E5, L.E8, L.E32);
EnumSet<L> set = original.clone();
assertTrue(set.removeAll(EnumSet.of(L.E3, L.E10, L.E32)));
assertEquals(EnumSet.of(L.E2, L.E5, L.E8), set);
set = original.clone();
assertFalse(set.removeAll(EnumSet.of(L.E4, L.E33)));
assertEquals(original, set);
}
@Test
public void contains() {
EnumSet<L> set = EnumSet.of(L.E2, L.E3, L.E5, L.E8, L.E32);
assertFalse(set.contains(L.E1));
assertTrue(set.contains(L.E2));
assertTrue(set.contains(L.E3));
assertFalse(set.contains(L.E4));
assertTrue(set.contains(L.E5));
assertTrue(set.contains(L.E8));
assertFalse(set.contains(L.E31));
assertTrue(set.contains(L.E32));
assertFalse(set.contains(L.E33));
}
@Test
public void add() {
EnumSet<L> set = EnumSet.of(L.E2, L.E4);
assertFalse(set.add(L.E2));
assertTrue(set.add(L.E3));
assertEquals(EnumSet.of(L.E2, L.E3, L.E4), set);
}
@Test
public void containsAll() {
EnumSet<L> set = EnumSet.of(L.E2, L.E3, L.E5, L.E8, L.E32);
assertFalse(set.containsAll(EnumSet.of(L.E1)));
assertFalse(set.containsAll(EnumSet.of(L.E1, L.E4)));
assertTrue(set.containsAll(EnumSet.of(L.E2)));
assertTrue(set.containsAll(EnumSet.of(L.E2, L.E5)));
assertFalse(set.containsAll(EnumSet.of(L.E2, L.E4)));
}
@Test
public void addAll() {
EnumSet<L> set = EnumSet.of(L.E2, L.E4);
assertTrue(set.addAll(EnumSet.of(L.E2, L.E3)));
assertEquals(EnumSet.of(L.E2, L.E3, L.E4), set);
assertFalse(set.addAll(EnumSet.of(L.E2, L.E4)));
assertEquals(EnumSet.of(L.E2, L.E3, L.E4), set);
assertTrue(set.addAll(EnumSet.of(L.E5, L.E6)));
assertEquals(EnumSet.of(L.E2, L.E3, L.E4, L.E5, L.E6), set);
}
@Test
public void retainAll() {
EnumSet<L> original = EnumSet.of(L.E2, L.E4, L.E5);
EnumSet<L> set = original.clone();
assertTrue(set.retainAll(EnumSet.of(L.E2, L.E4)));
assertEquals(EnumSet.of(L.E2, L.E4), set);
set = original.clone();
assertTrue(set.retainAll(EnumSet.of(L.E1, L.E2)));
assertEquals(EnumSet.of(L.E2), set);
set = original.clone();
assertTrue(set.retainAll(EnumSet.of(L.E1)));
assertEquals(EnumSet.noneOf(L.class), set);
set = original.clone();
assertFalse(set.retainAll(EnumSet.of(L.E2, L.E4, L.E5, L.E6)));
assertEquals(original, set);
}
enum L {
E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15, E16, E17, E18, E19, E20, E21, E22, E23,
E24, E25, E26, E27, E28, E29, E30, E31, E32, E33, E34, E35, E36
}
}