diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTreeMap.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTreeMap.java index cb591eb93..4ccb984c0 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTreeMap.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTreeMap.java @@ -18,7 +18,7 @@ package org.teavm.classlib.java.util; import org.teavm.classlib.java.io.TSerializable; import org.teavm.classlib.java.lang.*; -public class TTreeMap extends TAbstractMap implements TCloneable, TSerializable, TSortedMap { +public class TTreeMap extends TAbstractMap implements TCloneable, TSerializable, TNavigableMap { static class TreeNode extends SimpleEntry { TreeNode left; TreeNode right; @@ -392,12 +392,12 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS } @Override - public TSortedMap headMap(K toKey) { + public TNavigableMap headMap(K toKey) { return new MapView<>(this, null, true, false, toKey, false, true, false); } @Override - public TSortedMap tailMap(K fromKey) { + public TNavigableMap tailMap(K fromKey) { return new MapView<>(this, fromKey, true, true, null, false, false, false); } @@ -419,6 +419,104 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS return node.getKey(); } + @Override + public Entry lowerEntry(K key) { + return null; + } + + @Override + public K lowerKey(K key) { + return null; + } + + @Override + public Entry floorEntry(K key) { + return null; + } + + @Override + public K floorKey(K key) { + return null; + } + + @Override + public Entry ceilingEntry(K key) { + return null; + } + + @Override + public K ceilingKey(K key) { + return null; + } + + @Override + public Entry higherEntry(K key) { + return null; + } + + @Override + public K higherKey(K key) { + return null; + } + + @Override + public Entry firstEntry() { + return firstNode(false); + } + + @Override + public Entry lastEntry() { + return firstNode(true); + } + + @Override + public Entry pollFirstEntry() { + TreeNode node = firstNode(false); + if (node != null) { + deleteNode(root, node.getKey()); + } + return node; + } + + @Override + public Entry pollLastEntry() { + TreeNode node = firstNode(true); + if (node != null) { + deleteNode(root, node.getKey()); + } + return node; + } + + @Override + public TNavigableMap descendingMap() { + return new MapView<>(this, null, false, false, null, false, false, true); + } + + @Override + public TNavigableSet navigableKeySet() { + return null; + } + + @Override + public TNavigableSet descendingKeySet() { + return null; + } + + @Override + public TNavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return new MapView<>(this, fromKey, fromInclusive, true, toKey, toInclusive, true, false); + } + + @Override + public TNavigableMap headMap(K toKey, boolean inclusive) { + return new MapView<>(this, null, false, false, toKey, inclusive, true, false); + } + + @Override + public TNavigableMap tailMap(K fromKey, boolean inclusive) { + return new MapView<>(this, fromKey, inclusive, true, null, false, false, false); + } + private TreeNode firstNode(boolean reverse) { TreeNode node = root; TreeNode prev = null; @@ -502,21 +600,39 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS @Override public TIterator> iterator() { + return !reverse ? ascendingIterator() : descendingIterator(); + } + + private TIterator> ascendingIterator() { TreeNode[] fromPath; - K from = !reverse ? this.from : this.to; - K to = !reverse ? this.to : this.from; if (fromChecked) { - fromPath = fromIncluded ? owner.pathToExactOrNext(from, reverse) : owner.pathToNext(from, reverse); + fromPath = fromIncluded ? owner.pathToExactOrNext(from, false) : owner.pathToNext(from, false); } else { - fromPath = owner.pathToFirst(reverse); + fromPath = owner.pathToFirst(false); } TreeNode toEntry; if (toChecked) { - toEntry = toIncluded ? owner.findExactOrNext(to, !reverse) : owner.findNext(to, !reverse); + toEntry = toIncluded ? owner.findExactOrNext(to, true) : owner.findNext(to, true); } else { - toEntry = owner.firstNode(!reverse); + toEntry = owner.firstNode(true); } - return new EntryIterator<>(owner, fromPath, toEntry, reverse); + return new EntryIterator<>(owner, fromPath, toEntry, false); + } + + private TIterator> descendingIterator() { + TreeNode[] toPath; + if (toChecked) { + toPath = toIncluded ? owner.pathToExactOrNext(to, true) : owner.pathToNext(to, true); + } else { + toPath = owner.pathToFirst(true); + } + TreeNode fromEntry; + if (fromChecked) { + fromEntry = fromIncluded ? owner.findExactOrNext(to, false) : owner.findNext(to, false); + } else { + fromEntry = owner.firstNode(false); + } + return new EntryIterator<>(owner, toPath, fromEntry, true); } @Override @@ -611,7 +727,7 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS } } - private static class MapView extends TAbstractMap implements TSortedMap, TSerializable { + private static class MapView extends TAbstractMap implements TNavigableMap, TSerializable { private int modCount = -1; private int cachedSize; private TTreeMap owner; @@ -656,31 +772,42 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS } private void checkKey(K key) { + if (!keyInRange(key)) { + throw new TIllegalArgumentException(); + } + } + + private boolean keyInRange(K key) { if (fromChecked) { int cmp = owner.comparator.compare(key, from); if (fromIncluded ? cmp < 0 : cmp <= 0) { - throw new TIllegalArgumentException(); + return false; } } if (toChecked) { int cmp = owner.comparator.compare(key, to); if (fromIncluded ? cmp > 0 : cmp >= 0) { - throw new TIllegalArgumentException(); + return false; } } + return true; } @SuppressWarnings("unchecked") @Override public V get(Object key) { - checkKey((K)key); + if (!keyInRange((K)key)) { + return null; + } return owner.get(key); } @SuppressWarnings("unchecked") @Override public V remove(Object key) { - checkKey((K)key); + if (!keyInRange((K)key)) { + return null; + } return owner.remove(key); } @@ -690,6 +817,15 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS return owner.put(key, value); } + @Override + public void clear() { + if (!fromChecked && !toChecked) { + owner.clear(); + } else { + super.clear(); + } + } + @Override public int size() { int size = cachedSize; @@ -742,29 +878,17 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS @Override public TSortedMap subMap(K fromKey, K toKey) { - checkKey(fromKey); - checkKey(toKey); - return new MapView<>(owner, fromKey, true, true, toKey, false, true, reverse); + return subMap(fromKey, true, toKey, false); } @Override public TSortedMap headMap(K toKey) { - checkKey(toKey); - if (!reverse) { - return new MapView<>(owner, from, fromIncluded, fromChecked, toKey, false, true, false); - } else { - return new MapView<>(owner, toKey, false, true, to, toIncluded, toChecked, true); - } + return headMap(toKey, false); } @Override public TSortedMap tailMap(K fromKey) { - checkKey(fromKey); - if (!reverse) { - return new MapView<>(owner, fromKey, true, true, to, toIncluded, toChecked, false); - } else { - return new MapView<>(owner, from, fromIncluded, toChecked, fromKey, true, true, true); - } + return tailMap(fromKey, true); } @Override @@ -816,5 +940,135 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS } return node; } + + @Override + public Entry lowerEntry(K key) { + // TODO: implement + return null; + } + + @Override + public K lowerKey(K key) { + // TODO: implement + return null; + } + + @Override + public Entry floorEntry(K key) { + // TODO: implement + return null; + } + + @Override + public K floorKey(K key) { + // TODO: implement + return null; + } + + @Override + public Entry ceilingEntry(K key) { + // TODO: implement + return null; + } + + @Override + public K ceilingKey(K key) { + // TODO: implement + return null; + } + + @Override + public Entry higherEntry(K key) { + // TODO: implement + return null; + } + + @Override + public K higherKey(K key) { + // TODO: implement + return null; + } + + @Override + public Entry firstEntry() { + return !reverse ? firstNode() : lastNode(); + } + + @Override + public Entry lastEntry() { + return !reverse ? lastNode() : firstNode(); + } + + @Override + public Entry pollFirstEntry() { + TreeNode node = !reverse ? firstNode() : lastNode(); + if (node != null) { + owner.deleteNode(owner.root, node.getKey()); + } + return node; + } + + @Override + public Entry pollLastEntry() { + TreeNode node = !reverse ? lastNode() : firstNode(); + if (node != null) { + owner.deleteNode(owner.root, node.getKey()); + } + return node; + } + + @Override + public TNavigableMap descendingMap() { + return new MapView<>(owner, from, fromIncluded, fromChecked, to, toIncluded, toChecked, !reverse); + } + + @Override + public TNavigableSet navigableKeySet() { + // TODO: implement + return null; + } + + @Override + public TNavigableSet descendingKeySet() { + // TODO: implement + return null; + } + + @Override + public TNavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + checkKey(fromKey); + checkKey(toKey); + if (!reverse) { + if (owner.comparator.compare(fromKey, toKey) > 0) { + throw new IllegalArgumentException(); + } + return new MapView<>(owner, fromKey, fromInclusive, true, toKey, toInclusive, true, false); + } else { + if (owner.comparator.compare(fromKey, toKey) < 0) { + throw new IllegalArgumentException(); + } + return new MapView<>(owner, toKey, toInclusive, true, fromKey, fromInclusive, true, true); + } + } + + @Override + public TNavigableMap headMap(K toKey, boolean inclusive) { + checkKey(toKey); + if (!reverse) { + return new MapView<>(owner, from, fromIncluded, fromChecked, toKey, inclusive, true, false); + } else { + return new MapView<>(owner, toKey, inclusive, true, to, toIncluded, toChecked, true); + } + } + + @Override + public TNavigableMap tailMap(K fromKey, boolean inclusive) { + checkKey(fromKey); + if (!reverse) { + return new MapView<>(owner, fromKey, inclusive, true, to, toIncluded, toChecked, false); + } else { + return new MapView<>(owner, from, fromIncluded, toChecked, fromKey, inclusive, true, true); + } + } } } diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/TreeMapTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/TreeMapTest.java index 62841e18f..0e6d30bea 100644 --- a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/TreeMapTest.java +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/TreeMapTest.java @@ -563,7 +563,80 @@ public class TreeMapTest { master.put("null", "0"); entry = master.entrySet().toArray(); assertFalse("Null-valued entry should not equal non-null-valued entry", - test_map.entrySet().contains(entry[0])); + test_map.entrySet().contains(entry[0])); } + @Test + public void mapReversed() { + NavigableMap map = createMapOfEvenNumbers(); + NavigableMap reversedMap = map.descendingMap(); + assertEquals("The first key of reverse map is wrong", Integer.valueOf(998), reversedMap.firstKey()); + assertEquals("The last key of reverse map is wrong", Integer.valueOf(0), reversedMap.lastKey()); + assertTrue("Reversed map does not contain element from original map", reversedMap.containsKey(256)); + assertEquals("Reversed map is of a wrong size", 500, reversedMap.size()); + assertNull(reversedMap.get(1000)); + Iterator keys = reversedMap.keySet().iterator(); + assertEquals("Wrong first element got from iterator", Integer.valueOf(998), keys.next()); + assertEquals("Wrong second element got from iterator", Integer.valueOf(996), keys.next()); + assertEquals("Wrong third element got from iterator", Integer.valueOf(994), keys.next()); + } + + @Test + public void submapReversed() { + NavigableMap map = createMapOfEvenNumbers(); + NavigableMap reversedMap = map.subMap(100, true, 201, true).descendingMap(); + assertEquals("The first key of map is wrong", Integer.valueOf(200), reversedMap.firstKey()); + assertEquals("The last key of map is wrong", Integer.valueOf(100), reversedMap.lastKey()); + assertTrue("Reversed map does not contain element from original map", reversedMap.containsKey(104)); + assertEquals("Reversed map is of a wrong size", 51, reversedMap.size()); + assertNull(reversedMap.get(103)); + assertNull(reversedMap.get(256)); + Iterator keys = reversedMap.keySet().iterator(); + assertEquals("Wrong first element got from iterator", Integer.valueOf(200), keys.next()); + assertEquals("Wrong second element got from iterator", Integer.valueOf(198), keys.next()); + assertEquals("Wrong third element got from iterator", Integer.valueOf(196), keys.next()); + } + + @Test + public void submapOfReverseSubmapObtained() { + NavigableMap map = createMapOfEvenNumbers(); + NavigableMap reversedMap = map.subMap(100, true, 901, true).descendingMap() + .subMap(800, false, 201, false); + assertEquals("The first key of map is wrong", Integer.valueOf(798), reversedMap.firstKey()); + assertEquals("The last key of map is wrong", Integer.valueOf(202), reversedMap.lastKey()); + assertTrue("Reversed map does not contain element from original map", reversedMap.containsKey(244)); + assertEquals("Reversed map is of a wrong size", 299, reversedMap.size()); + assertNull(reversedMap.get(225)); + assertNull(reversedMap.get(100)); + Iterator keys = reversedMap.keySet().iterator(); + assertEquals("Wrong first element got from iterator", Integer.valueOf(798), keys.next()); + assertEquals("Wrong second element got from iterator", Integer.valueOf(796), keys.next()); + assertEquals("Wrong third element got from iterator", Integer.valueOf(794), keys.next()); + } + + @Test + public void tailOfReverseSubmapObtained() { + NavigableMap map = createMapOfEvenNumbers(); + NavigableMap reversedMap = map.subMap(100, true, 901, true).descendingMap() + .tailMap(800, false); + assertEquals("The first key of map is wrong", Integer.valueOf(798), reversedMap.firstKey()); + assertEquals("The last key of map is wrong", Integer.valueOf(100), reversedMap.lastKey()); + assertTrue("Reversed map does not contain element from original map", reversedMap.containsKey(144)); + assertEquals("Reversed map is of a wrong size", 350, reversedMap.size()); + assertNull(reversedMap.get(225)); + assertNull(reversedMap.get(94)); + assertNull(reversedMap.get(908)); + Iterator keys = reversedMap.keySet().iterator(); + assertEquals("Wrong first element got from iterator", Integer.valueOf(798), keys.next()); + assertEquals("Wrong second element got from iterator", Integer.valueOf(796), keys.next()); + assertEquals("Wrong third element got from iterator", Integer.valueOf(794), keys.next()); + } + + private TreeMap createMapOfEvenNumbers() { + TreeMap treeMap = new TreeMap<>(); + for (int i = 0; i < 1000; i += 2) { + treeMap.put(i, String.valueOf(i)); + } + return treeMap; + } }