classlib: fix issues in EnumSet and EnumMap (#834)

This commit is contained in:
Ivan Hetman 2023-10-26 09:04:26 +03:00 committed by GitHub
parent 16cd0aaab2
commit 4b6c4bd3d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 940 additions and 79 deletions

View File

@ -107,6 +107,7 @@ public abstract class TAbstractCollection<E> extends TObject implements TCollect
@Override @Override
public boolean removeAll(TCollection<?> c) { public boolean removeAll(TCollection<?> c) {
TObjects.requireNonNull(c);
boolean changed = false; boolean changed = false;
for (TIterator<E> iter = iterator(); iter.hasNext();) { for (TIterator<E> iter = iterator(); iter.hasNext();) {
E e = iter.next(); E e = iter.next();
@ -120,6 +121,7 @@ public abstract class TAbstractCollection<E> extends TObject implements TCollect
@Override @Override
public boolean retainAll(TCollection<?> c) { public boolean retainAll(TCollection<?> c) {
TObjects.requireNonNull(c);
boolean changed = false; boolean changed = false;
for (TIterator<E> iter = iterator(); iter.hasNext();) { for (TIterator<E> iter = iterator(); iter.hasNext();) {
E e = iter.next(); E e = iter.next();

View File

@ -16,21 +16,16 @@
package org.teavm.classlib.java.util; package org.teavm.classlib.java.util;
import java.io.Serializable; import java.io.Serializable;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import org.teavm.classlib.java.lang.TCloneNotSupportedException;
import java.util.Map; import org.teavm.interop.Rename;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
public class TEnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements Serializable, Cloneable { public class TEnumMap<K extends Enum<K>, V> extends TAbstractMap<K, V> implements Serializable, Cloneable {
private Class<K> keyType; private Class<K> keyType;
private Object[] data; private Object[] data;
private boolean[] provided; private boolean[] provided;
private int size; private int size;
private Set<Entry<K, V>> entrySet; private TSet<Entry<K, V>> entrySet;
public TEnumMap(Class<K> keyType) { public TEnumMap(Class<K> keyType) {
initFromKeyType(keyType); initFromKeyType(keyType);
@ -40,16 +35,24 @@ public class TEnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
initFromOtherEnumMap(m); initFromOtherEnumMap(m);
} }
public TEnumMap(Map<K, V> m) { public TEnumMap(TMap<K, V> m) {
if (m instanceof TEnumMap) { if (m instanceof TEnumMap) {
initFromOtherEnumMap((TEnumMap<K, V>) m); initFromOtherEnumMap((TEnumMap<K, V>) m);
} else { } else {
if (m.isEmpty()) { if (m.isEmpty()) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
initFromKeyType(m.keySet().iterator().next().getDeclaringClass()); for (TIterator<? extends TMap.Entry<K, V>> it = m.entrySet().iterator(); it.hasNext();) {
for (Entry<K, V> entry : m.entrySet()) { TMap.Entry<K, V> entry = it.next();
int index = entry.getKey().ordinal(); K key = entry.getKey();
if (keyType == null) {
initFromKeyType(key.getDeclaringClass());
}
Class<?> cls = key.getClass();
if (cls != keyType && cls.getSuperclass() != keyType) {
throw new ClassCastException();
}
int index = key.ordinal();
provided[index] = true; provided[index] = true;
data[index] = entry.getValue(); data[index] = entry.getValue();
} }
@ -78,7 +81,7 @@ public class TEnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
@Override @Override
public boolean containsValue(Object value) { public boolean containsValue(Object value) {
for (int i = 0; i < data.length; ++i) { for (int i = 0; i < data.length; ++i) {
if (provided[i] && Objects.equals(value, data[i])) { if (provided[i] && TObjects.equals(value, data[i])) {
return true; return true;
} }
} }
@ -107,6 +110,10 @@ public class TEnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
@Override @Override
public V put(K key, V value) { public V put(K key, V value) {
Class<?> cls = key.getClass();
if (cls != keyType && cls.getSuperclass() != keyType) {
throw new ClassCastException();
}
int index = key.ordinal(); int index = key.ordinal();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
V old = (V) data[index]; V old = (V) data[index];
@ -135,14 +142,24 @@ public class TEnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
} }
@Override @Override
public void putAll(Map<? extends K, ? extends V> m) { @SuppressWarnings("unchecked")
for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) { public void putAll(TMap<? extends K, ? extends V> m) {
int index = entry.getKey().ordinal(); if (m instanceof TEnumMap) {
if (!provided[index]) { TEnumMap<K, V> em = (TEnumMap<K, V>) m;
provided[index] = true; if (!em.isEmpty() && this.keyType != em.keyType) {
throw new ClassCastException(em.keyType + " != " + keyType);
}
for (int i = 0; i < data.length; i++) {
if (em.provided[i]) {
this.data[i] = em.data[i];
if (!this.provided[i]) {
this.provided[i] = true;
size++; size++;
} }
data[index] = entry.getValue(); }
}
} else {
super.putAll(m);
} }
} }
@ -155,13 +172,29 @@ public class TEnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
} }
} }
@Rename("clone")
@SuppressWarnings("unchecked")
public TEnumMap<K, V> clone0() {
try {
TEnumMap<K, V> map = (TEnumMap<K, V>) super.clone();
map.keyType = this.keyType;
map.provided = this.provided.clone();
map.data = this.data.clone();
map.size = this.size;
return map;
} catch (TCloneNotSupportedException e) {
return null;
}
}
@Override @Override
public Set<Entry<K, V>> entrySet() { public TSet<Entry<K, V>> entrySet() {
if (entrySet == null) { if (entrySet == null) {
entrySet = new AbstractSet<Entry<K, V>>() { entrySet = new TAbstractSet<>() {
@Override @Override
public Iterator<Entry<K, V>> iterator() { public TIterator<Entry<K, V>> iterator() {
return new Iterator<Entry<K, V>>() { return new TIterator<>() {
int index; int index;
int removeIndex = -1; int removeIndex = -1;
@ -177,7 +210,7 @@ public class TEnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
@Override @Override
public Entry<K, V> next() { public Entry<K, V> next() {
if (index >= data.length) { if (index >= data.length) {
throw new NoSuchElementException(); throw new TNoSuchElementException();
} }
removeIndex = index; removeIndex = index;
EntryImpl result = new EntryImpl(index++); EntryImpl result = new EntryImpl(index++);
@ -196,9 +229,11 @@ public class TEnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
if (removeIndex < 0) { if (removeIndex < 0) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
if (provided[removeIndex]) {
data[removeIndex] = null; data[removeIndex] = null;
provided[removeIndex] = false; provided[removeIndex] = false;
size--; size--;
}
removeIndex = -1; removeIndex = -1;
} }
}; };
@ -210,12 +245,32 @@ public class TEnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
} }
@Override @Override
public boolean remove(Object o) { public boolean contains(Object o) {
if (!keyType.isInstance(o)) { if (!(o instanceof TMap.Entry<?, ?>)) {
return false; return false;
} }
int index = ((Enum<?>) o).ordinal(); TMap.Entry<?, ?> e = (TMap.Entry<?, ?>) o;
if (provided[index]) { Class<?> cls = e.getKey().getClass();
if (cls != keyType && cls.getSuperclass() != keyType) {
return false;
}
int index = ((Enum<?>) e.getKey()).ordinal();
return provided[index] && TObjects.equals(data[index], e.getValue());
}
@Override
public boolean remove(Object o) {
if (!(o instanceof TMap.Entry<?, ?>)) {
return false;
}
TMap.Entry<?, ?> e = (TMap.Entry<?, ?>) o;
Class<?> cls = e.getKey().getClass();
if (cls != keyType && cls.getSuperclass() != keyType) {
return false;
}
int index = ((Enum<?>) e.getKey()).ordinal();
if (provided[index] && TObjects.equals(e.getValue(), data[index])) {
provided[index] = false; provided[index] = false;
data[index] = null; data[index] = null;
size--; size--;
@ -237,28 +292,119 @@ public class TEnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
this.index = index; this.index = index;
} }
@Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public K getKey() { private K key() {
return (K) TGenericEnumSet.getConstants(keyType)[index]; return (K) TGenericEnumSet.getConstants(keyType)[index];
} }
@Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public V getValue() { private V value() {
return (V) data[index]; return (V) data[index];
} }
@Override
public K getKey() {
if (!provided[index]) {
throw new IllegalStateException();
}
return key();
}
@Override
public V getValue() {
if (!provided[index]) {
throw new IllegalStateException();
}
return value();
}
@Override @Override
public V setValue(V value) { public V setValue(V value) {
if (!provided[index]) {
throw new IllegalStateException();
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
V old = (V) data[index]; V old = (V) data[index];
data[index] = value; data[index] = value;
return old; return old;
} }
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof TMap.Entry) {
TMap.Entry<?, ?> entry = (TMap.Entry<?, ?>) obj;
return TObjects.equals(key(), entry.getKey()) && TObjects.equals(value(), entry.getValue());
}
return false;
}
@Override
public int hashCode() {
return TObjects.hashCode(key()) ^ TObjects.hashCode(value());
}
@Override
public String toString() {
return key() + "=" + value();
}
} }
}; };
} }
return entrySet; return entrySet;
} }
@Override
public TCollection<V> values() {
if (cachedValues == null) {
cachedValues = new TAbstractCollection<>() {
@Override
public int size() {
return size;
}
@Override
public boolean contains(Object o) {
return containsValue(o);
}
@Override
public boolean remove(Object o) {
for (int i = 0; i < data.length; i++) {
if (provided[i] && TObjects.equals(o, data[i])) {
data[i] = null;
provided[i] = false;
size--;
return true;
}
}
return false;
}
@Override
public void clear() {
TEnumMap.this.clear();
}
@Override
public TIterator<V> iterator() {
final TIterator<TMap.Entry<K, V>> it = entrySet().iterator();
return new TIterator<>() {
@Override public boolean hasNext() {
return it.hasNext();
}
@Override public V next() {
return it.next().getValue();
}
@Override public void remove() {
it.remove();
}
};
}
};
}
return cachedValues;
}
} }

View File

@ -28,10 +28,16 @@ public abstract class TEnumSet<E extends Enum<E>> extends AbstractSet<E> impleme
} }
public static <E extends Enum<E>> TEnumSet<E> allOf(Class<E> elementType) { public static <E extends Enum<E>> TEnumSet<E> allOf(Class<E> elementType) {
int count = TGenericEnumSet.getConstants(elementType).length; Enum<?>[] constants = TGenericEnumSet.getConstants(elementType);
int[] bits = new int[((count - 1) / 32) + 1]; if (constants == null) {
throw new ClassCastException();
}
int count = constants.length;
int[] bits = new int[count == 0 ? 0 : ((count - 1) / Integer.SIZE) + 1];
Arrays.fill(bits, ~0); Arrays.fill(bits, ~0);
if (count > 0) {
zeroHighBits(bits, count); zeroHighBits(bits, count);
}
return new TGenericEnumSet<>(elementType, bits); return new TGenericEnumSet<>(elementType, bits);
} }
@ -62,7 +68,7 @@ public abstract class TEnumSet<E extends Enum<E>> extends AbstractSet<E> impleme
TGenericEnumSet<E> other = (TGenericEnumSet<E>) s; TGenericEnumSet<E> other = (TGenericEnumSet<E>) s;
int count = TGenericEnumSet.getConstants(other.cls).length; int count = TGenericEnumSet.getConstants(other.cls).length;
int[] bits = new int[other.bits.length]; int[] bits = new int[other.bits.length];
for (int i = 0; i < bits.length - 1; ++i) { for (int i = 0; i < bits.length; ++i) {
bits[i] = ~other.bits[i]; bits[i] = ~other.bits[i];
} }
zeroHighBits(bits, count); zeroHighBits(bits, count);
@ -148,6 +154,6 @@ public abstract class TEnumSet<E extends Enum<E>> extends AbstractSet<E> impleme
abstract void fastAdd(int n); abstract void fastAdd(int n);
private static void zeroHighBits(int[] bits, int count) { private static void zeroHighBits(int[] bits, int count) {
bits[bits.length - 1] &= (~0) >>> (32 - count % 32); bits[bits.length - 1] &= ~0 >>> (Integer.SIZE - count % Integer.SIZE);
} }
} }

View File

@ -18,7 +18,6 @@ package org.teavm.classlib.java.util;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.NoSuchElementException;
import org.teavm.classlib.java.lang.TClass; import org.teavm.classlib.java.lang.TClass;
import org.teavm.platform.Platform; import org.teavm.platform.Platform;
import org.teavm.platform.PlatformClass; import org.teavm.platform.PlatformClass;
@ -29,8 +28,12 @@ class TGenericEnumSet<E extends Enum<E>> extends TEnumSet<E> {
TGenericEnumSet(Class<E> cls) { TGenericEnumSet(Class<E> cls) {
this.cls = cls; this.cls = cls;
int constantCount = getConstants(cls).length; Enum<?>[] constants = getConstants(cls);
int bitCount = ((constantCount - 1) / 32) + 1; if (constants == null) {
throw new ClassCastException();
}
int constantCount = constants.length;
int bitCount = constantCount == 0 ? 0 : ((constantCount - 1) / Integer.SIZE) + 1;
this.bits = new int[bitCount]; this.bits = new int[bitCount];
} }
@ -47,34 +50,39 @@ class TGenericEnumSet<E extends Enum<E>> extends TEnumSet<E> {
@Override @Override
public Iterator<E> iterator() { public Iterator<E> iterator() {
return new Iterator<E>() { return new Iterator<>() {
int index; private int index = find();
int indexToRemove = -1; private int indexToRemove = -1;
int count = size();
private int find() {
int overflow = bits.length * Integer.SIZE;
while (index < overflow) {
int next = Integer.numberOfTrailingZeros(bits[index / Integer.SIZE] >>> (index % Integer.SIZE));
if (next < Integer.SIZE) {
index += next;
return index;
} else {
index = (index / Integer.SIZE + 1) * Integer.SIZE;
}
}
return index;
}
@Override @Override
public boolean hasNext() { public boolean hasNext() {
return count > 0; return index < bits.length * Integer.SIZE;
} }
@Override @Override
public E next() { public E next() {
if (count == 0) { if (!hasNext()) {
throw new NoSuchElementException(); throw new TNoSuchElementException();
} }
indexToRemove = index; indexToRemove = index;
while (true) {
int next = Integer.numberOfTrailingZeros(bits[index / 32] >>> (index % 32));
if (next < 32) {
index += next;
--count;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
E returnValue = (E) getConstants(cls)[index++]; E returnValue = (E) getConstants(cls)[index++];
index = find();
return returnValue; return returnValue;
} else {
index = (index / 32 + 1) * 32;
}
}
} }
@Override @Override
@ -82,8 +90,8 @@ class TGenericEnumSet<E extends Enum<E>> extends TEnumSet<E> {
if (indexToRemove < 0) { if (indexToRemove < 0) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
int bitNumber = indexToRemove / 32; int bitNumber = indexToRemove / Integer.SIZE;
bits[bitNumber] &= ~(1 << (indexToRemove % 32)); bits[bitNumber] &= ~(1 << (indexToRemove % Integer.SIZE));
indexToRemove = -1; indexToRemove = -1;
} }
}; };
@ -104,15 +112,13 @@ class TGenericEnumSet<E extends Enum<E>> extends TEnumSet<E> {
return true; return true;
} }
if (!(o instanceof TGenericEnumSet)) { if (!(o instanceof TGenericEnumSet)) {
return false; return super.equals(o);
} }
TGenericEnumSet<?> other = (TGenericEnumSet<?>) o; TGenericEnumSet<?> other = (TGenericEnumSet<?>) o;
return cls == other.cls && Arrays.equals(bits, other.bits); if (this.cls != other.cls) {
return this.size() == 0 && other.size() == 0;
} }
return Arrays.equals(bits, other.bits);
@Override
public int hashCode() {
return Arrays.hashCode(bits);
} }
@Override @Override
@ -140,19 +146,23 @@ class TGenericEnumSet<E extends Enum<E>> extends TEnumSet<E> {
return false; return false;
} }
int n = ((Enum<?>) o).ordinal(); int n = ((Enum<?>) o).ordinal();
int bitNumber = n / 32; int bitNumber = n / Integer.SIZE;
int bit = 1 << (n % 32); int bit = 1 << (n % Integer.SIZE);
return (bits[bitNumber] & bit) != 0; return (bits[bitNumber] & bit) != 0;
} }
@Override @Override
void fastAdd(int n) { void fastAdd(int n) {
int bitNumber = n / 32; int bitNumber = n / Integer.SIZE;
bits[bitNumber] |= 1 << (n % 32); bits[bitNumber] |= 1 << (n % Integer.SIZE);
} }
@Override @Override
public boolean add(E t) { public boolean add(E t) {
Class<?> tCls = t.getClass();
if (tCls != cls && tCls.getSuperclass() != cls) {
throw new ClassCastException();
}
int n = t.ordinal(); int n = t.ordinal();
int bitNumber = n / 32; int bitNumber = n / 32;
int bit = 1 << (n % 32); int bit = 1 << (n % 32);
@ -171,8 +181,8 @@ class TGenericEnumSet<E extends Enum<E>> extends TEnumSet<E> {
} }
int n = ((Enum<?>) o).ordinal(); int n = ((Enum<?>) o).ordinal();
int bitNumber = n / 32; int bitNumber = n / Integer.SIZE;
int bit = 1 << (n % 32); int bit = 1 << (n % Integer.SIZE);
if ((bits[bitNumber] & bit) != 0) { if ((bits[bitNumber] & bit) != 0) {
bits[bitNumber] &= ~bit; bits[bitNumber] &= ~bit;
return true; return true;

View File

@ -17,16 +17,23 @@ package org.teavm.classlib.java.util;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner; import org.teavm.junit.TeaVMTestRunner;
@ -228,7 +235,486 @@ public class EnumMapTest {
assertEquals(1, map.size()); assertEquals(1, map.size());
} }
@Test
@SuppressWarnings("unchecked")
public void constructorMap() {
EnumMap enumMap;
Map enumColorMap = null;
try {
enumMap = new EnumMap(enumColorMap);
fail("Expected NullPointerException");
} catch (NullPointerException e) {
// Expected
}
enumColorMap = new EnumMap<Color, Double>(Color.class);
enumMap = new EnumMap(enumColorMap);
enumColorMap.put(Color.Blue, 3);
enumMap = new EnumMap(enumColorMap);
HashMap hashColorMap = null;
try {
enumMap = new EnumMap(hashColorMap);
fail("Expected NullPointerException");
} catch (NullPointerException e) {
// Expected
}
hashColorMap = new HashMap();
try {
enumMap = new EnumMap(hashColorMap);
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
// Expected
}
hashColorMap.put(Color.Green, 2);
enumMap = new EnumMap(hashColorMap);
assertEquals("Constructor fails", 2, enumMap.get(Color.Green));
assertNull("Constructor fails", enumMap.get(Color.Red));
enumMap.put(Color.Red, 1);
assertEquals("Wrong value", 1, enumMap.get(Color.Red));
hashColorMap.put(Size.Big, 3);
try {
enumMap = new EnumMap(hashColorMap);
fail("Expected ClassCastException");
} catch (ClassCastException e) {
// Expected
}
hashColorMap = new HashMap();
hashColorMap.put(1, 1);
try {
enumMap = new EnumMap(hashColorMap);
fail("Expected ClassCastException");
} catch (ClassCastException e) {
// Expected
}
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void putAll() {
EnumMap enumColorMap = new EnumMap(Color.class);
enumColorMap.put(Color.Green, 2);
EnumMap enumSizeMap = new EnumMap(Size.class);
enumColorMap.putAll(enumSizeMap);
enumSizeMap.put(Size.Big, 1);
try {
enumColorMap.putAll(enumSizeMap);
fail("Expected ClassCastException");
} catch (ClassCastException e) {
// Expected
}
EnumMap enumColorMap1 = new EnumMap<Color, Double>(Color.class);
enumColorMap1.put(Color.Blue, 3);
enumColorMap.putAll(enumColorMap1);
assertEquals("Get returned incorrect value for given key", 3, enumColorMap.get(Color.Blue));
assertEquals("Wrong Size", 2, enumColorMap.size());
enumColorMap = new EnumMap<Color, Double>(Color.class);
HashMap hashColorMap = null;
try {
enumColorMap.putAll(hashColorMap);
fail("Expected NullPointerException");
} catch (NullPointerException e) {
// Expected
}
hashColorMap = new HashMap();
enumColorMap.putAll(hashColorMap);
hashColorMap.put(Color.Green, 2);
enumColorMap.putAll(hashColorMap);
assertEquals("Get returned incorrect value for given key", 2, enumColorMap.get(Color.Green));
assertNull("Get returned non-null for non mapped key", enumColorMap.get(Color.Red));
hashColorMap.put(Color.Red, 1);
enumColorMap.putAll(hashColorMap);
assertEquals("Get returned incorrect value for given key", 2, enumColorMap.get(Color.Green));
hashColorMap.put(Size.Big, 3);
try {
enumColorMap.putAll(hashColorMap);
fail("Expected ClassCastException");
} catch (ClassCastException e) {
// Expected
}
hashColorMap = new HashMap();
hashColorMap.put(1, 1);
try {
enumColorMap.putAll(hashColorMap);
fail("Expected ClassCastException");
} catch (ClassCastException e) {
// Expected
}
}
@Test
public void cloneWorks() {
EnumMap<Size, Integer> enumSizeMap = new EnumMap<>(Size.class);
Integer integer = Integer.valueOf("3");
enumSizeMap.put(Size.Small, integer);
EnumMap<Size, Integer> enumSizeMapClone = enumSizeMap.clone();
assertNotSame("Should not be same", enumSizeMap, enumSizeMapClone);
assertEquals("Clone answered unequal EnumMap", enumSizeMap, enumSizeMapClone);
assertSame("Should be same", enumSizeMap.get(Size.Small), enumSizeMapClone.get(Size.Small));
assertSame("Clone is not shallow clone", integer, enumSizeMapClone.get(Size.Small));
enumSizeMap.remove(Size.Small);
assertSame("Clone is not shallow clone", integer, enumSizeMapClone.get(Size.Small));
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void entrySet() {
EnumMap<Size, Integer> enumSizeMap = new EnumMap<>(Size.class);
enumSizeMap.put(Size.Middle, 1);
enumSizeMap.put(Size.Big, null);
MockEntry<Size, Integer> mockEntry = new MockEntry<>(Size.Middle, 1);
Set<Map.Entry<Size, Integer>> set = enumSizeMap.entrySet();
Set<Map.Entry<Size, Integer>> set1 = enumSizeMap.entrySet();
assertSame("Should be same", set1, set);
try {
set.add(mockEntry);
fail("Should throw UnsupportedOperationException");
} catch (UnsupportedOperationException e) {
// Expected
}
assertTrue("Returned false for contained object", set.contains(mockEntry));
mockEntry = new MockEntry<>(Size.Middle, null);
assertFalse("Returned true for uncontained object", set.contains(mockEntry));
assertFalse("Returned true for uncontained object", set.contains(Size.Small));
assertFalse("Returned true for uncontained object", set.contains(new MockEntry(1, 1)));
assertFalse("Returned true for uncontained object", set.contains(1));
mockEntry = new MockEntry<>(Size.Big, null);
assertTrue("Returned false for contained object", set.contains(mockEntry));
assertTrue("Returned false when the object can be removed", set.remove(mockEntry));
assertFalse("Returned true for uncontained object", set.contains(mockEntry));
assertFalse("Returned true when the object can not be removed", set.remove(mockEntry));
assertFalse("Returned true when the object can not be removed", set.remove(new MockEntry(1, 1)));
assertFalse("Returned true when the object can not be removed", set.remove(1));
// The set is backed by the map so changes to one are reflected by the
// other.
enumSizeMap.put(Size.Big, 3);
mockEntry = new MockEntry<>(Size.Big, 3);
assertTrue("Returned false for contained object", set.contains(mockEntry));
enumSizeMap.remove(Size.Big);
assertFalse("Returned true for uncontained object", set.contains(mockEntry));
assertEquals("Wrong size", 1, set.size());
set.clear();
assertEquals("Wrong size", 0, set.size());
enumSizeMap = new EnumMap<>(Size.class);
enumSizeMap.put(Size.Middle, 1);
enumSizeMap.put(Size.Big, null);
set = enumSizeMap.entrySet();
Collection<Map.Entry<Size, Integer>> c = new ArrayList<>();
c.add(new MockEntry<>(Size.Middle, 1));
assertTrue("Return wrong value", set.containsAll(c));
assertTrue("Remove does not success", set.removeAll(c));
enumSizeMap.put(Size.Middle, 1);
c.add(new MockEntry(Size.Big, 3));
assertTrue("Remove does not success", set.removeAll(c));
assertFalse("Should return false", set.removeAll(c));
assertEquals("Wrong size", 1, set.size());
enumSizeMap = new EnumMap<>(Size.class);
enumSizeMap.put(Size.Middle, 1);
enumSizeMap.put(Size.Big, null);
set = enumSizeMap.entrySet();
c = new ArrayList<>();
c.add(new MockEntry(Size.Middle, 1));
c.add(new MockEntry(Size.Big, 3));
assertTrue("Retain does not success", set.retainAll(c));
assertEquals("Wrong size", 1, set.size());
assertFalse("Should return false", set.retainAll(c));
enumSizeMap = new EnumMap<>(Size.class);
enumSizeMap.put(Size.Middle, 1);
enumSizeMap.put(Size.Big, null);
set = enumSizeMap.entrySet();
Object[] array = set.toArray();
assertEquals("Wrong length", 2, array.length);
Map.Entry entry = (Map.Entry) array[0];
assertEquals("Wrong key", Size.Middle, entry.getKey());
assertEquals("Wrong value", 1, entry.getValue());
Object[] array1 = new Object[10];
array1 = set.toArray();
assertEquals("Wrong length", 2, array1.length);
entry = (Map.Entry) array[0];
assertEquals("Wrong key", Size.Middle, entry.getKey());
assertEquals("Wrong value", 1, entry.getValue());
array1 = new Object[10];
array1 = set.toArray(array1);
assertEquals("Wrong length", 10, array1.length);
entry = (Map.Entry) array[1];
assertEquals("Wrong key", Size.Big, entry.getKey());
assertNull("Should be null", array1[2]);
set = enumSizeMap.entrySet();
Integer integer = Integer.valueOf("1");
assertFalse("Returned true when the object can not be removed", set.remove(integer));
assertTrue("Returned false when the object can be removed", set.remove(entry));
enumSizeMap = new EnumMap<>(EnumMapTest.Size.class);
enumSizeMap.put(Size.Middle, 1);
enumSizeMap.put(Size.Big, null);
set = enumSizeMap.entrySet();
Iterator<Map.Entry<Size, Integer>> iter = set.iterator();
entry = iter.next();
assertTrue("Returned false for contained object", set.contains(entry));
mockEntry = new MockEntry<>(Size.Middle, 2);
assertFalse("Returned true for uncontained object", set.contains(mockEntry));
assertFalse("Returned true for uncontained object", set
.contains(new MockEntry(2, 2)));
entry = iter.next();
assertTrue("Returned false for contained object", set.contains(entry));
enumSizeMap.put(Size.Middle, 1);
enumSizeMap.remove(Size.Big);
mockEntry = new MockEntry<>(Size.Big, null);
assertEquals("Wrong size", 1, set.size());
assertFalse("Returned true for uncontained object", set.contains(mockEntry));
enumSizeMap.put(Size.Big, 2);
mockEntry = new MockEntry<>(Size.Big, 2);
assertTrue("Returned false for contained object", set
.contains(mockEntry));
iter.remove();
try {
iter.remove();
fail("Should throw IllegalStateException");
} catch (IllegalStateException e) {
// Expected
}
try {
entry.setValue(2);
fail("Should throw IllegalStateException");
} catch (IllegalStateException e) {
// Expected
}
try {
set.contains(entry);
fail("Should throw IllegalStateException");
} catch (IllegalStateException e) {
// Expected
}
enumSizeMap = new EnumMap(Size.class);
enumSizeMap.put(Size.Middle, 1);
enumSizeMap.put(Size.Big, null);
set = enumSizeMap.entrySet();
iter = set.iterator();
entry = iter.next();
assertEquals("Wrong key", Size.Middle, entry.getKey());
assertTrue("Returned false for contained object", set.contains(entry));
enumSizeMap.put(Size.Middle, 3);
assertTrue("Returned false for contained object", set.contains(entry));
entry.setValue(2);
assertTrue("Returned false for contained object", set.contains(entry));
assertFalse("Returned true for uncontained object", set.remove(1));
iter.next();
assertEquals("Wrong key", Size.Middle, entry.getKey());
set.clear();
assertEquals("Wrong size", 0, set.size());
enumSizeMap = new EnumMap<>(Size.class);
enumSizeMap.put(Size.Middle, 1);
enumSizeMap.put(Size.Big, null);
set = enumSizeMap.entrySet();
iter = set.iterator();
mockEntry = new MockEntry<>(Size.Middle, 1);
assertNotEquals("Wrong result", entry, mockEntry);
try {
iter.remove();
fail("Should throw IllegalStateException");
} catch (IllegalStateException e) {
// Expected
}
entry = iter.next();
assertEquals("Wrong key", Size.Middle, entry.getKey());
assertEquals("Should return true", entry, mockEntry);
assertEquals("Should be equal", mockEntry.hashCode(), entry.hashCode());
mockEntry = new MockEntry<>(Size.Big, 1);
assertNotEquals("Wrong result", entry, mockEntry);
entry = iter.next();
assertNotEquals("Wrong result", entry, mockEntry);
assertEquals("Wrong key", Size.Big, entry.getKey());
iter.remove();
assertNotEquals("Wrong result", entry, mockEntry);
assertEquals("Wrong size", 1, set.size());
try {
iter.remove();
fail("Should throw IllegalStateException");
} catch (IllegalStateException e) {
// Expected
}
try {
iter.next();
fail("Should throw NoSuchElementException");
} catch (NoSuchElementException e) {
// Expected
}
}
@Test
@SuppressWarnings({ "unchecked", "boxing" })
public void values() {
EnumMap<Color, Integer> enumColorMap = new EnumMap<>(Color.class);
enumColorMap.put(Color.Red, 1);
enumColorMap.put(Color.Blue, null);
Collection<Integer> collection = enumColorMap.values();
Collection<Integer> collection1 = enumColorMap.values();
assertSame("Should be same", collection1, collection);
try {
collection.add(1);
fail("Should throw UnsupportedOperationException");
} catch (UnsupportedOperationException e) {
// Expected
}
assertTrue("Returned false for contained object", collection.contains(1));
assertTrue("Returned false for contained object", collection.contains(null));
assertFalse("Returned true for uncontained object", collection.contains(2));
assertTrue("Returned false when the object can be removed", collection.remove(null));
assertFalse("Returned true for uncontained object", collection.contains(null));
assertFalse("Returned true when the object can not be removed", collection.remove(null));
// The set is backed by the map so changes to one are reflected by the other.
enumColorMap.put(Color.Blue, 3);
assertTrue("Returned false for contained object", collection.contains(3));
enumColorMap.remove(Color.Blue);
assertFalse("Returned true for uncontained object", collection.contains(3));
assertEquals("Wrong size", 1, collection.size());
collection.clear();
assertEquals("Wrong size", 0, collection.size());
enumColorMap = new EnumMap<>(Color.class);
enumColorMap.put(Color.Red, 1);
enumColorMap.put(Color.Blue, null);
collection = enumColorMap.values();
Collection c = new ArrayList<>();
c.add(1);
assertTrue("Should return true", collection.containsAll(c));
c.add(3.4);
assertFalse("Should return false", collection.containsAll(c));
assertTrue("Should return true", collection.removeAll(c));
assertEquals("Wrong size", 1, collection.size());
assertFalse("Should return false", collection.removeAll(c));
assertEquals("Wrong size", 1, collection.size());
try {
collection.addAll(c);
fail("Should throw UnsupportedOperationException");
} catch (UnsupportedOperationException e) {
// Expected
}
enumColorMap.put(Color.Red, 1);
assertEquals("Wrong size", 2, collection.size());
assertTrue("Should return true", collection.retainAll(c));
assertEquals("Wrong size", 1, collection.size());
assertFalse("Should return false", collection.retainAll(c));
assertEquals(1, collection.size());
Object[] array = collection.toArray();
assertEquals("Wrong length", 1, array.length);
assertEquals("Wrong key", 1, array[0]);
enumColorMap = new EnumMap<>(Color.class);
enumColorMap.put(Color.Red, 1);
enumColorMap.put(Color.Blue, null);
collection = enumColorMap.values();
assertEquals("Wrong size", 2, collection.size());
assertFalse("Returned true when the object can not be removed",
collection.remove(Integer.valueOf("10")));
Iterator<Integer> iter = enumColorMap.values().iterator();
Object value = iter.next();
assertTrue("Returned false for contained object", collection.contains(value));
value = iter.next();
assertTrue("Returned false for contained object", collection.contains(value));
enumColorMap.put(Color.Green, 1);
enumColorMap.remove(Color.Blue);
assertFalse("Returned true for uncontained object", collection.contains(value));
iter.remove();
assertEquals("{Red=1, Green=1}", enumColorMap.toString());
try {
iter.remove();
fail("Should throw IllegalStateException");
} catch (IllegalStateException e) {
// Expected
}
assertFalse("Returned true for uncontained object", collection.contains(value));
iter = enumColorMap.values().iterator();
value = iter.next();
assertTrue("Returned false for contained object", collection.contains(value));
enumColorMap.put(Color.Green, 3);
assertTrue("Returned false for contained object", collection.contains(value));
assertTrue("Returned false for contained object", collection.remove(Integer.valueOf("1")));
assertEquals("Wrong size", 1, collection.size());
collection.clear();
assertEquals("Wrong size", 0, collection.size());
enumColorMap = new EnumMap<>(Color.class);
Integer integer1 = 1;
enumColorMap.put(Color.Green, integer1);
enumColorMap.put(Color.Blue, null);
collection = enumColorMap.values();
iter = enumColorMap.values().iterator();
try {
iter.remove();
fail("Should throw IllegalStateException");
} catch (IllegalStateException e) {
// Expected
}
value = iter.next();
assertEquals("Wrong value", integer1, value);
assertSame("Wrong value", integer1, value);
assertNotEquals("Returned true for unequal object", iter, value);
iter.remove();
assertNotEquals("Returned true for unequal object", iter, value);
try {
iter.remove();
fail("Should throw IllegalStateException");
} catch (IllegalStateException e) {
// Expected
}
assertEquals("Wrong size", 1, collection.size());
value = iter.next();
assertNotEquals("Returned true for unequal object", iter, value);
iter.remove();
try {
iter.next();
fail("Should throw NoSuchElementException");
} catch (NoSuchElementException e) {
// Expected
}
}
enum Size {
Small, Middle, Big {
}
}
enum Color {
Red, Green, Blue {
}
}
enum Empty {
//Empty
}
enum L { enum L {
A, B, C A, B, C
} }
private static class MockEntry<K, V> implements Map.Entry<K, V> {
private K key;
private V value;
public MockEntry(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public int hashCode() {
return (key == null ? 0 : key.hashCode())
^ (value == null ? 0 : value.hashCode());
}
@Override
public boolean equals(Object o) {
return o instanceof Map.Entry<?, ?> e
&& Objects.equals(key, e.getKey())
&& Objects.equals(value, e.getValue());
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V object) {
V oldValue = value;
value = object;
return oldValue;
}
}
} }

View File

@ -19,7 +19,9 @@ import static org.junit.Assert.*;
import java.util.*; import java.util.*;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.teavm.junit.SkipPlatform;
import org.teavm.junit.TeaVMTestRunner; import org.teavm.junit.TeaVMTestRunner;
import org.teavm.junit.TestPlatform;
@RunWith(TeaVMTestRunner.class) @RunWith(TeaVMTestRunner.class)
public class EnumSetTest { public class EnumSetTest {
@ -196,6 +198,215 @@ public class EnumSetTest {
assertEquals(original, set); assertEquals(original, set);
} }
@Test
@SkipPlatform({ TestPlatform.WEBASSEMBLY, TestPlatform.WASI })
public void iterator() {
Set<EnumFoo> set = EnumSet.noneOf(EnumFoo.class);
set.add(EnumFoo.a);
set.add(EnumFoo.b);
Iterator<EnumFoo> iterator = set.iterator();
Iterator<EnumFoo> anotherIterator = set.iterator();
assertNotSame("Should not be same", iterator, anotherIterator);
try {
iterator.remove();
fail("Should throw IllegalStateException");
} catch (IllegalStateException e) {
// expectedd
}
assertTrue("Should has next element:", iterator.hasNext());
assertSame("Should be identical", EnumFoo.a, iterator.next());
iterator.remove();
assertTrue("Should has next element:", iterator.hasNext());
assertSame("Should be identical", EnumFoo.b, iterator.next());
assertFalse("Should not has next element:", iterator.hasNext());
assertFalse("Should not has next element:", iterator.hasNext());
assertEquals("Size should be 1:", 1, set.size());
try {
iterator.next();
fail("Should throw NoSuchElementException");
} catch (NoSuchElementException e) {
// expected
}
set = EnumSet.noneOf(EnumFoo.class);
set.add(EnumFoo.a);
iterator = set.iterator();
assertEquals("Should be equal", EnumFoo.a, iterator.next());
iterator.remove();
try {
iterator.remove();
fail("Should throw IllegalStateException");
} catch (IllegalStateException e) {
// expected
}
Set<EmptyEnum> emptySet = EnumSet.allOf(EmptyEnum.class);
Iterator<EmptyEnum> emptyIterator = emptySet.iterator();
try {
emptyIterator.next();
fail("Should throw NoSuchElementException");
} catch (NoSuchElementException e) {
// expected
}
Set<EnumWithInnerClass> setWithSubclass = EnumSet
.allOf(EnumWithInnerClass.class);
setWithSubclass.remove(EnumWithInnerClass.e);
Iterator<EnumWithInnerClass> iteratorWithSubclass = setWithSubclass
.iterator();
assertSame("Should be same", EnumWithInnerClass.a, iteratorWithSubclass.next());
assertTrue("Should return true", iteratorWithSubclass.hasNext());
assertSame("Should be same", EnumWithInnerClass.b, iteratorWithSubclass.next());
setWithSubclass.remove(EnumWithInnerClass.c);
assertTrue("Should return true", iteratorWithSubclass.hasNext());
assertSame("Should be same", EnumWithInnerClass.c, iteratorWithSubclass.next());
assertTrue("Should return true", iteratorWithSubclass.hasNext());
assertSame("Should be same", EnumWithInnerClass.d, iteratorWithSubclass.next());
setWithSubclass.add(EnumWithInnerClass.e);
assertTrue("Should return true", iteratorWithSubclass.hasNext());
assertSame("Should be same", EnumWithInnerClass.f, iteratorWithSubclass.next());
set = EnumSet.noneOf(EnumFoo.class);
iterator = set.iterator();
try {
iterator.next();
fail("Should throw NoSuchElementException");
} catch (NoSuchElementException e) {
// expected
}
set.add(EnumFoo.a);
iterator = set.iterator();
assertEquals("Should return EnumFoo.a", EnumFoo.a, iterator.next());
assertEquals("Size of set should be 1", 1, set.size());
iterator.remove();
assertEquals("Size of set should be 0", 0, set.size());
assertFalse("Should return false", set.contains(EnumFoo.a));
set.add(EnumFoo.a);
set.add(EnumFoo.b);
iterator = set.iterator();
assertEquals("Should be equals", EnumFoo.a, iterator.next());
iterator.remove();
try {
iterator.remove();
fail("Should throw IllegalStateException");
} catch (IllegalStateException e) {
// expected
}
assertTrue("Should have next element", iterator.hasNext());
try {
iterator.remove();
fail("Should throw IllegalStateException");
} catch (IllegalStateException e) {
// expected
}
assertEquals("Size of set should be 1", 1, set.size());
assertTrue("Should have next element", iterator.hasNext());
assertEquals("Should return EnumFoo.b", EnumFoo.b, iterator.next());
set.remove(EnumFoo.b);
assertEquals("Size of set should be 0", 0, set.size());
iterator.remove();
assertFalse("Should return false", set.contains(EnumFoo.a));
assertFalse("Should return false", set.contains(EnumFoo.b));
// test enum type with more than 64 elements
Set<HugeEnum> hugeSet = EnumSet.noneOf(HugeEnum.class);
hugeSet.add(HugeEnum.a);
hugeSet.add(HugeEnum.b);
Iterator<HugeEnum> hIterator = hugeSet.iterator();
Iterator<HugeEnum> anotherHugeIterator = hugeSet.iterator();
assertNotSame(hIterator, anotherHugeIterator);
try {
hIterator.remove();
fail("Should throw IllegalStateException");
} catch (IllegalStateException e) {
// expected
}
assertTrue(hIterator.hasNext());
assertSame(HugeEnum.a, hIterator.next());
hIterator.remove();
assertTrue(hIterator.hasNext());
assertSame(HugeEnum.b, hIterator.next());
assertFalse(hIterator.hasNext());
assertFalse(hIterator.hasNext());
assertEquals(1, hugeSet.size());
try {
hIterator.next();
fail("Should throw NoSuchElementException");
} catch (NoSuchElementException e) {
// expected
}
Set<HugeEnumWithInnerClass> hugeSetWithSubclass = EnumSet
.allOf(HugeEnumWithInnerClass.class);
hugeSetWithSubclass.remove(HugeEnumWithInnerClass.e);
Iterator<HugeEnumWithInnerClass> hugeIteratorWithSubclass = hugeSetWithSubclass.iterator();
assertSame(HugeEnumWithInnerClass.a, hugeIteratorWithSubclass.next());
assertTrue(hugeIteratorWithSubclass.hasNext());
assertSame(HugeEnumWithInnerClass.b, hugeIteratorWithSubclass.next());
setWithSubclass.remove(HugeEnumWithInnerClass.c);
assertTrue(hugeIteratorWithSubclass.hasNext());
assertSame(HugeEnumWithInnerClass.c, hugeIteratorWithSubclass.next());
assertTrue(hugeIteratorWithSubclass.hasNext());
assertSame(HugeEnumWithInnerClass.d, hugeIteratorWithSubclass.next());
hugeSetWithSubclass.add(HugeEnumWithInnerClass.e);
assertTrue(hugeIteratorWithSubclass.hasNext());
assertSame(HugeEnumWithInnerClass.f, hugeIteratorWithSubclass.next());
hugeSet = EnumSet.noneOf(HugeEnum.class);
hIterator = hugeSet.iterator();
try {
hIterator.next();
fail("Should throw NoSuchElementException");
} catch (NoSuchElementException e) {
// expected
}
hugeSet.add(HugeEnum.a);
hIterator = hugeSet.iterator();
assertEquals(HugeEnum.a, hIterator.next());
assertEquals(1, hugeSet.size());
hIterator.remove();
assertEquals(0, hugeSet.size());
assertFalse(hugeSet.contains(HugeEnum.a));
hugeSet.add(HugeEnum.a);
hugeSet.add(HugeEnum.b);
hIterator = hugeSet.iterator();
hIterator.next();
hIterator.remove();
assertTrue(hIterator.hasNext());
try {
hIterator.remove();
fail("Should throw IllegalStateException");
} catch (IllegalStateException e) {
// expected
}
assertEquals(1, hugeSet.size());
assertTrue(hIterator.hasNext());
assertEquals(HugeEnum.b, hIterator.next());
hugeSet.remove(HugeEnum.b);
assertEquals(0, hugeSet.size());
hIterator.remove();
assertFalse(hugeSet.contains(HugeEnum.a));
assertFalse("Should return false", set.contains(EnumFoo.b));
}
enum EnumWithInnerClass {
a, b, c, d, e, f {
},
}
enum EnumFoo {
a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
aa, bb, cc, dd, ee, ff, gg, hh, ii, jj, kk, ll,
}
enum EmptyEnum {
// expected
}
enum HugeEnumWithInnerClass {
a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
aa, bb, cc, dd, ee, ff, gg, hh, ii, jj, kk, ll,
mm {
},
}
enum HugeEnum {
a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
aa, bb, cc, dd, ee, ff, gg, hh, ii, jj, kk, ll, mm,
}
enum L { 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, 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 E24, E25, E26, E27, E28, E29, E30, E31, E32, E33, E34, E35, E36