From 035c616b5ec10f0a0824afee8cc0af7c22b0a0a7 Mon Sep 17 00:00:00 2001 From: Sergey Kapralov Date: Thu, 4 Aug 2016 15:50:59 +0300 Subject: [PATCH 1/5] added TStaskOverflowError --- .../java/lang/TStackOverflowError.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 classlib/src/main/java/org/teavm/classlib/java/lang/TStackOverflowError.java diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TStackOverflowError.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TStackOverflowError.java new file mode 100644 index 000000000..7f3dfa2cd --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TStackOverflowError.java @@ -0,0 +1,31 @@ +/* + * Copyright 2016 skapral. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.lang; + +/** + * + * @author skapral + */ +public class TStackOverflowError extends TVirtualMachineError { + + public TStackOverflowError() { + super(); + } + + public TStackOverflowError(TString string) { + super(string); + } +} From dc4a937262a4440b3154c51693a10152cf047547 Mon Sep 17 00:00:00 2001 From: Sergey Kapralov Date: Tue, 23 Aug 2016 10:39:37 +0300 Subject: [PATCH 2/5] Added missing map methods --- .../org/teavm/classlib/java/util/TMap.java | 75 ++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java b/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java index 799db2ca0..2d078a6fd 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java @@ -15,6 +15,9 @@ */ package org.teavm.classlib.java.util; +import java.util.function.BiFunction; +import java.util.function.Function; + /** * * @author Alexey Andreev @@ -43,7 +46,7 @@ public interface TMap { V put(K key, V value); V remove(Object key); - + void putAll(TMap m); void clear(); @@ -53,4 +56,74 @@ public interface TMap { TCollection values(); TSet> entrySet(); + + default boolean replace(K key, V value, V newValue) { + if (containsKey(key) && TObjects.equals(get(key), value)) { + put(key, newValue); + return true; + } else { + return false; + } + } + + default V replace(K key, V value) { + if (containsKey(key)) { + return put(key, value); + } else { + return null; + } + } + + default V computeIfAbsent(K key, Function mappingFunction) { + V v = get(key); + if (v == null) { + V newValue = mappingFunction.apply(key); + if (newValue != null) { + put(key, newValue); + } + return newValue; + } + return v; + } + + default V computeIfPresent(K key, BiFunction remappingFunction) { + V v = get(key); + if (v != null) { + V oldValue = v; + V newValue = remappingFunction.apply(key, oldValue); + if (newValue != null) { + return put(key, newValue); + } else { + return remove(key); + } + } + return v; + } + + default V compute(K key, BiFunction remappingFunction) { + V oldValue = get(key); + V newValue = remappingFunction.apply(key, oldValue); + if (oldValue != null) { + if (newValue != null) { + return put(key, newValue); + } else { + return remove(key); + } + } else if (newValue != null) { + return put(key, newValue); + } else { + return null; + } + } + + default V merge(K key, V value, BiFunction remappingFunction) { + V oldValue = get(key); + V newValue = (oldValue == null) ? value + : remappingFunction.apply(oldValue, value); + if (newValue == null) { + return remove(key); + } else { + return put(key, newValue); + } + } } From ec2a52900e2c7aff14e9845ebcbb4772937ce838 Mon Sep 17 00:00:00 2001 From: Sergey Kapralov Date: Thu, 25 Aug 2016 11:13:08 +0300 Subject: [PATCH 3/5] TMap.compute fixed and tested --- .../org/teavm/classlib/java/util/TMap.java | 9 ++- .../classlib/java/util/HashtableTest.java | 57 +++++++++++++++++++ 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java b/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java index 2d078a6fd..30387d785 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java @@ -105,15 +105,14 @@ public interface TMap { V newValue = remappingFunction.apply(key, oldValue); if (oldValue != null) { if (newValue != null) { - return put(key, newValue); + put(key, newValue); } else { - return remove(key); + remove(key); } } else if (newValue != null) { - return put(key, newValue); - } else { - return null; + put(key, newValue); } + return newValue; } default V merge(K key, V value, BiFunction remappingFunction) { diff --git a/tests/src/test/java/org/teavm/classlib/java/util/HashtableTest.java b/tests/src/test/java/org/teavm/classlib/java/util/HashtableTest.java index 8478b05bd..deb94921d 100644 --- a/tests/src/test/java/org/teavm/classlib/java/util/HashtableTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/util/HashtableTest.java @@ -693,6 +693,63 @@ public class HashtableTest { } } + @Test + public void test_computeUpdatesValueIfPresent() { + Hashtable ht10 = new Hashtable<>(); + for(int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + String newVal = ht10.compute("Key5", (k,v) -> "changed"); + assertEquals("changed", newVal); + assertEquals(10, ht10.size()); + + for(int i = 0; i < 10; i++) { + if(i == 5) { + assertEquals("Value was incorrectly changed", "changed", ht10.get("Key" + i)); + } else { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + } + } + + @Test + public void test_computePutsNewEntryIfKeyIsAbsent() { + Hashtable ht10 = new Hashtable<>(); + for(int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + String newVal = ht10.compute("absent key", (k,v) -> "added"); + assertEquals("added", newVal); + assertEquals(11, ht10.size()); + + for(int i = 0; i < 10; i++) { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + assertEquals("New value expected","added", ht10.get("absent key")); + } + + @Test + public void test_computeRemovesEntryOnNullMapping() { + Hashtable ht10 = new Hashtable<>(); + for(int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + String newVal = ht10.compute("Key5", (k,v) -> null); + assertEquals(null, newVal); + assertEquals(9, ht10.size()); + + for(int i = 0; i < 10; i++) { + if(i == 5) { + assertEquals("Value was unexpectedly present in map", null, ht10.get("Key" + i)); + } else { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + } + } + @SuppressWarnings("unchecked") protected Hashtable hashtableClone(Hashtable s) { return (Hashtable) s.clone(); From 6b950e618ef894aabacda5ac9a6243ab714b864b Mon Sep 17 00:00:00 2001 From: Sergey Kapralov Date: Thu, 25 Aug 2016 11:30:53 +0300 Subject: [PATCH 4/5] Applied minor remarks. --- .../org/teavm/classlib/java/lang/TStackOverflowError.java | 4 ++-- classlib/src/main/java/org/teavm/classlib/java/util/TMap.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TStackOverflowError.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TStackOverflowError.java index 7f3dfa2cd..65c108b2e 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TStackOverflowError.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TStackOverflowError.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 skapral. + * Copyright 2016 Sergey Kapralov. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ package org.teavm.classlib.java.lang; /** * - * @author skapral + * @author Sergey Kapralov */ public class TStackOverflowError extends TVirtualMachineError { diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java b/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java index 30387d785..b85f0e74d 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java @@ -46,7 +46,7 @@ public interface TMap { V put(K key, V value); V remove(Object key); - + void putAll(TMap m); void clear(); From d1ade89ed4db146de45f40bd0b39cf1093ca84d3 Mon Sep 17 00:00:00 2001 From: Sergey Kapralov Date: Thu, 25 Aug 2016 17:12:03 +0300 Subject: [PATCH 5/5] TMap new methods are covered with tests --- .../org/teavm/classlib/java/util/TMap.java | 12 +- .../classlib/java/util/HashtableTest.java | 254 +++++++++++++++++- 2 files changed, 260 insertions(+), 6 deletions(-) diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java b/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java index b85f0e74d..b1ceca084 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TMap.java @@ -92,12 +92,13 @@ public interface TMap { V oldValue = v; V newValue = remappingFunction.apply(key, oldValue); if (newValue != null) { - return put(key, newValue); + put(key, newValue); } else { - return remove(key); + remove(key); } + return newValue; } - return v; + return null; } default V compute(K key, BiFunction remappingFunction) { @@ -120,9 +121,10 @@ public interface TMap { V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value); if (newValue == null) { - return remove(key); + remove(key); } else { - return put(key, newValue); + put(key, newValue); } + return newValue; } } diff --git a/tests/src/test/java/org/teavm/classlib/java/util/HashtableTest.java b/tests/src/test/java/org/teavm/classlib/java/util/HashtableTest.java index deb94921d..e8582ba32 100644 --- a/tests/src/test/java/org/teavm/classlib/java/util/HashtableTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/util/HashtableTest.java @@ -731,7 +731,7 @@ public class HashtableTest { } @Test - public void test_computeRemovesEntryOnNullMapping() { + public void test_computeRemovesEntryWhenNullProduced() { Hashtable ht10 = new Hashtable<>(); for(int i = 0; i < 10; i++) { ht10.put("Key" + i, "Val" + i); @@ -750,6 +750,258 @@ public class HashtableTest { } } + @Test + public void test_computeIfAbsentNominal() { + Hashtable ht10 = new Hashtable<>(); + for(int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + String newVal = ht10.computeIfAbsent("absent key", (k) -> "added"); + assertEquals("added", newVal); + assertEquals(11, ht10.size()); + + for(int i = 0; i < 10; i++) { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + assertEquals("New value expected","added", ht10.get("absent key")); + } + + @Test + public void test_computeIfAbsentIgnoresExistingEntry() { + Hashtable ht10 = new Hashtable<>(); + for(int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + String newVal = ht10.computeIfAbsent("Key5", (v) -> "changed"); + assertEquals("Val5", newVal); + assertEquals(10, ht10.size()); + + for(int i = 0; i < 10; i++) { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + } + + @Test + public void test_computeIfAbsentDoesNothingIfNullProduced() { + Hashtable ht10 = new Hashtable<>(); + for(int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + String newVal = ht10.computeIfAbsent("absent key", (v) -> null); + assertEquals(null, newVal); + assertEquals(10, ht10.size()); + + for(int i = 0; i < 10; i++) { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + } + + @Test + public void test_computeIfPresentNominal() { + Hashtable ht10 = new Hashtable<>(); + for(int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + String newVal = ht10.computeIfPresent("Key5", (k, v) -> "changed"); + assertEquals("changed", newVal); + assertEquals(10, ht10.size()); + + for(int i = 0; i < 10; i++) { + if(i == 5) { + assertEquals("Value was incorrectly updated", "changed", ht10.get("Key" + i)); + } else { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + } + } + + @Test + public void test_computeIfPresentIgnoresAbsentKeys() { + Hashtable ht10 = new Hashtable<>(); + for(int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + String newVal = ht10.computeIfPresent("absent key", (k, v) -> "added"); + assertEquals(null, newVal); + assertEquals(10, ht10.size()); + + for(int i = 0; i < 10; i++) { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + } + + @Test + public void test_computeIfPresentRemovesEntryWhenNullProduced() { + Hashtable ht10 = new Hashtable<>(); + for(int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + String newVal = ht10.computeIfPresent("Key5", (k, v) -> null); + assertEquals(null, newVal); + assertEquals(9, ht10.size()); + + for(int i = 0; i < 10; i++) { + if(i == 5) { + assertNull("Value unexpectedly present", ht10.get("Key" + i)); + } else { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + } + } + + + @Test + public void test_mergeKeyAbsentCase() { + Hashtable ht10 = new Hashtable<>(); + for (int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + String newVal = ht10.merge("absent key", "changed", (k, v) -> "remapped"); + assertEquals("changed", newVal); + assertEquals(11, ht10.size()); + + for (int i = 0; i < 10; i++) { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + assertEquals("New value expected", "changed", ht10.get("absent key")); + } + + @Test + public void test_mergeKeyPresentCase() { + Hashtable ht10 = new Hashtable<>(); + for (int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + String newVal = ht10.merge("Key5", "changed", (k, v) -> "remapped"); + assertEquals("remapped", newVal); + assertEquals(10, ht10.size()); + + for (int i = 0; i < 10; i++) { + if (i == 5) { + assertEquals("Value was incorrectly updated", "remapped", ht10.get("Key" + i)); + } else { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + } + } + + @Test + public void test_mergeKeyAbsentCase_remapToNull() { + Hashtable ht10 = new Hashtable<>(); + for (int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + String newVal = ht10.merge("absent key", "changed", (k, v) -> null); + assertEquals("changed", newVal); + assertEquals(11, ht10.size()); + + for (int i = 0; i < 10; i++) { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + assertEquals("New value expected", "changed", ht10.get("absent key")); + } + + @Test + public void test_mergeKeyPresentCase_remapToNull() { + Hashtable ht10 = new Hashtable<>(); + for (int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + String newVal = ht10.merge("Key5", "changed", (k, v) -> null); + assertEquals(null, newVal); + assertEquals(9, ht10.size()); + + for (int i = 0; i < 10; i++) { + if (i == 5) { + assertNull("Null value expected", ht10.get("Key" + i)); + } else { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + } + } + + @Test + public void test_replaceNominal() { + Hashtable ht10 = new Hashtable<>(); + for (int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + String newVal = ht10.replace("Key5", "changed"); + assertEquals("Val5", newVal); + assertEquals(10, ht10.size()); + + for (int i = 0; i < 10; i++) { + if (i == 5) { + assertEquals("Value was incorrectly updated", "changed", ht10.get("Key" + i)); + } else { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + } + } + + @Test + public void test_replaceAbsentKey() { + Hashtable ht10 = new Hashtable<>(); + for (int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + String newVal = ht10.replace("absent key", "changed"); + assertEquals(null, newVal); + assertEquals(10, ht10.size()); + + for (int i = 0; i < 10; i++) { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + } + + @Test + public void test_replace2Nominal() { + Hashtable ht10 = new Hashtable<>(); + for (int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + boolean replaced = ht10.replace("Key5", "Val5", "changed"); + assertEquals(true, replaced); + assertEquals(10, ht10.size()); + + for (int i = 0; i < 10; i++) { + if (i == 5) { + assertEquals("Value was incorrectly updated", "changed", ht10.get("Key" + i)); + } else { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + } + } + + @Test + public void test_replace2WithIncorrectExpectation() { + Hashtable ht10 = new Hashtable<>(); + for (int i = 0; i < 10; i++) { + ht10.put("Key" + i, "Val" + i); + } + + boolean replaced = ht10.replace("Key5", "incorrect value", "changed"); + assertEquals(false, replaced); + assertEquals(10, ht10.size()); + + for (int i = 0; i < 10; i++) { + assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i)); + } + } + @SuppressWarnings("unchecked") protected Hashtable hashtableClone(Hashtable s) { return (Hashtable) s.clone();