From a2e5e669f1fd138fa06a6fe826974156757be8ed Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Tue, 16 Jul 2019 16:56:07 +0200 Subject: [PATCH] added system property to switch object value implementation --- .../worth/serialization/ValueDumper.java | 4 +- .../worth/serialization/ValueParser.java | 2 +- .../serialization/binary/JBONDumper.java | 4 +- .../serialization/binary/JBONParser.java | 6 +- .../worth/serialization/json/JSONDumper.java | 4 +- .../worth/serialization/json/JSONParser.java | 2 +- .../net/woggioni/worth/value/ArrayValue.java | 4 +- .../net/woggioni/worth/value/ObjectValue.java | 68 +++++++++++++++---- .../worth/antlr/JSONListenerImpl.java | 4 +- .../net/woggioni/worth/antlr/ParseTest.java | 2 +- .../worth/serialization/binary/JBONTest.java | 8 +-- .../worth/serialization/json/JSONTest.java | 6 +- .../serialization/json/PerformanceTest.java | 51 ++++++++------ 13 files changed, 108 insertions(+), 57 deletions(-) diff --git a/src/main/java/net/woggioni/worth/serialization/ValueDumper.java b/src/main/java/net/woggioni/worth/serialization/ValueDumper.java index 56fb397..ff349e1 100644 --- a/src/main/java/net/woggioni/worth/serialization/ValueDumper.java +++ b/src/main/java/net/woggioni/worth/serialization/ValueDumper.java @@ -24,7 +24,7 @@ public abstract class ValueDumper implements Dumper { } protected static class ArrayStackLevel extends StackLevel implements Iterator { - private final Iterator iterator = value.asArray().iterator(); + private final Iterator iterator = ((ArrayValue) value).iterator(); @Override public Value next() { @@ -43,7 +43,7 @@ public abstract class ValueDumper implements Dumper { } protected static class ObjectStackLevel extends StackLevel implements Iterator> { - private final Iterator> iterator = value.asObject().entrySet().iterator(); + private final Iterator> iterator = ((ObjectValue) value).iterator(); @Override public Map.Entry next() { diff --git a/src/main/java/net/woggioni/worth/serialization/ValueParser.java b/src/main/java/net/woggioni/worth/serialization/ValueParser.java index 0f20f2c..9e2c78b 100644 --- a/src/main/java/net/woggioni/worth/serialization/ValueParser.java +++ b/src/main/java/net/woggioni/worth/serialization/ValueParser.java @@ -4,8 +4,8 @@ import lombok.RequiredArgsConstructor; import net.woggioni.worth.exception.NotImplementedException; import net.woggioni.worth.utils.WorthUtils; import net.woggioni.worth.value.*; -import net.woggioni.worth.xface.Value; import net.woggioni.worth.xface.Parser; +import net.woggioni.worth.xface.Value; import java.io.InputStream; import java.io.InputStreamReader; diff --git a/src/main/java/net/woggioni/worth/serialization/binary/JBONDumper.java b/src/main/java/net/woggioni/worth/serialization/binary/JBONDumper.java index 50f8730..4c0aa1e 100644 --- a/src/main/java/net/woggioni/worth/serialization/binary/JBONDumper.java +++ b/src/main/java/net/woggioni/worth/serialization/binary/JBONDumper.java @@ -2,13 +2,13 @@ package net.woggioni.worth.serialization.binary; import lombok.SneakyThrows; import net.woggioni.worth.exception.NotImplementedException; +import net.woggioni.worth.serialization.ValueDumper; import net.woggioni.worth.utils.Leb128; import net.woggioni.worth.utils.WorthUtils; import net.woggioni.worth.value.ArrayValue; import net.woggioni.worth.value.ObjectValue; -import net.woggioni.worth.xface.Value; -import net.woggioni.worth.serialization.ValueDumper; import net.woggioni.worth.xface.Dumper; +import net.woggioni.worth.xface.Value; import java.io.OutputStream; import java.io.Writer; diff --git a/src/main/java/net/woggioni/worth/serialization/binary/JBONParser.java b/src/main/java/net/woggioni/worth/serialization/binary/JBONParser.java index 3c02c99..037c76c 100644 --- a/src/main/java/net/woggioni/worth/serialization/binary/JBONParser.java +++ b/src/main/java/net/woggioni/worth/serialization/binary/JBONParser.java @@ -1,13 +1,13 @@ package net.woggioni.worth.serialization.binary; import lombok.SneakyThrows; -import net.woggioni.worth.utils.Leb128; -import net.woggioni.worth.utils.WorthUtils; -import net.woggioni.worth.xface.Value; import net.woggioni.worth.buffer.LookAheadInputStream; import net.woggioni.worth.exception.ParseException; import net.woggioni.worth.serialization.ValueParser; +import net.woggioni.worth.utils.Leb128; +import net.woggioni.worth.utils.WorthUtils; import net.woggioni.worth.xface.Parser; +import net.woggioni.worth.xface.Value; import java.io.InputStream; import java.util.function.Function; diff --git a/src/main/java/net/woggioni/worth/serialization/json/JSONDumper.java b/src/main/java/net/woggioni/worth/serialization/json/JSONDumper.java index 17f08cb..6df547b 100644 --- a/src/main/java/net/woggioni/worth/serialization/json/JSONDumper.java +++ b/src/main/java/net/woggioni/worth/serialization/json/JSONDumper.java @@ -1,12 +1,12 @@ package net.woggioni.worth.serialization.json; import lombok.SneakyThrows; +import net.woggioni.worth.serialization.ValueDumper; import net.woggioni.worth.utils.WorthUtils; import net.woggioni.worth.value.ArrayValue; import net.woggioni.worth.value.ObjectValue; -import net.woggioni.worth.xface.Value; -import net.woggioni.worth.serialization.ValueDumper; import net.woggioni.worth.xface.Dumper; +import net.woggioni.worth.xface.Value; import java.io.OutputStream; import java.io.OutputStreamWriter; diff --git a/src/main/java/net/woggioni/worth/serialization/json/JSONParser.java b/src/main/java/net/woggioni/worth/serialization/json/JSONParser.java index c4df6e8..aea328a 100644 --- a/src/main/java/net/woggioni/worth/serialization/json/JSONParser.java +++ b/src/main/java/net/woggioni/worth/serialization/json/JSONParser.java @@ -1,7 +1,6 @@ package net.woggioni.worth.serialization.json; import lombok.SneakyThrows; -import net.woggioni.worth.xface.Value; import net.woggioni.worth.buffer.LookAheadTextInputStream; import net.woggioni.worth.exception.IOException; import net.woggioni.worth.exception.NotImplementedException; @@ -9,6 +8,7 @@ import net.woggioni.worth.exception.ParseException; import net.woggioni.worth.serialization.ValueParser; import net.woggioni.worth.utils.WorthUtils; import net.woggioni.worth.xface.Parser; +import net.woggioni.worth.xface.Value; import java.io.InputStream; import java.io.InputStreamReader; diff --git a/src/main/java/net/woggioni/worth/value/ArrayValue.java b/src/main/java/net/woggioni/worth/value/ArrayValue.java index ddbffdd..13bb057 100644 --- a/src/main/java/net/woggioni/worth/value/ArrayValue.java +++ b/src/main/java/net/woggioni/worth/value/ArrayValue.java @@ -4,6 +4,7 @@ import lombok.EqualsAndHashCode; import net.woggioni.worth.xface.Value; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -59,10 +60,9 @@ public class ArrayValue implements Value, Iterable { @Override public List asArray() { - return value; + return Collections.unmodifiableList(value); } - @Override public Iterator iterator() { return value.iterator(); diff --git a/src/main/java/net/woggioni/worth/value/ObjectValue.java b/src/main/java/net/woggioni/worth/value/ObjectValue.java index 380ec64..5aa7bf7 100644 --- a/src/main/java/net/woggioni/worth/value/ObjectValue.java +++ b/src/main/java/net/woggioni/worth/value/ObjectValue.java @@ -2,29 +2,48 @@ package net.woggioni.worth.value; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import net.woggioni.worth.utils.WorthUtils; import net.woggioni.worth.xface.Value; import java.util.*; public interface ObjectValue extends Value, Iterable> { - boolean listBasedImplementation = Boolean.valueOf( - System.getProperty(ObjectValue.class.getName() + ".listBasedImplementation", "false")); - boolean preserveKeyOrder = Boolean.valueOf( - System.getProperty(ObjectValue.class.getName() + ".preserveKeyOrder", "false")); + Implementation implementation = Implementation.valueOf( + System.getProperty(ObjectValue.class.getName() + ".implementation", "TreeMap")); static ObjectValue newInstance() { - if (listBasedImplementation) { - return new ListObjectValue(); - } else { - return new MapObjectValue(); + ObjectValue result; + switch(implementation) { + case ArrayList: + result = new ListObjectValue(); + break; + case TreeMap: + result = new TreeMapObjectValue(); + break; + case HashMap: + result = new HashMapObjectValue(); + break; + case LinkedHashMap: + result = new LinkedHashMapObjectValue(); + break; + default: + throw WorthUtils.newThrowable(IllegalArgumentException.class, + "Unknown value of %s: %s", + Implementation.class.getName(), + implementation); } + return result; } @Override default Type type() { return Type.OBJECT; } + + enum Implementation { + ArrayList, TreeMap, HashMap, LinkedHashMap + } } final class ObjectEntry implements Map.Entry { @@ -55,14 +74,10 @@ final class ObjectEntry implements Map.Entry { } @EqualsAndHashCode -class MapObjectValue implements ObjectValue { +abstract class MapObjectValue implements ObjectValue { private final Map value; - public MapObjectValue() { - this.value = ObjectValue.preserveKeyOrder ? new LinkedHashMap<>() : new HashMap<>(); - } - public MapObjectValue(Map value) { this.value = value; } @@ -114,6 +129,31 @@ class MapObjectValue implements ObjectValue { } } +@EqualsAndHashCode +class HashMapObjectValue extends MapObjectValue { + + public HashMapObjectValue() { + super(new HashMap<>()); + } +} + +@EqualsAndHashCode +class LinkedHashMapObjectValue extends MapObjectValue { + + public LinkedHashMapObjectValue() { + super(new LinkedHashMap<>()); + } +} + +@EqualsAndHashCode +class TreeMapObjectValue extends MapObjectValue { + + public TreeMapObjectValue() { + super(new TreeMap<>()); + } +} + + @NoArgsConstructor @EqualsAndHashCode class ListObjectValue implements ObjectValue { @@ -126,7 +166,7 @@ class ListObjectValue implements ObjectValue { @Override public Map asObject() { - Map result = preserveKeyOrder ? new LinkedHashMap<>() : new HashMap<>(); + Map result = new LinkedHashMap<>(); for (Map.Entry entry : value) { result.put(entry.getKey(), entry.getValue()); } diff --git a/src/test/java/net/woggioni/worth/antlr/JSONListenerImpl.java b/src/test/java/net/woggioni/worth/antlr/JSONListenerImpl.java index 76494ea..6969e06 100644 --- a/src/test/java/net/woggioni/worth/antlr/JSONListenerImpl.java +++ b/src/test/java/net/woggioni/worth/antlr/JSONListenerImpl.java @@ -1,10 +1,10 @@ package net.woggioni.worth.antlr; +import net.woggioni.worth.serialization.ValueParser; +import net.woggioni.worth.xface.Value; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ErrorNode; import org.antlr.v4.runtime.tree.TerminalNode; -import net.woggioni.worth.serialization.ValueParser; -import net.woggioni.worth.xface.Value; public class JSONListenerImpl extends ValueParser implements JSONListener { diff --git a/src/test/java/net/woggioni/worth/antlr/ParseTest.java b/src/test/java/net/woggioni/worth/antlr/ParseTest.java index 3071863..c5a1013 100644 --- a/src/test/java/net/woggioni/worth/antlr/ParseTest.java +++ b/src/test/java/net/woggioni/worth/antlr/ParseTest.java @@ -1,13 +1,13 @@ package net.woggioni.worth.antlr; import lombok.SneakyThrows; +import net.woggioni.worth.serialization.json.JSONDumper; import net.woggioni.worth.xface.Value; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CodePointCharStream; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.tree.ParseTreeWalker; import org.junit.Test; -import net.woggioni.worth.serialization.json.JSONDumper; import java.io.InputStreamReader; diff --git a/src/test/java/net/woggioni/worth/serialization/binary/JBONTest.java b/src/test/java/net/woggioni/worth/serialization/binary/JBONTest.java index a121dcf..66926b6 100644 --- a/src/test/java/net/woggioni/worth/serialization/binary/JBONTest.java +++ b/src/test/java/net/woggioni/worth/serialization/binary/JBONTest.java @@ -1,13 +1,13 @@ package net.woggioni.worth.serialization.binary; import lombok.SneakyThrows; -import org.junit.Assert; -import org.junit.Test; import net.woggioni.worth.buffer.LookAheadTextInputStream; import net.woggioni.worth.serialization.json.JSONParser; import net.woggioni.worth.value.ObjectValue; import net.woggioni.worth.xface.Parser; import net.woggioni.worth.xface.Value; +import org.junit.Assert; +import org.junit.Test; import java.io.*; import java.lang.reflect.Method; @@ -26,7 +26,7 @@ public class JBONTest { @Test @SneakyThrows public void consistencyTest() { - System.setProperty(ObjectValue.class.getName() + ".preserveKeyOrder", "true"); + System.setProperty(ObjectValue.class.getName() + ".implementation", "TreeMap"); for (String testFile : testFiles) { Value parsedValue; try(InputStream is = getTestSource(testFile)) { @@ -59,7 +59,7 @@ public class JBONTest { for (String testFile : testFiles) { Value originalValue = new JSONParser().parse(getTestSource(testFile)); - Path outputFile = Files.createTempFile(Paths.get("/tmp"),"worh", null); + Path outputFile = Files.createTempFile(Paths.get("/tmp"),"worth", null); try (OutputStream os = new FileOutputStream(outputFile.toFile())) { JBONDumper jbonDumper = new JBONDumper(); jbonDumper.dump(originalValue, os); diff --git a/src/test/java/net/woggioni/worth/serialization/json/JSONTest.java b/src/test/java/net/woggioni/worth/serialization/json/JSONTest.java index 0ee438e..11c25e7 100644 --- a/src/test/java/net/woggioni/worth/serialization/json/JSONTest.java +++ b/src/test/java/net/woggioni/worth/serialization/json/JSONTest.java @@ -4,8 +4,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeType; import lombok.SneakyThrows; -import org.junit.Assert; -import org.junit.Test; import net.woggioni.worth.buffer.LookAheadTextInputStream; import net.woggioni.worth.exception.NotImplementedException; import net.woggioni.worth.utils.WorthUtils; @@ -13,6 +11,8 @@ import net.woggioni.worth.value.ArrayValue; import net.woggioni.worth.value.ObjectValue; import net.woggioni.worth.xface.Parser; import net.woggioni.worth.xface.Value; +import org.junit.Assert; +import org.junit.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -186,7 +186,7 @@ public class JSONTest { @Test @SneakyThrows public void consistencyTest() { - System.setProperty(ObjectValue.class.getName() + ".preserveKeyOrder", "true"); + System.setProperty(ObjectValue.class.getName() + ".implementation", "ArrayList"); for (String testFile : testFiles) { Parser parser = new JSONParser(); Value parsedValue = parser.parse(getTestSource(testFile)); diff --git a/src/test/java/net/woggioni/worth/serialization/json/PerformanceTest.java b/src/test/java/net/woggioni/worth/serialization/json/PerformanceTest.java index 3141a43..879139f 100644 --- a/src/test/java/net/woggioni/worth/serialization/json/PerformanceTest.java +++ b/src/test/java/net/woggioni/worth/serialization/json/PerformanceTest.java @@ -3,18 +3,20 @@ package net.woggioni.worth.serialization.json; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.SneakyThrows; +import net.woggioni.worth.antlr.JSONLexer; import net.woggioni.worth.antlr.JSONListenerImpl; +import net.woggioni.worth.xface.Value; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.tree.ParseTreeWalker; import org.junit.Ignore; import org.junit.Test; -import net.woggioni.worth.antlr.JSONLexer; -import net.woggioni.worth.xface.Value; import org.tukaani.xz.XZInputStream; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; class Chronometer { @@ -52,7 +54,11 @@ public class PerformanceTest { @SneakyThrows private static InputStream extractTestData() { - return new XZInputStream(PerformanceTest.class.getResourceAsStream("/citylots.json.xz")); + return new XZInputStream(new BufferedInputStream(PerformanceTest.class.getResourceAsStream("/citylots.json.xz"))); + } + + private static InputStream smallTestData() { + return new BufferedInputStream(PerformanceTest.class.getResourceAsStream("/wordpress.json")); } @Test @@ -72,32 +78,37 @@ public class PerformanceTest { Chronometer chr = new Chronometer(); { ObjectMapper om = new ObjectMapper(); - chr.reset(); - for (int i = 0; i < loops; i++) { - JsonNode jsonNode = om.readTree(getClass().getResourceAsStream("/wordpress.json")); + for(int j = 0; j < 2; j++) { + chr.reset(); + for (int i = 0; i < loops; i++) { + JsonNode jsonNode = om.readTree(smallTestData()); + } } jacksonTime = chr.stop(Chronometer.TimeUnit.MILLISECOND); System.out.printf("Jackson time: %8s msec\n", String.format("%.3f", jacksonTime)); } { - chr.reset(); - for (int i = 0; i < loops; i++) { - Value value = new JSONParser().parse(getClass().getResourceAsStream("/wordpress.json")); + for(int j = 0; j < 2; j++) { + chr.reset(); + for(int i = 0; i < loops; i++) { + Value value = new JSONParser().parse(smallTestData()); + } } worthTime = chr.stop(Chronometer.TimeUnit.MILLISECOND); System.out.printf("Worth time: %8s msec\n", String.format("%.3f", worthTime)); } { - chr.reset(); - for (int i = 0; i < loops; i++) { - CharStream inputStream = CharStreams.fromReader( - new InputStreamReader(getClass().getResourceAsStream("/wordpress.json"))); - JSONLexer lexer = new JSONLexer(inputStream); - CommonTokenStream commonTokenStream = new CommonTokenStream(lexer); - net.woggioni.worth.antlr.JSONParser parser = new net.woggioni.worth.antlr.JSONParser(commonTokenStream); - JSONListenerImpl listener = new JSONListenerImpl(); - ParseTreeWalker walker = new ParseTreeWalker(); - walker.walk(listener, parser.json()); + for(int j = 0; j < 2; j++) { + chr.reset(); + for(int i = 0; i < loops; i++) { + CharStream inputStream = CharStreams.fromReader(new InputStreamReader(smallTestData())); + JSONLexer lexer = new JSONLexer(inputStream); + CommonTokenStream commonTokenStream = new CommonTokenStream(lexer); + net.woggioni.worth.antlr.JSONParser parser = new net.woggioni.worth.antlr.JSONParser(commonTokenStream); + JSONListenerImpl listener = new JSONListenerImpl(); + ParseTreeWalker walker = new ParseTreeWalker(); + walker.walk(listener, parser.json()); + } } antlrTime = chr.stop(Chronometer.TimeUnit.MILLISECOND); System.out.printf("Antlr time: %8s msec\n", String.format("%.3f", antlrTime));