diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/THashMap.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/THashMap.java index 97316312f..16890395f 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/THashMap.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/THashMap.java @@ -1,3 +1,18 @@ +/* + * Copyright 2014 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. + */ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -28,23 +43,23 @@ import org.teavm.javascript.ni.Rename; public class THashMap extends TAbstractMap implements TSerializable { transient int elementCount; - transient Entry[] elementData; + transient HashEntry[] elementData; transient int modCount = 0; private static final int DEFAULT_SIZE = 16; final float loadFactor; int threshold; - static class Entry extends TMapEntry { + static class HashEntry extends TMapEntry { final int origKeyHash; - Entry next; + HashEntry next; - Entry(K theKey, int hash) { + HashEntry(K theKey, int hash) { super(theKey, null); this.origKeyHash = hash; } - Entry(K theKey, V theValue) { + HashEntry(K theKey, V theValue) { super(theKey, theValue); origKeyHash = (theKey == null ? 0 : computeHashCode(theKey)); } @@ -52,9 +67,9 @@ public class THashMap extends TAbstractMap implements TSerializable @Override @SuppressWarnings("unchecked") public Object clone() { - Entry entry = (Entry) super.clone(); + HashEntry entry = (HashEntry) super.clone(); if (next != null) { - entry.next = (Entry) next.clone(); + entry.next = (HashEntry) next.clone(); } return entry; } @@ -63,9 +78,9 @@ public class THashMap extends TAbstractMap implements TSerializable private static class AbstractMapIterator { private int position = 0; int expectedModCount; - Entry futureEntry; - Entry currentEntry; - Entry prevEntry; + HashEntry futureEntry; + HashEntry currentEntry; + HashEntry prevEntry; final THashMap associatedMap; @@ -228,8 +243,8 @@ public class THashMap extends TAbstractMap implements TSerializable } @SuppressWarnings("unchecked") - Entry[] newElementArray(int s) { - return new Entry[s]; + HashEntry[] newElementArray(int s) { + return new HashEntry[s]; } public THashMap() { @@ -315,7 +330,7 @@ public class THashMap extends TAbstractMap implements TSerializable @Override public boolean containsKey(Object key) { - Entry m = getEntry(key); + HashEntry m = getEntry(key); return m != null; } @@ -323,7 +338,7 @@ public class THashMap extends TAbstractMap implements TSerializable public boolean containsValue(Object value) { if (value != null) { for (int i = 0; i < elementData.length; i++) { - Entry entry = elementData[i]; + HashEntry entry = elementData[i]; while (entry != null) { if (areEqualValues(value, entry.value)) { return true; @@ -333,7 +348,7 @@ public class THashMap extends TAbstractMap implements TSerializable } } else { for (int i = 0; i < elementData.length; i++) { - Entry entry = elementData[i]; + HashEntry entry = elementData[i]; while (entry != null) { if (entry.value == null) { return true; @@ -352,15 +367,15 @@ public class THashMap extends TAbstractMap implements TSerializable @Override public V get(Object key) { - Entry m = getEntry(key); + HashEntry m = getEntry(key); if (m != null) { return m.value; } return null; } - final Entry getEntry(Object key) { - Entry m; + final HashEntry getEntry(Object key) { + HashEntry m; if (key == null) { m = findNullKeyEntry(); } else { @@ -371,8 +386,8 @@ public class THashMap extends TAbstractMap implements TSerializable return m; } - final Entry findNonNullKeyEntry(Object key, int index, int keyHash) { - Entry m = elementData[index]; + final HashEntry findNonNullKeyEntry(Object key, int index, int keyHash) { + HashEntry m = elementData[index]; while (m != null && (m.origKeyHash != keyHash || !areEqualKeys(key, m.key))) { m = m.next; @@ -380,8 +395,8 @@ public class THashMap extends TAbstractMap implements TSerializable return m; } - final Entry findNullKeyEntry() { - Entry m = elementData[0]; + final HashEntry findNullKeyEntry() { + HashEntry m = elementData[0]; while (m != null && m.key != null) m = m.next; return m; @@ -406,7 +421,7 @@ public class THashMap extends TAbstractMap implements TSerializable THashMap.this.clear(); } @Override public boolean remove(Object key) { - Entry entry = THashMap.this.removeEntry(key); + HashEntry entry = THashMap.this.removeEntry(key); return entry != null; } @Override public TIterator iterator() { @@ -423,7 +438,7 @@ public class THashMap extends TAbstractMap implements TSerializable } V putImpl(K key, V value) { - Entry entry; + HashEntry entry; if(key == null) { entry = findNullKeyEntry(); if (entry == null) { @@ -451,15 +466,15 @@ public class THashMap extends TAbstractMap implements TSerializable return result; } - Entry createEntry(K key, int index, V value) { - Entry entry = new Entry<>(key, value); + HashEntry createEntry(K key, int index, V value) { + HashEntry entry = new HashEntry<>(key, value); entry.next = elementData[index]; elementData[index] = entry; return entry; } - Entry createHashedEntry(K key, int index, int hash) { - Entry entry = new Entry<>(key,hash); + HashEntry createHashedEntry(K key, int index, int hash) { + HashEntry entry = new HashEntry<>(key,hash); entry.next = elementData[index]; elementData[index] = entry; return entry; @@ -487,13 +502,13 @@ public class THashMap extends TAbstractMap implements TSerializable void rehash(int capacity) { int length = calculateCapacity((capacity == 0 ? 1 : capacity << 1)); - Entry[] newData = newElementArray(length); + HashEntry[] newData = newElementArray(length); for (int i = 0; i < elementData.length; i++) { - Entry entry = elementData[i]; + HashEntry entry = elementData[i]; elementData[i] = null; while (entry != null) { int index = entry.origKeyHash & (length - 1); - Entry next = entry.next; + HashEntry next = entry.next; entry.next = newData[index]; newData[index] = entry; entry = next; @@ -509,16 +524,16 @@ public class THashMap extends TAbstractMap implements TSerializable @Override public V remove(Object key) { - Entry entry = removeEntry(key); + HashEntry entry = removeEntry(key); if (entry != null) { return entry.value; } return null; } - final void removeEntry(Entry entry) { + final void removeEntry(HashEntry entry) { int index = entry.origKeyHash & (elementData.length - 1); - Entry m = elementData[index]; + HashEntry m = elementData[index]; if (m == entry) { elementData[index] = entry.next; } else { @@ -532,10 +547,10 @@ public class THashMap extends TAbstractMap implements TSerializable elementCount--; } - final Entry removeEntry(Object key) { + final HashEntry removeEntry(Object key) { int index = 0; - Entry entry; - Entry last = null; + HashEntry entry; + HashEntry last = null; if (key != null) { int hash = computeHashCode(key); index = hash & (elementData.length - 1); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLinkedHashMap.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLinkedHashMap.java new file mode 100644 index 000000000..ca57f3cae --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLinkedHashMap.java @@ -0,0 +1,489 @@ +/* + * Copyright 2014 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. + */ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.teavm.classlib.java.lang.TIllegalStateException; + +public class TLinkedHashMap extends THashMap implements TMap { + private final boolean accessOrder; + + transient private LinkedHashMapEntry head, tail; + + public TLinkedHashMap() { + accessOrder = false; + head = null; + } + + public TLinkedHashMap(int s) { + super(s); + accessOrder = false; + head = null; + } + + public TLinkedHashMap(int s, float lf) { + super(s, lf); + accessOrder = false; + head = null; + tail = null; + } + + public TLinkedHashMap(int s, float lf, boolean order) { + super(s, lf); + accessOrder = order; + head = null; + tail = null; + } + + public TLinkedHashMap(TMap m) { + accessOrder = false; + head = null; + tail = null; + putAll(m); + } + + private static class AbstractMapIterator { + int expectedModCount; + LinkedHashMapEntry futureEntry; + LinkedHashMapEntry currentEntry; + final TLinkedHashMap associatedMap; + + AbstractMapIterator(TLinkedHashMap map) { + expectedModCount = map.modCount; + futureEntry = map.head; + associatedMap = map; + } + + public boolean hasNext() { + return (futureEntry != null); + } + + final void checkConcurrentMod() throws TConcurrentModificationException { + if (expectedModCount != associatedMap.modCount) { + throw new TConcurrentModificationException(); + } + } + + final void makeNext() { + checkConcurrentMod(); + if (!hasNext()) { + throw new TNoSuchElementException(); + } + currentEntry = futureEntry; + futureEntry = futureEntry.chainForward; + } + + public void remove() { + checkConcurrentMod(); + if (currentEntry==null) { + throw new TIllegalStateException(); + } + associatedMap.removeEntry(currentEntry); + LinkedHashMapEntry lhme = currentEntry; + LinkedHashMapEntry p = lhme.chainBackward; + LinkedHashMapEntry n = lhme.chainForward; + TLinkedHashMap lhm = associatedMap; + if (p != null) { + p.chainForward = n; + if (n != null) { + n.chainBackward = p; + } else { + lhm.tail = p; + } + } else { + lhm.head = n; + if (n != null) { + n.chainBackward = null; + } else { + lhm.tail = null; + } + } + currentEntry = null; + expectedModCount++; + } + } + + private static class EntryIterator extends AbstractMapIterator implements TIterator> { + EntryIterator (TLinkedHashMap map) { + super(map); + } + + @Override + public Entry next() { + makeNext(); + return currentEntry; + } + } + + private static class KeyIterator extends AbstractMapIterator implements TIterator { + KeyIterator (TLinkedHashMap map) { + super(map); + } + + @Override + public K next() { + makeNext(); + return currentEntry.key; + } + } + + private static class ValueIterator extends AbstractMapIterator implements TIterator { + ValueIterator (TLinkedHashMap map) { + super(map); + } + + @Override + public V next() { + makeNext(); + return currentEntry.value; + } + } + + static final class LinkedHashMapEntrySet extends HashMapEntrySet { + public LinkedHashMapEntrySet(TLinkedHashMap lhm) { + super(lhm); + } + + @Override + public TIterator> iterator() { + return new EntryIterator<>((TLinkedHashMap) hashMap()); + } + } + + static final class LinkedHashMapEntry extends HashEntry { + LinkedHashMapEntry chainForward, chainBackward; + + LinkedHashMapEntry(K theKey, V theValue) { + super(theKey, theValue); + chainForward = null; + chainBackward = null; + } + + LinkedHashMapEntry(K theKey, int hash) { + super(theKey, hash); + chainForward = null; + chainBackward = null; + } + + @Override + @SuppressWarnings("unchecked") + public Object clone() { + LinkedHashMapEntry entry = (LinkedHashMapEntry) super.clone(); + entry.chainBackward = chainBackward; + entry.chainForward = chainForward; + LinkedHashMapEntry lnext = (LinkedHashMapEntry) entry.next; + if (lnext != null) { + entry.next = (LinkedHashMapEntry) lnext.clone(); + } + return entry; + } + } + + @Override + public boolean containsValue(Object value) { + LinkedHashMapEntry entry = head; + if (null == value) { + while (null != entry) { + if (null == entry.value) { + return true; + } + entry = entry.chainForward; + } + } else { + while (null != entry) { + if (value.equals(entry.value)) { + return true; + } + entry = entry.chainForward; + } + } + return false; + } + + @Override + @SuppressWarnings("unchecked") + HashEntry[] newElementArray(int s) { + return new LinkedHashMapEntry[s]; + } + + @Override + public V get(Object key) { + LinkedHashMapEntry m; + if (key == null) { + m = (LinkedHashMapEntry) findNullKeyEntry(); + } else { + int hash = key.hashCode(); + int index = (hash & 0x7FFFFFFF) % elementData.length; + m = (LinkedHashMapEntry) findNonNullKeyEntry(key, index, hash); + } + if (m == null) { + return null; + } + if (accessOrder && tail != m) { + LinkedHashMapEntry p = m.chainBackward; + LinkedHashMapEntry n = m.chainForward; + n.chainBackward = p; + if (p != null) { + p.chainForward = n; + } else { + head = n; + } + m.chainForward = null; + m.chainBackward = tail; + tail.chainForward = m; + tail = m; + } + return m.value; + } + + @Override + HashEntry createEntry(K key, int index, V value) { + LinkedHashMapEntry m = new LinkedHashMapEntry<>(key, value); + m.next = elementData[index]; + elementData[index] = m; + linkEntry(m); + return m; + } + + @Override + HashEntry createHashedEntry(K key, int index, int hash) { + LinkedHashMapEntry m = new LinkedHashMapEntry<>(key, hash); + m.next = elementData[index]; + elementData[index] = m; + linkEntry(m); + return m; + } + + @Override + public V put(K key, V value) { + V result = putImpl(key, value); + + if (removeEldestEntry(head)) { + remove(head.key); + } + + return result; + } + + @Override + V putImpl(K key, V value) { + LinkedHashMapEntry m; + if (elementCount == 0) { + head = tail = null; + } + if (key == null) { + m = (LinkedHashMapEntry) findNullKeyEntry(); + if (m == null) { + modCount++; + // Check if we need to remove the oldest entry. The check + // includes accessOrder since an accessOrder LinkedHashMap does + // not record the oldest member in 'head'. + if (++elementCount > threshold) { + rehash(); + } + m = (LinkedHashMapEntry) createHashedEntry(null, 0, 0); + } else { + linkEntry(m); + } + } else { + int hash = key.hashCode(); + int index = (hash & 0x7FFFFFFF) % elementData.length; + m = (LinkedHashMapEntry) findNonNullKeyEntry(key, index, hash); + if (m == null) { + modCount++; + if (++elementCount > threshold) { + rehash(); + index = (hash & 0x7FFFFFFF) % elementData.length; + } + m = (LinkedHashMapEntry) createHashedEntry(key, index, hash); + } else { + linkEntry(m); + } + } + + V result = m.value; + m.value = value; + return result; + } + + void linkEntry(LinkedHashMapEntry m) { + if (tail == m) { + return; + } + + if (head == null) { + // Check if the map is empty + head = tail = m; + return; + } + + // we need to link the new entry into either the head or tail + // of the chain depending on if the LinkedHashMap is accessOrder or not + LinkedHashMapEntry p = m.chainBackward; + LinkedHashMapEntry n = m.chainForward; + if (p == null) { + if (n != null) { + // The entry must be the head but not the tail + if (accessOrder) { + head = n; + n.chainBackward = null; + m.chainBackward = tail; + m.chainForward = null; + tail.chainForward = m; + tail = m; + } + } else { + // This is a new entry + m.chainBackward = tail; + m.chainForward = null; + tail.chainForward = m; + tail = m; + } + return; + } + + if (n == null) { + // The entry must be the tail so we can't get here + return; + } + + // The entry is neither the head nor tail + if (accessOrder) { + p.chainForward = n; + n.chainBackward = p; + m.chainForward = null; + m.chainBackward = tail; + tail.chainForward = m; + tail = m; + } + } + + @Override + public TSet> entrySet() { + return new LinkedHashMapEntrySet<>(this); + } + + @Override + public TSet keySet() { + if (cachedKeySet == null) { + cachedKeySet = new TAbstractSet() { + @Override + public boolean contains(Object object) { + return containsKey(object); + } + + @Override + public int size() { + return TLinkedHashMap.this.size(); + } + + @Override + public void clear() { + TLinkedHashMap.this.clear(); + } + + @Override + public boolean remove(Object key) { + if (containsKey(key)) { + TLinkedHashMap.this.remove(key); + return true; + } + return false; + } + + @Override + public TIterator iterator() { + return new KeyIterator<>(TLinkedHashMap.this); + } + }; + } + return cachedKeySet; + } + + @Override + public TCollection values() { + if (cachedValues == null) { + cachedValues = new TAbstractCollection() { + @Override + public boolean contains(Object object) { + return containsValue(object); + } + + @Override + public int size() { + return TLinkedHashMap.this.size(); + } + + @Override + public void clear() { + TLinkedHashMap.this.clear(); + } + + @Override + public TIterator iterator() { + return new ValueIterator<>(TLinkedHashMap.this); + } + }; + } + return cachedValues; + } + + @Override + public V remove(Object key) { + LinkedHashMapEntry m = (LinkedHashMapEntry) removeEntry(key); + if (m == null) { + return null; + } + LinkedHashMapEntry p = m.chainBackward; + LinkedHashMapEntry n = m.chainForward; + if (p != null) { + p.chainForward = n; + } else { + head = n; + } + if (n != null) { + n.chainBackward = p; + } else { + tail = p; + } + return m.value; + } + + protected boolean removeEldestEntry(@SuppressWarnings("unused") Entry eldest) { + return false; + } + + @Override + public void clear() { + super.clear(); + head = tail = null; + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLinkedHashSet.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLinkedHashSet.java new file mode 100644 index 000000000..d65f30e42 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLinkedHashSet.java @@ -0,0 +1,62 @@ +/* + * Copyright 2014 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. + */ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.teavm.classlib.java.io.TSerializable; +import org.teavm.classlib.java.lang.TCloneable; + +public class TLinkedHashSet extends THashSet implements TSet, TCloneable, TSerializable { + public TLinkedHashSet() { + super(new TLinkedHashMap>()); + } + + public TLinkedHashSet(int capacity) { + super(new TLinkedHashMap>(capacity)); + } + + public TLinkedHashSet(int capacity, float loadFactor) { + super(new TLinkedHashMap>(capacity, loadFactor)); + } + + public TLinkedHashSet(TCollection collection) { + super(new TLinkedHashMap>(collection.size() < 6 ? 11 : collection.size() * 2)); + for (TIterator iter = collection.iterator(); iter.hasNext();) { + add(iter.next()); + } + } + + /* overrides method in HashMap */ + @Override + THashMap> createBackingMap(int capacity, float loadFactor) { + return new TLinkedHashMap<>(capacity, loadFactor); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/LinkedHashMapTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/LinkedHashMapTest.java new file mode 100644 index 000000000..1a3b32b95 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/LinkedHashMapTest.java @@ -0,0 +1,503 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.util.Map.Entry; +import org.junit.Test; +import org.teavm.classlib.support.Support_MapTest2; + +/** + * @tests java.util.LinkedHashMap + */ +public class LinkedHashMapTest { + + LinkedHashMap hm; + + final static int hmSize = 1000; + + static Object[] objArray; + + static Object[] objArray2; + { + objArray = new Object[hmSize]; + objArray2 = new Object[hmSize]; + for (int i = 0; i < objArray.length; i++) { + objArray[i] = new Integer(i); + objArray2[i] = objArray[i].toString(); + } + } + + public LinkedHashMapTest() { + hm = new LinkedHashMap<>(); + for (int i = 0; i < objArray.length; i++) + hm.put(objArray2[i], objArray[i]); + hm.put("test", null); + hm.put(null, "test"); + } + + @Test + public void test_Constructor() { + // Test for method java.util.LinkedHashMap() + new Support_MapTest2(new LinkedHashMap()).runTest(); + + LinkedHashMap hm2 = new LinkedHashMap<>(); + assertEquals("Created incorrect LinkedHashMap", 0, hm2.size()); + } + + @Test + public void test_ConstructorI() { + // Test for method java.util.LinkedHashMap(int) + LinkedHashMap hm2 = new LinkedHashMap<>(5); + assertEquals("Created incorrect LinkedHashMap", 0, hm2.size()); + try { + new LinkedHashMap<>(-1); + fail("Failed to throw IllegalArgumentException for initial capacity < 0"); + } catch (IllegalArgumentException e) { + } + + LinkedHashMap empty = new LinkedHashMap<>(0); + assertNull("Empty LinkedHashMap access", empty.get("nothing")); + empty.put("something", "here"); + assertTrue("cannot get element", empty.get("something") == "here"); + } + + @Test + public void test_ConstructorIF() { + // Test for method java.util.LinkedHashMap(int, float) + LinkedHashMap hm2 = new LinkedHashMap<>(5, (float) 0.5); + assertEquals("Created incorrect LinkedHashMap", 0, hm2.size()); + try { + new LinkedHashMap<>(0, 0); + fail("Failed to throw IllegalArgumentException for initial load factor <= 0"); + } catch (IllegalArgumentException e) { + } + LinkedHashMap empty = new LinkedHashMap<>(0, 0.75f); + assertNull("Empty hashtable access", empty.get("nothing")); + empty.put("something", "here"); + assertTrue("cannot get element", empty.get("something") == "here"); + } + + @Test + public void test_ConstructorLjava_util_Map() { + // Test for method java.util.LinkedHashMap(java.util.Map) + Map myMap = new TreeMap<>(); + for (int counter = 0; counter < hmSize; counter++) + myMap.put(objArray2[counter], objArray[counter]); + LinkedHashMap hm2 = new LinkedHashMap<>(myMap); + for (int counter = 0; counter < hmSize; counter++) + assertSame("Failed to construct correct LinkedHashMap", hm.get(objArray2[counter]), + hm2.get(objArray2[counter])); + } + + @Test + public void test_getLjava_lang_Object() { + // Test for method java.lang.Object + // java.util.LinkedHashMap.get(java.lang.Object) + assertNull("Get returned non-null for non existent key", hm.get("T")); + hm.put("T", "HELLO"); + assertEquals("Get returned incorecct value for existing key", "HELLO", hm.get("T")); + + LinkedHashMap m = new LinkedHashMap<>(); + m.put(null, "test"); + assertEquals("Failed with null key", "test", m.get(null)); + assertNull("Failed with missing key matching null hash", m.get(new Integer(0))); + } + + @Test + public void test_putLjava_lang_ObjectLjava_lang_Object() { + // Test for method java.lang.Object + // java.util.LinkedHashMap.put(java.lang.Object, java.lang.Object) + hm.put("KEY", "VALUE"); + assertEquals("Failed to install key/value pair", "VALUE", hm.get("KEY")); + + LinkedHashMap m = new LinkedHashMap<>(); + m.put(new Short((short) 0), "short"); + m.put(null, "test"); + m.put(new Integer(0), "int"); + assertEquals("Failed adding to bucket containing null", "short", m.get(new Short((short) 0))); + assertEquals("Failed adding to bucket containing null2", "int", m.get(new Integer(0))); + } + + @Test + public void test_putAllLjava_util_Map() { + LinkedHashMap hm2 = new LinkedHashMap<>(); + hm2.putAll(hm); + for (int i = 0; i < 1000; i++) + assertTrue("Failed to clear all elements", hm2.get(new Integer(i).toString()).equals((new Integer(i)))); + } + + @Test + public void test_entrySet() {; + assertEquals("Returned set of incorrect size", hm.size(), hm.entrySet().size()); + for (Entry m : hm.entrySet()) { + assertTrue("Returned incorrect entry set", hm.containsKey(m.getKey()) && hm.containsValue(m.getValue())); + } + } + + @Test + public void test_keySet() { + // Test for method java.util.Set java.util.LinkedHashMap.keySet() + Set s = hm.keySet(); + assertEquals("Returned set of incorrect size()", s.size(), hm.size()); + for (int i = 0; i < objArray.length; i++) + assertTrue("Returned set does not contain all keys", s.contains(objArray[i].toString())); + + LinkedHashMap m = new LinkedHashMap<>(); + m.put(null, "test"); + assertTrue("Failed with null key", m.keySet().contains(null)); + assertNull("Failed with null key", m.keySet().iterator().next()); + + Map map = new LinkedHashMap<>(101); + map.put(new Integer(1), "1"); + map.put(new Integer(102), "102"); + map.put(new Integer(203), "203"); + Iterator it = map.keySet().iterator(); + Integer remove1 = it.next(); + it.hasNext(); + it.remove(); + Integer remove2 = it.next(); + it.remove(); + ArrayList list = new ArrayList<>(Arrays.asList(new Integer[] { 1, 102, 203 })); + list.remove(remove1); + list.remove(remove2); + assertTrue("Wrong result", it.next().equals(list.get(0))); + assertEquals("Wrong size", 1, map.size()); + assertTrue("Wrong contents", map.keySet().iterator().next().equals(list.get(0))); + + Map map2 = new LinkedHashMap<>(101); + map2.put(new Integer(1), "1"); + map2.put(new Integer(4), "4"); + Iterator it2 = map2.keySet().iterator(); + Integer remove3 = it2.next(); + Integer next; + if (remove3.intValue() == 1) + next = new Integer(4); + else + next = new Integer(1); + it2.hasNext(); + it2.remove(); + assertTrue("Wrong result 2", it2.next().equals(next)); + assertEquals("Wrong size 2", 1, map2.size()); + assertTrue("Wrong contents 2", map2.keySet().iterator().next().equals(next)); + } + + @Test + public void test_values() { + // Test for method java.util.Collection java.util.LinkedHashMap.values() + Collection c = hm.values(); + assertTrue("Returned collection of incorrect size()", c.size() == hm.size()); + for (int i = 0; i < objArray.length; i++) + assertTrue("Returned collection does not contain all keys", c.contains(objArray[i])); + + LinkedHashMap myLinkedHashMap = new LinkedHashMap<>(); + for (int i = 0; i < 100; i++) + myLinkedHashMap.put(objArray2[i], objArray[i]); + Collection values = myLinkedHashMap.values(); + values.remove(0); + assertTrue("Removing from the values collection should remove from the original map", + !myLinkedHashMap.containsValue(0)); + + } + + @Test + public void test_removeLjava_lang_Object() { + // Test for method java.lang.Object + // java.util.LinkedHashMap.remove(java.lang.Object) + int size = hm.size(); + Integer y = new Integer(9); + Integer x = ((Integer) hm.remove(y.toString())); + assertTrue("Remove returned incorrect value", x.equals(new Integer(9))); + assertNull("Failed to remove given key", hm.get(new Integer(9))); + assertTrue("Failed to decrement size", hm.size() == (size - 1)); + assertNull("Remove of non-existent key returned non-null", hm.remove("LCLCLC")); + + LinkedHashMap m = new LinkedHashMap<>(); + m.put(null, "test"); + assertNull("Failed with same hash as null", m.remove(new Integer(0))); + assertEquals("Failed with null key", "test", m.remove(null)); + } + + @Test + public void test_clear() { + // Test for method void java.util.LinkedHashMap.clear() + hm.clear(); + assertEquals("Clear failed to reset size", 0, hm.size()); + for (int i = 0; i < hmSize; i++) + assertNull("Failed to clear all elements", hm.get(objArray2[i])); + + } + + @Test + public void test_clone() { + // Test for method java.lang.Object java.util.LinkedHashMap.clone() + @SuppressWarnings("unchecked") + LinkedHashMap hm2 = (LinkedHashMap) hm.clone(); + assertTrue("Clone answered equivalent LinkedHashMap", hm2 != hm); + for (int counter = 0; counter < hmSize; counter++) + assertTrue("Clone answered unequal LinkedHashMap", hm.get(objArray2[counter]) == + hm2.get(objArray2[counter])); + + LinkedHashMap map = new LinkedHashMap<>(); + map.put("key", "value"); + // get the keySet() and values() on the original Map + Set keys = map.keySet(); + Collection values = map.values(); + assertEquals("values() does not work", "value", values.iterator().next()); + assertEquals("keySet() does not work", "key", keys.iterator().next()); + @SuppressWarnings("unchecked") + AbstractMap map2 = (AbstractMap) map.clone(); + map2.put("key", "value2"); + Collection values2 = map2.values(); + assertTrue("values() is identical", values2 != values); + + // values() and keySet() on the cloned() map should be different + assertEquals("values() was not cloned", "value2", values2.iterator().next()); + map2.clear(); + map2.put("key2", "value3"); + Set key2 = map2.keySet(); + assertTrue("keySet() is identical", key2 != keys); + assertEquals("keySet() was not cloned", "key2", key2.iterator().next()); + } + + @Test + public void test_clone_Mock() { + LinkedHashMap hashMap = new MockMap(); + String value = "value a"; + hashMap.put("key", value); + MockMap cloneMap = (MockMap) hashMap.clone(); + assertEquals(value, cloneMap.get("key")); + assertEquals(hashMap, cloneMap); + assertEquals(1, cloneMap.num); + + hashMap.put("key", "value b"); + assertFalse(hashMap.equals(cloneMap)); + } + + class MockMap extends LinkedHashMap { + private static final long serialVersionUID = 1L; + int num; + + @Override + public String put(String k, String v) { + num++; + return super.put(k, v); + } + + @Override + protected boolean removeEldestEntry(Entry e) { + return num > 1; + } + } + + @Test + public void test_containsKeyLjava_lang_Object() { + // Test for method boolean + // java.util.LinkedHashMap.containsKey(java.lang.Object) + assertTrue("Returned false for valid key", hm.containsKey(String.valueOf(876))); + assertTrue("Returned true for invalid key", !hm.containsKey("KKDKDKD")); + + LinkedHashMap m = new LinkedHashMap<>(); + m.put(null, "test"); + assertTrue("Failed with null key", m.containsKey(null)); + assertTrue("Failed with missing key matching null hash", !m.containsKey(new Integer(0))); + } + + @Test + public void test_containsValueLjava_lang_Object() { + // Test for method boolean + // java.util.LinkedHashMap.containsValue(java.lang.Object) + assertTrue("Returned false for valid value", hm.containsValue(new Integer(875))); + assertTrue("Returned true for invalid valie", !hm.containsValue(new Integer(-9))); + } + + @Test + public void test_isEmpty() { + // Test for method boolean java.util.LinkedHashMap.isEmpty() + assertTrue("Returned false for new map", new LinkedHashMap<>().isEmpty()); + assertTrue("Returned true for non-empty", !hm.isEmpty()); + } + + @Test + public void test_size() { + // Test for method int java.util.LinkedHashMap.size() + assertTrue("Returned incorrect size", + hm.size() == (objArray.length + 2)); + } + + @Test + public void test_ordered_entrySet() { + int i; + int sz = 100; + LinkedHashMap lhm = new LinkedHashMap<>(); + for (i = 0; i < sz; i++) { + Integer ii = new Integer(i); + lhm.put(ii, ii.toString()); + } + + assertTrue("Returned set of incorrect size 1", lhm.size() == lhm.entrySet().size()); + i = 0; + for (Map.Entry m : lhm.entrySet()) { + Integer jj = m.getKey(); + assertTrue("Returned incorrect entry set 1", jj.intValue() == i++); + } + + LinkedHashMap lruhm = new LinkedHashMap<>(200, .75f, true); + for (i = 0; i < sz; i++) { + Integer ii = new Integer(i); + lruhm.put(ii, ii.toString()); + } + + Set> s3 = lruhm.entrySet(); + Iterator> it3 = s3.iterator(); + assertTrue("Returned set of incorrect size 2", lruhm.size() == s3.size()); + for (i = 0; i < sz && it3.hasNext(); i++) { + Map.Entry m = it3.next(); + assertTrue("Returned incorrect entry set 2", m.getKey().intValue() == i); + } + + /* fetch the even numbered entries to affect traversal order */ + int p = 0; + for (i = 0; i < sz; i += 2) { + String ii = lruhm.get(new Integer(i)); + p = p + Integer.parseInt(ii); + } + assertEquals("invalid sum of even numbers", 2450, p); + + Set> s2 = lruhm.entrySet(); + Iterator> it2 = s2.iterator(); + assertTrue("Returned set of incorrect size 3", lruhm.size() == s2.size()); + for (i = 1; i < sz && it2.hasNext(); i += 2) { + Entry m = it2.next(); + assertTrue("Returned incorrect entry set 3", m.getKey().intValue() == i); + } + for (i = 0; i < sz && it2.hasNext(); i += 2) { + Entry m = it2.next(); + assertTrue("Returned incorrect entry set 4", m.getKey().intValue() == i); + } + assertTrue("Entries left to iterate on", !it2.hasNext()); + } + + @Test + public void test_ordered_keySet() { + int i; + int sz = 100; + LinkedHashMap lhm = new LinkedHashMap<>(); + for (i = 0; i < sz; i++) { + Integer ii = new Integer(i); + lhm.put(ii, ii.toString()); + } + + Set s1 = lhm.keySet(); + Iterator it1 = s1.iterator(); + assertTrue("Returned set of incorrect size", lhm.size() == s1.size()); + for (i = 0; it1.hasNext(); i++) { + Integer jj = it1.next(); + assertTrue("Returned incorrect entry set", jj.intValue() == i); + } + + LinkedHashMap lruhm = new LinkedHashMap<>(200, .75f, true); + for (i = 0; i < sz; i++) { + Integer ii = new Integer(i); + lruhm.put(ii, ii.toString()); + } + + Set s3 = lruhm.keySet(); + Iterator it3 = s3.iterator(); + assertTrue("Returned set of incorrect size", lruhm.size() == s3.size()); + for (i = 0; i < sz && it3.hasNext(); i++) { + Integer jj = it3.next(); + assertTrue("Returned incorrect entry set", jj.intValue() == i); + } + + /* fetch the even numbered entries to affect traversal order */ + int p = 0; + for (i = 0; i < sz; i += 2) { + String ii = lruhm.get(new Integer(i)); + p = p + Integer.parseInt(ii); + } + assertEquals("invalid sum of even numbers", 2450, p); + + Set s2 = lruhm.keySet(); + Iterator it2 = s2.iterator(); + assertTrue("Returned set of incorrect size", lruhm.size() == s2.size()); + for (i = 1; i < sz && it2.hasNext(); i += 2) { + Integer jj = it2.next(); + assertTrue("Returned incorrect entry set", jj.intValue() == i); + } + for (i = 0; i < sz && it2.hasNext(); i += 2) { + Integer jj = it2.next(); + assertTrue("Returned incorrect entry set", jj.intValue() == i); + } + assertTrue("Entries left to iterate on", !it2.hasNext()); + } + + @Test + public void test_ordered_values() { + int i; + int sz = 100; + LinkedHashMap lhm = new LinkedHashMap<>(); + for (i = 0; i < sz; i++) { + Integer ii = new Integer(i); + lhm.put(ii, new Integer(i * 2)); + } + + Collection s1 = lhm.values(); + Iterator it1 = s1.iterator(); + assertTrue("Returned set of incorrect size 1", lhm.size() == s1.size()); + for (i = 0; it1.hasNext(); i++) { + Integer jj = it1.next(); + assertTrue("Returned incorrect entry set 1", jj.intValue() == i * 2); + } + + LinkedHashMap lruhm = new LinkedHashMap<>(200, .75f, true); + for (i = 0; i < sz; i++) { + Integer ii = new Integer(i); + lruhm.put(ii, new Integer(i * 2)); + } + + Collection s3 = lruhm.values(); + Iterator it3 = s3.iterator(); + assertTrue("Returned set of incorrect size", lruhm.size() == s3.size()); + for (i = 0; i < sz && it3.hasNext(); i++) { + Integer jj = it3.next(); + assertTrue("Returned incorrect entry set", jj.intValue() == i * 2); + } + + // fetch the even numbered entries to affect traversal order + int p = 0; + for (i = 0; i < sz; i += 2) { + Integer ii = lruhm.get(new Integer(i)); + p = p + ii.intValue(); + } + assertTrue("invalid sum of even numbers", p == 2450 * 2); + + Collection s2 = lruhm.values(); + Iterator it2 = s2.iterator(); + assertTrue("Returned set of incorrect size", lruhm.size() == s2.size()); + for (i = 1; i < sz && it2.hasNext(); i += 2) { + Integer jj = it2.next(); + assertTrue("Returned incorrect entry set", jj.intValue() == i * 2); + } + for (i = 0; i < sz && it2.hasNext(); i += 2) { + Integer jj = it2.next(); + assertTrue("Returned incorrect entry set", jj.intValue() == i * 2); + } + assertTrue("Entries left to iterate on", !it2.hasNext()); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/support/Support_MapTest2.java b/teavm-classlib/src/test/java/org/teavm/classlib/support/Support_MapTest2.java new file mode 100644 index 000000000..283824838 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/support/Support_MapTest2.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.support; + +import static org.junit.Assert.*; +import java.util.Map; + +public class Support_MapTest2 { + + Map map; + + public Support_MapTest2(Map m) { + super(); + map = m; + if (!map.isEmpty()) { + fail("Map must be empty"); + } + } + + public void runTest() { + try { + map.put("one", "1"); + assertEquals("size should be one", 1, map.size()); + map.clear(); + assertEquals("size should be zero", 0, map.size()); + assertTrue("Should not have entries", !map.entrySet().iterator().hasNext()); + assertTrue("Should not have keys", !map.keySet().iterator().hasNext()); + assertTrue("Should not have values", !map.values().iterator().hasNext()); + } catch (UnsupportedOperationException e) { + } + + try { + map.put("one", "1"); + assertEquals("size should be one", 1, map.size()); + map.remove("one"); + assertEquals("size should be zero", 0, map.size()); + assertTrue("Should not have entries", !map.entrySet().iterator().hasNext()); + assertTrue("Should not have keys", !map.keySet().iterator().hasNext()); + assertTrue("Should not have values", !map.values().iterator().hasNext()); + } catch (UnsupportedOperationException e) { + } + } + +}