From 161c05f2940b94fd6b646ddba29cc32261172936 Mon Sep 17 00:00:00 2001 From: Peter Ansell Date: Sat, 23 Apr 2016 13:07:23 +1000 Subject: [PATCH 01/11] Add performance test for JsonLdApi.fromRDF(RDFDataset) --- .../core/JsonLdPerformanceTest.java | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java b/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java index baa72e88..ab580f2b 100644 --- a/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java +++ b/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java @@ -5,11 +5,18 @@ import java.io.File; import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.DoubleSummaryStatistics; +import java.util.List; +import java.util.LongSummaryStatistics; +import java.util.Random; import java.util.zip.GZIPInputStream; import org.junit.Ignore; import org.junit.Test; +import com.github.jsonldjava.core.RDFDataset.Quad; import com.github.jsonldjava.utils.JsonUtils; /** @@ -41,4 +48,110 @@ public final void test() throws Exception { System.out.printf("Compaction time: %d", (compactEnd - compactStart)); } + @Test + public final void testSerialisationPerformance() throws Exception { + Random prng = new Random(); + + String exNs = "http://example.org/"; + + String bnode = "_:anon"; + String uri1 = exNs + "a1"; + String uri2 = exNs + "b2"; + String uri3 = exNs + "c3"; + List potentialSubjects = new ArrayList(); + potentialSubjects.add(bnode); + potentialSubjects.add(uri1); + potentialSubjects.add(uri2); + potentialSubjects.add(uri3); + for (int i = 0; i < 50; i++) { + potentialSubjects.add("_:" + i); + } + for (int i = 1; i < 50; i++) { + potentialSubjects.add("_:a" + Integer.toHexString(i).toUpperCase()); + } + for (int i = 0; i < 200; i++) { + potentialSubjects.add(exNs + Integer.toHexString(i) + "/z" + + Integer.toOctalString(i % 20)); + } + Collections.shuffle(potentialSubjects, prng); + + List potentialObjects = new ArrayList(); + potentialObjects.addAll(potentialSubjects); + Collections.shuffle(potentialObjects, prng); + + List potentialPredicates = new ArrayList(); + potentialPredicates.add(JsonLdConsts.RDF_TYPE); + potentialPredicates.add(JsonLdConsts.RDF_LIST); + potentialPredicates.add(JsonLdConsts.RDF_NIL); + potentialPredicates.add(JsonLdConsts.RDF_FIRST); + potentialPredicates.add(JsonLdConsts.RDF_OBJECT); + potentialPredicates.add(JsonLdConsts.XSD_STRING); + Collections.shuffle(potentialPredicates, prng); + + RDFDataset testData = new RDFDataset(); + + for (int i = 0; i < 8000; i++) { + String nextObject = potentialObjects.get(prng.nextInt(potentialObjects.size())); + boolean isLiteral = true; + if (nextObject.startsWith("_:") || nextObject.startsWith("http://")) { + isLiteral = false; + } + if (isLiteral) { + if (i % 2 == 0) { + testData.addQuad(potentialSubjects.get(prng.nextInt(potentialSubjects.size())), + potentialPredicates.get(prng.nextInt(potentialPredicates.size())), + nextObject, JsonLdConsts.XSD_STRING, + potentialSubjects.get(prng.nextInt(potentialSubjects.size())), null); + } else if (i % 5 == 0) { + testData.addTriple( + potentialSubjects.get(prng.nextInt(potentialSubjects.size())), + potentialPredicates.get(prng.nextInt(potentialPredicates.size())), + nextObject, JsonLdConsts.RDF_LANGSTRING, "en"); + } + } else { + if (i % 2 == 0) { + testData.addQuad(potentialSubjects.get(prng.nextInt(potentialSubjects.size())), + potentialPredicates.get(prng.nextInt(potentialPredicates.size())), + nextObject, + potentialSubjects.get(prng.nextInt(potentialSubjects.size()))); + } else if (i % 5 == 0) { + testData.addTriple( + potentialSubjects.get(prng.nextInt(potentialSubjects.size())), + potentialPredicates.get(prng.nextInt(potentialPredicates.size())), + nextObject); + } + } + } + + JsonLdOptions options = new JsonLdOptions(); + JsonLdApi jsonLdApi = new JsonLdApi(options); + int rounds = 10000; + int[] hashCodes = new int[rounds]; + LongSummaryStatistics statsFirst5000 = new LongSummaryStatistics(); + LongSummaryStatistics stats = new LongSummaryStatistics(); + for (int i = 0; i < rounds; i++) { + long start = System.nanoTime(); + Object fromRDF = jsonLdApi.fromRDF(testData); + if (i < 5000) { + statsFirst5000.accept(System.nanoTime() - start); + } else { + stats.accept(System.nanoTime() - start); + } + hashCodes[i] = fromRDF.hashCode(); + fromRDF = null; + } + System.out.println("First 5000 out of " + rounds); + System.out.println("Average: " + statsFirst5000.getAverage() / 100000); + System.out.println("Sum: " + statsFirst5000.getSum() / 100000); + System.out.println("Maximum: " + statsFirst5000.getMax() / 100000); + System.out.println("Minimum: " + statsFirst5000.getMin() / 100000); + System.out.println("Count: " + statsFirst5000.getCount()); + + System.out.println("Post 5000 out of " + rounds); + System.out.println("Average: " + stats.getAverage() / 100000); + System.out.println("Sum: " + stats.getSum() / 100000); + System.out.println("Maximum: " + stats.getMax() / 100000); + System.out.println("Minimum: " + stats.getMin() / 100000); + System.out.println("Count: " + stats.getCount()); + } } From 104f3bb340dcdfc5ef2932037d8f83772e497fb8 Mon Sep 17 00:00:00 2001 From: Peter Ansell Date: Sat, 23 Apr 2016 13:33:46 +1000 Subject: [PATCH 02/11] Minimum reasonable map size should be 4, not 2 --- core/src/main/java/com/github/jsonldjava/utils/Obj.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/github/jsonldjava/utils/Obj.java b/core/src/main/java/com/github/jsonldjava/utils/Obj.java index a7f371de..4e7e36bb 100644 --- a/core/src/main/java/com/github/jsonldjava/utils/Obj.java +++ b/core/src/main/java/com/github/jsonldjava/utils/Obj.java @@ -11,7 +11,7 @@ public class Obj { * @return A new {@link Map} instance. */ public static Map newMap() { - return new LinkedHashMap(2, 0.75f); + return new LinkedHashMap(4, 0.75f); } /** From 52e4c05fc78f053003b24d4901ee429d7811e437 Mon Sep 17 00:00:00 2001 From: Peter Ansell Date: Sat, 23 Apr 2016 13:34:25 +1000 Subject: [PATCH 03/11] start array lists by default at size 4 --- .../com/github/jsonldjava/core/JsonLdApi.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java b/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java index 6911ca37..fe134e3d 100644 --- a/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java +++ b/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java @@ -1029,7 +1029,7 @@ void generateNodeMap(Object element, Map nodeMap, String activeG if (elem.get("@type") instanceof List) { oldTypes = (List) elem.get("@type"); } else { - oldTypes = new ArrayList(); + oldTypes = new ArrayList(4); oldTypes.add((String) elem.get("@type")); } for (final String item : oldTypes) { @@ -1061,7 +1061,7 @@ void generateNodeMap(Object element, Map nodeMap, String activeG // 5) else if (elem.containsKey("@list")) { // 5.1) - final Map result = newMap("@list", new ArrayList()); + final Map result = newMap("@list", new ArrayList(4)); // 5.2) // for (final Object item : (List) elem.get("@list")) { // generateNodeMap(item, nodeMap, activeGraph, activeSubject, @@ -1166,7 +1166,7 @@ else if (activeProperty != null) { } // 6.11.2) if (!node.containsKey(property)) { - node.put(property, new ArrayList()); + node.put(property, new ArrayList(4)); } // 6.11.3) generateNodeMap(value, nodeMap, activeGraph, id, property, null); @@ -1739,7 +1739,7 @@ public UsagesNode(NodeMapNode node, String property, Map value) } private class NodeMapNode extends LinkedHashMap { - public List usages = new ArrayList(); + public List usages = new ArrayList(4); public NodeMapNode(String id) { super(); @@ -1798,9 +1798,9 @@ public Map serialize() { */ public List fromRDF(final RDFDataset dataset) throws JsonLdError { // 1) - final Map defaultGraph = new LinkedHashMap(); + final Map defaultGraph = new LinkedHashMap(4); // 2) - final Map> graphMap = new LinkedHashMap>(); + final Map> graphMap = new LinkedHashMap>(4); graphMap.put("@default", defaultGraph); // 3/3.1) @@ -1883,8 +1883,8 @@ public List fromRDF(final RDFDataset dataset) throws JsonLdError { String property = usage.property; Map head = usage.value; // 4.3.2) - final List list = new ArrayList(); - final List listNodes = new ArrayList(); + final List list = new ArrayList(4); + final List listNodes = new ArrayList(4); // 4.3.3) while (RDF_REST.equals(property) && node.isWellFormedListNode()) { // 4.3.3.1) @@ -1931,7 +1931,7 @@ public List fromRDF(final RDFDataset dataset) throws JsonLdError { } // 5) - final List result = new ArrayList(); + final List result = new ArrayList(4); // 6) final List ids = new ArrayList(defaultGraph.keySet()); Collections.sort(ids); @@ -1940,7 +1940,7 @@ public List fromRDF(final RDFDataset dataset) throws JsonLdError { // 6.1) if (graphMap.containsKey(subject)) { // 6.1.1) - node.put("@graph", new ArrayList()); + node.put("@graph", new ArrayList(4)); // 6.1.2) final List keys = new ArrayList(graphMap.get(subject).keySet()); Collections.sort(keys); From 261e20b5b4e01afe4c1639bcc255cc94ac66118a Mon Sep 17 00:00:00 2001 From: Peter Ansell Date: Sat, 23 Apr 2016 14:23:28 +1000 Subject: [PATCH 04/11] Add tests for various algorithms --- .../core/JsonLdPerformanceTest.java | 94 ++++++++++++++++++- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java b/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java index ab580f2b..74b7d4fb 100644 --- a/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java +++ b/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java @@ -49,8 +49,9 @@ public final void test() throws Exception { } @Test - public final void testSerialisationPerformance() throws Exception { + public final void testPerformance() throws Exception { Random prng = new Random(); + int rounds = 2000; String exNs = "http://example.org/"; @@ -123,9 +124,10 @@ public final void testSerialisationPerformance() throws Exception { } } + System.out + .println("RDF triples to JSON-LD (internal objects, not parsed from a document)..."); JsonLdOptions options = new JsonLdOptions(); JsonLdApi jsonLdApi = new JsonLdApi(options); - int rounds = 10000; int[] hashCodes = new int[rounds]; LongSummaryStatistics statsFirst5000 = new LongSummaryStatistics(); LongSummaryStatistics stats = new LongSummaryStatistics(); @@ -153,5 +155,93 @@ public final void testSerialisationPerformance() throws Exception { System.out.println("Maximum: " + stats.getMax() / 100000); System.out.println("Minimum: " + stats.getMin() / 100000); System.out.println("Count: " + stats.getCount()); + + System.out.println("Non-pretty print benchmarking..."); + JsonLdOptions options2 = new JsonLdOptions(); + JsonLdApi jsonLdApi2 = new JsonLdApi(options2); + LongSummaryStatistics statsFirst5000Part2 = new LongSummaryStatistics(); + LongSummaryStatistics statsPart2 = new LongSummaryStatistics(); + Object fromRDF2 = jsonLdApi2.fromRDF(testData); + for (int i = 0; i < rounds; i++) { + long start = System.nanoTime(); + JsonUtils.toString(fromRDF2); + if (i < 5000) { + statsFirst5000Part2.accept(System.nanoTime() - start); + } else { + statsPart2.accept(System.nanoTime() - start); + } + } + System.out.println("First 5000 out of " + rounds); + System.out.println("Average: " + statsFirst5000Part2.getAverage() / 100000); + System.out.println("Sum: " + statsFirst5000Part2.getSum() / 100000); + System.out.println("Maximum: " + statsFirst5000Part2.getMax() / 100000); + System.out.println("Minimum: " + statsFirst5000Part2.getMin() / 100000); + System.out.println("Count: " + statsFirst5000Part2.getCount()); + + System.out.println("Post 5000 out of " + rounds); + System.out.println("Average: " + statsPart2.getAverage() / 100000); + System.out.println("Sum: " + statsPart2.getSum() / 100000); + System.out.println("Maximum: " + statsPart2.getMax() / 100000); + System.out.println("Minimum: " + statsPart2.getMin() / 100000); + System.out.println("Count: " + statsPart2.getCount()); + + System.out.println("Pretty print benchmarking..."); + JsonLdOptions options3 = new JsonLdOptions(); + JsonLdApi jsonLdApi3 = new JsonLdApi(options3); + LongSummaryStatistics statsFirst5000Part3 = new LongSummaryStatistics(); + LongSummaryStatistics statsPart3 = new LongSummaryStatistics(); + Object fromRDF3 = jsonLdApi3.fromRDF(testData); + for (int i = 0; i < rounds; i++) { + long start = System.nanoTime(); + JsonUtils.toPrettyString(fromRDF3); + if (i < 5000) { + statsFirst5000Part3.accept(System.nanoTime() - start); + } else { + statsPart3.accept(System.nanoTime() - start); + } + } + System.out.println("First 5000 out of " + rounds); + System.out.println("Average: " + statsFirst5000Part3.getAverage() / 100000); + System.out.println("Sum: " + statsFirst5000Part3.getSum() / 100000); + System.out.println("Maximum: " + statsFirst5000Part3.getMax() / 100000); + System.out.println("Minimum: " + statsFirst5000Part3.getMin() / 100000); + System.out.println("Count: " + statsFirst5000Part3.getCount()); + + System.out.println("Post 5000 out of " + rounds); + System.out.println("Average: " + statsPart3.getAverage() / 100000); + System.out.println("Sum: " + statsPart3.getSum() / 100000); + System.out.println("Maximum: " + statsPart3.getMax() / 100000); + System.out.println("Minimum: " + statsPart3.getMin() / 100000); + System.out.println("Count: " + statsPart3.getCount()); + + System.out.println("Expansion benchmarking..."); + JsonLdOptions options4 = new JsonLdOptions(); + JsonLdApi jsonLdApi4 = new JsonLdApi(options4); + LongSummaryStatistics statsFirst5000Part4 = new LongSummaryStatistics(); + LongSummaryStatistics statsPart4 = new LongSummaryStatistics(); + Object fromRDF4 = jsonLdApi4.fromRDF(testData); + for (int i = 0; i < rounds; i++) { + long start = System.nanoTime(); + JsonLdProcessor.expand(fromRDF4, options4); + if (i < 5000) { + statsFirst5000Part4.accept(System.nanoTime() - start); + } else { + statsPart4.accept(System.nanoTime() - start); + } + } + System.out.println("First 5000 out of " + rounds); + System.out.println("Average: " + statsFirst5000Part4.getAverage() / 100000); + System.out.println("Sum: " + statsFirst5000Part4.getSum() / 100000); + System.out.println("Maximum: " + statsFirst5000Part4.getMax() / 100000); + System.out.println("Minimum: " + statsFirst5000Part4.getMin() / 100000); + System.out.println("Count: " + statsFirst5000Part4.getCount()); + + System.out.println("Post 5000 out of " + rounds); + System.out.println("Average: " + statsPart4.getAverage() / 100000); + System.out.println("Sum: " + statsPart4.getSum() / 100000); + System.out.println("Maximum: " + statsPart4.getMax() / 100000); + System.out.println("Minimum: " + statsPart4.getMin() / 100000); + System.out.println("Count: " + statsPart4.getCount()); + } } From 0c6a20ea96ff1aab0233a60f20726814fa02ca5f Mon Sep 17 00:00:00 2001 From: Peter Ansell Date: Sat, 23 Apr 2016 14:24:30 +1000 Subject: [PATCH 05/11] Ignore performance test by default --- .../java/com/github/jsonldjava/core/JsonLdPerformanceTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java b/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java index 74b7d4fb..929b11e2 100644 --- a/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java +++ b/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java @@ -48,6 +48,7 @@ public final void test() throws Exception { System.out.printf("Compaction time: %d", (compactEnd - compactStart)); } + @Ignore("Disable performance tests by default") @Test public final void testPerformance() throws Exception { Random prng = new Random(); From f8d663d451e3e4df3658214490909f1f14ba6624 Mon Sep 17 00:00:00 2001 From: Peter Ansell Date: Sat, 7 May 2016 09:47:27 +1000 Subject: [PATCH 06/11] Add laxMergeValue option to possibly streamline parsing in future --- .../com/github/jsonldjava/core/JsonLdApi.java | 3 +- .../github/jsonldjava/core/JsonLdUtils.java | 17 ++++ .../core/JsonLdPerformanceTest.java | 99 +++++++++++++++---- 3 files changed, 101 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java b/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java index fe134e3d..4d2bc69d 100644 --- a/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java +++ b/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java @@ -1855,7 +1855,8 @@ public List fromRDF(final RDFDataset dataset) throws JsonLdError { // 3.5.6+7) JsonLdUtils.mergeValue(node, predicate, value); - + // JsonLdUtils.laxMergeValue(node, predicate, value); + // 3.5.8) if (object.isBlankNode() || object.isIRI()) { // 3.5.8.1-3) diff --git a/core/src/main/java/com/github/jsonldjava/core/JsonLdUtils.java b/core/src/main/java/com/github/jsonldjava/core/JsonLdUtils.java index 890f8751..10310001 100644 --- a/core/src/main/java/com/github/jsonldjava/core/JsonLdUtils.java +++ b/core/src/main/java/com/github/jsonldjava/core/JsonLdUtils.java @@ -118,6 +118,23 @@ static void mergeValue(Map obj, String key, Object value) { } } + static void laxMergeValue(Map obj, String key, Object value) { + if (obj == null) { + return; + } + List values = (List) obj.get(key); + if (values == null) { + values = new ArrayList(); + obj.put(key, values); + } + if ("@list".equals(key) + || (value instanceof Map && ((Map) value).containsKey("@list")) + //|| !deepContains(values, value) + ) { + values.add(value); + } + } + static void mergeCompactedValue(Map obj, String key, Object value) { if (obj == null) { return; diff --git a/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java b/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java index 929b11e2..e7b8e4f0 100644 --- a/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java +++ b/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java @@ -3,8 +3,12 @@ */ package com.github.jsonldjava.core; +import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.DoubleSummaryStatistics; @@ -13,8 +17,12 @@ import java.util.Random; import java.util.zip.GZIPInputStream; +import org.apache.commons.io.FileUtils; +import org.junit.Before; import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import com.github.jsonldjava.core.RDFDataset.Quad; import com.github.jsonldjava.utils.JsonUtils; @@ -25,6 +33,16 @@ */ public class JsonLdPerformanceTest { + @Rule + public TemporaryFolder tempDir = new TemporaryFolder(); + + private File testDir; + + @Before + public void setUp() throws Exception { + testDir = tempDir.newFolder("jsonld-perf-tests-"); + } + /** * Test performance parsing using test data from: * @@ -34,23 +52,70 @@ public class JsonLdPerformanceTest { */ @Ignore("Enable as necessary for manual testing, particularly to test that it fails due to irregular URIs") @Test - public final void test() throws Exception { - final long parseStart = System.currentTimeMillis(); - final Object inputObject = JsonUtils.fromInputStream(new GZIPInputStream( - new FileInputStream(new File("/home/ans025/Downloads/2000007922.jsonld.gz")))); - final long parseEnd = System.currentTimeMillis(); - System.out.printf("Parse time: %d", (parseEnd - parseStart)); - final JsonLdOptions opts = new JsonLdOptions("urn:test:"); - - final long compactStart = System.currentTimeMillis(); - JsonLdProcessor.compact(inputObject, null, opts); - final long compactEnd = System.currentTimeMillis(); - System.out.printf("Compaction time: %d", (compactEnd - compactStart)); + public final void testPerformance1() throws Exception { + testCompaction("Long", new GZIPInputStream( + new FileInputStream(new File("/home/peter/Downloads/2000007922.jsonld.gz")))); + } + + /** + * Test performance parsing using test data from: + * + * https://github.com/jsonld-java/jsonld-java/files/245372/jsonldperfs.zip + * + * @throws Exception + */ + @Ignore("Enable as necessary to test performance") + @Test + public final void testLaxMergeValuesPerfFast() throws Exception { + testCompaction("Fast", + new FileInputStream(new File("/home/peter/Downloads/jsonldperfs/fast.jsonld"))); + } + + /** + * Test performance parsing using test data from: + * + * https://github.com/jsonld-java/jsonld-java/files/245372/jsonldperfs.zip + * + * @throws Exception + */ + @Ignore("Enable as necessary to test performance") + @Test + public final void testLaxMergeValuesPerfSlow() throws Exception { + testCompaction("Slow", + new FileInputStream(new File("/home/peter/Downloads/jsonldperfs/slow.jsonld"))); + } + + private void testCompaction(String label, InputStream nextInputStream) + throws IOException, FileNotFoundException, JsonLdError { + File testFile = File.createTempFile("jsonld-perf-source-", ".jsonld", testDir); + FileUtils.copyInputStreamToFile(nextInputStream, testFile); + + LongSummaryStatistics parseStats = new LongSummaryStatistics(); + LongSummaryStatistics compactStats = new LongSummaryStatistics(); + + for (int i = 0; i < 1000; i++) { + InputStream testInput = new BufferedInputStream(new FileInputStream(testFile)); + try { + final long parseStart = System.currentTimeMillis(); + final Object inputObject = JsonUtils.fromInputStream(testInput); + parseStats.accept(System.currentTimeMillis() - parseStart); + final JsonLdOptions opts = new JsonLdOptions("urn:test:"); + + final long compactStart = System.currentTimeMillis(); + JsonLdProcessor.compact(inputObject, null, opts); + compactStats.accept(System.currentTimeMillis() - compactStart); + } finally { + testInput.close(); + } + } + + System.out.println("(" + label + ") Parse average : " + parseStats.getAverage()); + System.out.println("(" + label + ") Compact average : " + compactStats.getAverage()); } @Ignore("Disable performance tests by default") @Test - public final void testPerformance() throws Exception { + public final void testPerformanceRandom() throws Exception { Random prng = new Random(); int rounds = 2000; @@ -72,8 +137,8 @@ public final void testPerformance() throws Exception { potentialSubjects.add("_:a" + Integer.toHexString(i).toUpperCase()); } for (int i = 0; i < 200; i++) { - potentialSubjects.add(exNs + Integer.toHexString(i) + "/z" - + Integer.toOctalString(i % 20)); + potentialSubjects + .add(exNs + Integer.toHexString(i) + "/z" + Integer.toOctalString(i % 20)); } Collections.shuffle(potentialSubjects, prng); @@ -125,8 +190,8 @@ public final void testPerformance() throws Exception { } } - System.out - .println("RDF triples to JSON-LD (internal objects, not parsed from a document)..."); + System.out.println( + "RDF triples to JSON-LD (internal objects, not parsed from a document)..."); JsonLdOptions options = new JsonLdOptions(); JsonLdApi jsonLdApi = new JsonLdApi(options); int[] hashCodes = new int[rounds]; From 3b4200cf7271de77d62d8aca97f89bc3eed59d2e Mon Sep 17 00:00:00 2001 From: Peter Ansell Date: Tue, 17 May 2016 21:59:06 -0400 Subject: [PATCH 07/11] Note that need to use Java-8 to build this branch due to the use of Java-8 classes in tests. However, the rest of the codebase is still compatible with Java-6. --- .travis.yml | 1 - README.md | 1 + pom.xml | 33 +++++++++++++++++++++++++++++---- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 36aa3a3e..312621f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: java jdk: - - oraclejdk7 - oraclejdk8 notifications: email: diff --git a/README.md b/README.md index 33585b0d..2fa376d0 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,7 @@ For Developers `jsonld-java` uses maven to compile. From the base `jsonld-java` module run `mvn clean install` to install the jar into your local maven repository. +The tests require Java-8 to compile, while the rest of the codebase is still compatible and built using the Java-6 APIs. ### Running tests diff --git a/pom.xml b/pom.xml index b3f6baa8..aba0cb75 100755 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,11 @@ 2.7.4 4.12 1.7.21 + + 1.6 + 1.6 + 1.8 + 1.8 3.0.5 @@ -207,10 +212,30 @@ org.apache.maven.plugins maven-compiler-plugin 3.5.1 - - 1.6 - 1.6 - + + + default-compile + + true + true + + ${maven.compiler.target} + ${maven.compiler.source} + + + + + default-testCompile + + true + true + + ${maven.compiler.testTarget} + ${maven.compiler.testSource} + + + + org.apache.maven.plugins From 157d6ffa440e3e3c3d6ab93e19a9dc9e77813705 Mon Sep 17 00:00:00 2001 From: Peter Ansell Date: Wed, 18 May 2016 00:35:56 -0400 Subject: [PATCH 08/11] Add ide profile to allow Eclipse to use Java-8 for test compilation --- pom.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pom.xml b/pom.xml index aba0cb75..7b0908d5 100755 --- a/pom.xml +++ b/pom.xml @@ -462,6 +462,24 @@ + + ide + + false + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven.compiler.testSource} + ${maven.compiler.testTarget} + + + + + From 5cc8c382921796d40e5644bbdf9cd0abb0d29327 Mon Sep 17 00:00:00 2001 From: Peter Ansell Date: Wed, 18 May 2016 00:36:48 -0400 Subject: [PATCH 09/11] Add support for laxMergeValues --- .../com/github/jsonldjava/core/JsonLdApi.java | 210 +++++++++++------- .../github/jsonldjava/core/JsonLdUtils.java | 8 +- 2 files changed, 130 insertions(+), 88 deletions(-) diff --git a/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java b/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java index b3f1befb..a7f5d548 100644 --- a/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java +++ b/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java @@ -161,7 +161,8 @@ public Object compact(Context activeCtx, String activeProperty, Object element, // 2.2) for (final Object item : (List) element) { // 2.2.1) - final Object compactedItem = compact(activeCtx, activeProperty, item, compactArrays); + final Object compactedItem = compact(activeCtx, activeProperty, item, + compactArrays); // 2.2.2) if (compactedItem != null) { result.add(compactedItem); @@ -200,7 +201,8 @@ public Object compact(Context activeCtx, String activeProperty, Object element, final Object expandedValue = elem.get(expandedProperty); // 7.1) - if (JsonLdConsts.ID.equals(expandedProperty) || JsonLdConsts.TYPE.equals(expandedProperty)) { + if (JsonLdConsts.ID.equals(expandedProperty) + || JsonLdConsts.TYPE.equals(expandedProperty)) { Object compactedValue; // 7.1.1) @@ -248,8 +250,8 @@ public Object compact(Context activeCtx, String activeProperty, Object element, // 7.2.2.1) if (activeCtx.isReverseProperty(property)) { // 7.2.2.1.1) - if ((JsonLdConsts.SET.equals(activeCtx.getContainer(property)) || !compactArrays) - && !(value instanceof List)) { + if ((JsonLdConsts.SET.equals(activeCtx.getContainer(property)) + || !compactArrays) && !(value instanceof List)) { final List tmp = new ArrayList(); tmp.add(value); result.put(property, tmp); @@ -292,7 +294,8 @@ public Object compact(Context activeCtx, String activeProperty, Object element, continue; } // 7.4) - else if (JsonLdConsts.INDEX.equals(expandedProperty) || JsonLdConsts.VALUE.equals(expandedProperty) + else if (JsonLdConsts.INDEX.equals(expandedProperty) + || JsonLdConsts.VALUE.equals(expandedProperty) || JsonLdConsts.LANGUAGE.equals(expandedProperty)) { // 7.4.1) final String alias = activeCtx.compactIri(expandedProperty, true); @@ -331,16 +334,16 @@ else if (JsonLdConsts.INDEX.equals(expandedProperty) || JsonLdConsts.VALUE.equal final String container = activeCtx.getContainer(itemActiveProperty); // get @list value if appropriate - final boolean isList = (expandedItem instanceof Map && ((Map) expandedItem) - .containsKey(JsonLdConsts.LIST)); + final boolean isList = (expandedItem instanceof Map + && ((Map) expandedItem).containsKey(JsonLdConsts.LIST)); Object list = null; if (isList) { list = ((Map) expandedItem).get(JsonLdConsts.LIST); } // 7.6.3) - Object compactedItem = compact(activeCtx, itemActiveProperty, isList ? list - : expandedItem, compactArrays); + Object compactedItem = compact(activeCtx, itemActiveProperty, + isList ? list : expandedItem, compactArrays); // 7.6.4) if (isList) { @@ -355,16 +358,19 @@ else if (JsonLdConsts.INDEX.equals(expandedProperty) || JsonLdConsts.VALUE.equal // 7.6.4.2.1) final Map wrapper = newMap(); // TODO: SPEC: no mention of vocab = true - wrapper.put(activeCtx.compactIri(JsonLdConsts.LIST, true), compactedItem); + wrapper.put(activeCtx.compactIri(JsonLdConsts.LIST, true), + compactedItem); compactedItem = wrapper; // 7.6.4.2.2) - if (((Map) expandedItem).containsKey(JsonLdConsts.INDEX)) { + if (((Map) expandedItem) + .containsKey(JsonLdConsts.INDEX)) { ((Map) compactedItem).put( // TODO: SPEC: no mention of vocab = // true activeCtx.compactIri(JsonLdConsts.INDEX, true), - ((Map) expandedItem).get(JsonLdConsts.INDEX)); + ((Map) expandedItem) + .get(JsonLdConsts.INDEX)); } } // 7.6.4.3) @@ -375,7 +381,8 @@ else if (result.containsKey(itemActiveProperty)) { } // 7.6.5) - if (JsonLdConsts.LANGUAGE.equals(container) || JsonLdConsts.INDEX.equals(container)) { + if (JsonLdConsts.LANGUAGE.equals(container) + || JsonLdConsts.INDEX.equals(container)) { // 7.6.5.1) Map mapObject; if (result.containsKey(itemActiveProperty)) { @@ -386,10 +393,11 @@ else if (result.containsKey(itemActiveProperty)) { } // 7.6.5.2) - if (JsonLdConsts.LANGUAGE.equals(container) - && (compactedItem instanceof Map && ((Map) compactedItem) + if (JsonLdConsts.LANGUAGE.equals(container) && (compactedItem instanceof Map + && ((Map) compactedItem) .containsKey(JsonLdConsts.VALUE))) { - compactedItem = ((Map) compactedItem).get(JsonLdConsts.VALUE); + compactedItem = ((Map) compactedItem) + .get(JsonLdConsts.VALUE); } // 7.6.5.3) @@ -413,8 +421,9 @@ else if (result.containsKey(itemActiveProperty)) { else { // 7.6.6.1) final Boolean check = (!compactArrays || JsonLdConsts.SET.equals(container) - || JsonLdConsts.LIST.equals(container) || JsonLdConsts.LIST.equals(expandedProperty) || JsonLdConsts.GRAPH - .equals(expandedProperty)) + || JsonLdConsts.LIST.equals(container) + || JsonLdConsts.LIST.equals(expandedProperty) + || JsonLdConsts.GRAPH.equals(expandedProperty)) && (!(compactedItem instanceof List)); if (check) { final List tmp = new ArrayList(); @@ -507,10 +516,10 @@ public Object expand(Context activeCtx, String activeProperty, Object element) // 3.2.1) final Object v = expand(activeCtx, activeProperty, item); // 3.2.2) - if ((JsonLdConsts.LIST.equals(activeProperty) || JsonLdConsts.LIST.equals(activeCtx - .getContainer(activeProperty))) - && (v instanceof List || (v instanceof Map && ((Map) v) - .containsKey(JsonLdConsts.LIST)))) { + if ((JsonLdConsts.LIST.equals(activeProperty) + || JsonLdConsts.LIST.equals(activeCtx.getContainer(activeProperty))) + && (v instanceof List || (v instanceof Map + && ((Map) v).containsKey(JsonLdConsts.LIST)))) { throw new JsonLdError(Error.LIST_OF_LISTS, "lists of lists are not permitted."); } // 3.2.3) @@ -561,8 +570,8 @@ else if (element instanceof Map) { } // 7.4.2) if (result.containsKey(expandedProperty)) { - throw new JsonLdError(Error.COLLIDING_KEYWORDS, expandedProperty - + " already exists in result"); + throw new JsonLdError(Error.COLLIDING_KEYWORDS, + expandedProperty + " already exists in result"); } // 7.4.3) if (JsonLdConsts.ID.equals(expandedProperty)) { @@ -570,8 +579,8 @@ else if (element instanceof Map) { throw new JsonLdError(Error.INVALID_ID_VALUE, "value of @id must be a string"); } - expandedValue = activeCtx - .expandIri((String) value, true, false, null, null); + expandedValue = activeCtx.expandIri((String) value, true, false, null, + null); } // 7.4.4) else if (JsonLdConsts.TYPE.equals(expandedProperty)) { @@ -582,8 +591,8 @@ else if (JsonLdConsts.TYPE.equals(expandedProperty)) { throw new JsonLdError(Error.INVALID_TYPE_VALUE, "@type value must be a string or array of strings"); } - ((List) expandedValue).add(activeCtx.expandIri((String) v, - true, true, null, null)); + ((List) expandedValue).add( + activeCtx.expandIri((String) v, true, true, null, null)); } } else if (value instanceof String) { expandedValue = activeCtx.expandIri((String) value, true, true, null, @@ -608,8 +617,8 @@ else if (JsonLdConsts.GRAPH.equals(expandedProperty)) { // 7.4.6) else if (JsonLdConsts.VALUE.equals(expandedProperty)) { if (value != null && (value instanceof Map || value instanceof List)) { - throw new JsonLdError(Error.INVALID_VALUE_OBJECT_VALUE, "value of " - + expandedProperty + " must be a scalar or null"); + throw new JsonLdError(Error.INVALID_VALUE_OBJECT_VALUE, + "value of " + expandedProperty + " must be a scalar or null"); } expandedValue = value; if (expandedValue == null) { @@ -620,16 +629,16 @@ else if (JsonLdConsts.VALUE.equals(expandedProperty)) { // 7.4.7) else if (JsonLdConsts.LANGUAGE.equals(expandedProperty)) { if (!(value instanceof String)) { - throw new JsonLdError(Error.INVALID_LANGUAGE_TAGGED_STRING, "Value of " - + expandedProperty + " must be a string"); + throw new JsonLdError(Error.INVALID_LANGUAGE_TAGGED_STRING, + "Value of " + expandedProperty + " must be a string"); } expandedValue = ((String) value).toLowerCase(); } // 7.4.8) else if (JsonLdConsts.INDEX.equals(expandedProperty)) { if (!(value instanceof String)) { - throw new JsonLdError(Error.INVALID_INDEX_VALUE, "Value of " - + expandedProperty + " must be a string"); + throw new JsonLdError(Error.INVALID_INDEX_VALUE, + "Value of " + expandedProperty + " must be a string"); } expandedValue = value; } @@ -651,7 +660,8 @@ else if (JsonLdConsts.LIST.equals(expandedProperty)) { // 7.4.9.3) for (final Object o : (List) expandedValue) { - if (o instanceof Map && ((Map) o).containsKey(JsonLdConsts.LIST)) { + if (o instanceof Map + && ((Map) o).containsKey(JsonLdConsts.LIST)) { throw new JsonLdError(Error.LIST_OF_LISTS, "A list may not contain another list"); } @@ -671,7 +681,8 @@ else if (JsonLdConsts.REVERSE.equals(expandedProperty)) { expandedValue = expand(activeCtx, JsonLdConsts.REVERSE, value); // NOTE: algorithm assumes the result is a map // 7.4.11.2) - if (((Map) expandedValue).containsKey(JsonLdConsts.REVERSE)) { + if (((Map) expandedValue) + .containsKey(JsonLdConsts.REVERSE)) { final Map reverse = (Map) ((Map) expandedValue) .get(JsonLdConsts.REVERSE); for (final String property : reverse.keySet()) { @@ -690,8 +701,9 @@ else if (JsonLdConsts.REVERSE.equals(expandedProperty)) { } } // 7.4.11.3) - if (((Map) expandedValue).size() > (((Map) expandedValue) - .containsKey(JsonLdConsts.REVERSE) ? 1 : 0)) { + if (((Map) expandedValue) + .size() > (((Map) expandedValue) + .containsKey(JsonLdConsts.REVERSE) ? 1 : 0)) { // 7.4.11.3.1) if (!result.containsKey(JsonLdConsts.REVERSE)) { result.put(JsonLdConsts.REVERSE, newMap()); @@ -710,8 +722,9 @@ else if (JsonLdConsts.REVERSE.equals(expandedProperty)) { .get(property); for (final Object item : items) { // 7.4.11.3.3.1.1) - if (item instanceof Map - && (((Map) item).containsKey(JsonLdConsts.VALUE) || ((Map) item) + if (item instanceof Map && (((Map) item) + .containsKey(JsonLdConsts.VALUE) + || ((Map) item) .containsKey(JsonLdConsts.LIST))) { throw new JsonLdError(Error.INVALID_REVERSE_PROPERTY_VALUE); } @@ -743,7 +756,8 @@ else if (JsonLdConsts.EXPLICIT.equals(expandedProperty) continue; } // 7.5 - else if (JsonLdConsts.LANGUAGE.equals(activeCtx.getContainer(key)) && value instanceof Map) { + else if (JsonLdConsts.LANGUAGE.equals(activeCtx.getContainer(key)) + && value instanceof Map) { // 7.5.1) expandedValue = new ArrayList(); // 7.5.2) @@ -759,8 +773,8 @@ else if (JsonLdConsts.LANGUAGE.equals(activeCtx.getContainer(key)) && value inst for (final Object item : (List) languageValue) { // 7.5.2.2.1) if (!(item instanceof String)) { - throw new JsonLdError(Error.INVALID_LANGUAGE_MAP_VALUE, "Expected " - + item.toString() + " to be a string"); + throw new JsonLdError(Error.INVALID_LANGUAGE_MAP_VALUE, + "Expected " + item.toString() + " to be a string"); } // 7.5.2.2.2) final Map tmp = newMap(); @@ -771,7 +785,8 @@ else if (JsonLdConsts.LANGUAGE.equals(activeCtx.getContainer(key)) && value inst } } // 7.6) - else if (JsonLdConsts.INDEX.equals(activeCtx.getContainer(key)) && value instanceof Map) { + else if (JsonLdConsts.INDEX.equals(activeCtx.getContainer(key)) + && value instanceof Map) { // 7.6.1) expandedValue = new ArrayList(); // 7.6.2) @@ -809,8 +824,8 @@ else if (JsonLdConsts.INDEX.equals(activeCtx.getContainer(key)) && value instanc } // 7.9) if (JsonLdConsts.LIST.equals(activeCtx.getContainer(key))) { - if (!(expandedValue instanceof Map) - || !((Map) expandedValue).containsKey(JsonLdConsts.LIST)) { + if (!(expandedValue instanceof Map) || !((Map) expandedValue) + .containsKey(JsonLdConsts.LIST)) { Object tmp = expandedValue; if (!(tmp instanceof List)) { tmp = new ArrayList(); @@ -838,9 +853,9 @@ else if (JsonLdConsts.INDEX.equals(activeCtx.getContainer(key)) && value instanc // 7.10.4) for (final Object item : (List) expandedValue) { // 7.10.4.1) - if (item instanceof Map - && (((Map) item).containsKey(JsonLdConsts.VALUE) || ((Map) item) - .containsKey(JsonLdConsts.LIST))) { + if (item instanceof Map && (((Map) item) + .containsKey(JsonLdConsts.VALUE) + || ((Map) item).containsKey(JsonLdConsts.LIST))) { throw new JsonLdError(Error.INVALID_REVERSE_PROPERTY_VALUE); } // 7.10.4.2) @@ -918,7 +933,8 @@ else if (result.containsKey(JsonLdConsts.TYPE)) { } } // 10) - else if (result.containsKey(JsonLdConsts.SET) || result.containsKey(JsonLdConsts.LIST)) { + else if (result.containsKey(JsonLdConsts.SET) + || result.containsKey(JsonLdConsts.LIST)) { // 10.1) if (result.size() > (result.containsKey(JsonLdConsts.INDEX) ? 2 : 1)) { throw new JsonLdError(Error.INVALID_SET_OR_LIST_OBJECT, @@ -941,13 +957,13 @@ else if (result.containsKey(JsonLdConsts.SET) || result.containsKey(JsonLdConsts // 12) if (activeProperty == null || JsonLdConsts.GRAPH.equals(activeProperty)) { // 12.1) - if (result != null - && (result.size() == 0 || result.containsKey(JsonLdConsts.VALUE) || result - .containsKey(JsonLdConsts.LIST))) { + if (result != null && (result.size() == 0 || result.containsKey(JsonLdConsts.VALUE) + || result.containsKey(JsonLdConsts.LIST))) { result = null; } // 12.2) - else if (result != null && result.containsKey(JsonLdConsts.ID) && result.size() == 1) { + else if (result != null && result.containsKey(JsonLdConsts.ID) + && result.size() == 1) { result = null; } } @@ -1018,8 +1034,8 @@ void generateNodeMap(Object element, Map nodeMap, String activeG nodeMap.put(activeGraph, newMap()); } final Map graph = (Map) nodeMap.get(activeGraph); - Map node = (Map) (activeSubject == null ? null : graph - .get(activeSubject)); + Map node = (Map) (activeSubject == null ? null + : graph.get(activeSubject)); // 3) if (elem.containsKey(JsonLdConsts.TYPE)) { @@ -1067,8 +1083,8 @@ else if (elem.containsKey(JsonLdConsts.LIST)) { // generateNodeMap(item, nodeMap, activeGraph, activeSubject, // activeProperty, result); // } - generateNodeMap(elem.get(JsonLdConsts.LIST), nodeMap, activeGraph, activeSubject, activeProperty, - result); + generateNodeMap(elem.get(JsonLdConsts.LIST), nodeMap, activeGraph, activeSubject, + activeProperty, result); // 5.3) JsonLdUtils.mergeValue(node, activeProperty, result); } @@ -1147,7 +1163,8 @@ else if (activeProperty != null) { // 6.9.3.1) for (final Object value : values) { // 6.9.3.1.1) - generateNodeMap(value, nodeMap, activeGraph, referencedNode, property, null); + generateNodeMap(value, nodeMap, activeGraph, referencedNode, property, + null); } } } @@ -1268,8 +1285,8 @@ private class EmbedNode { private Map nodeMap; /** - * Performs JSON-LD framing. + * Performs JSON-LD + * framing. * * @param input * the expanded JSON-LD to frame. @@ -1291,8 +1308,7 @@ public List frame(Object input, List frame) throws JsonLdError { final List framed = new ArrayList(); // NOTE: frame validation is done by the function not allowing anything // other than list to me passed - frame(state, - this.nodeMap, + frame(state, this.nodeMap, (frame != null && frame.size() > 0 ? (Map) frame.get(0) : newMap()), framed, null); @@ -1360,8 +1376,8 @@ private void frame(FramingContext state, Map nodes, Map) existing.parent).containsKey(existing.property)) { for (final Object v : (List) ((Map) existing.parent) .get(existing.property)) { - if (v instanceof Map - && Obj.equals(id, ((Map) v).get(JsonLdConsts.ID))) { + if (v instanceof Map && Obj.equals(id, + ((Map) v).get(JsonLdConsts.ID))) { embedOn = true; break; } @@ -1429,7 +1445,8 @@ private void frame(FramingContext state, Map nodes, Map) ((List) frame.get(prop)) - .get(0), list, JsonLdConsts.LIST); + .get(0), + list, JsonLdConsts.LIST); } else { // include other values automatcially (TODO: // may need JsonLdUtils.clone(n)) @@ -1441,7 +1458,8 @@ private void frame(FramingContext state, Map nodes, Map tmp = newMap(); - final String itemid = (String) ((Map) item).get(JsonLdConsts.ID); + final String itemid = (String) ((Map) item) + .get(JsonLdConsts.ID); // TODO: nodes may need to be node_map, which is // global tmp.put(itemid, this.nodeMap.get(itemid)); @@ -1466,13 +1484,13 @@ else if (JsonLdUtils.isNodeReference(item)) { } final List pf = (List) frame.get(prop); - Map propertyFrame = pf.size() > 0 ? (Map) pf - .get(0) : null; + Map propertyFrame = pf.size() > 0 + ? (Map) pf.get(0) : null; if (propertyFrame == null) { propertyFrame = newMap(); } - final boolean omitDefaultOn = getFrameFlag(propertyFrame, JsonLdConsts.OMIT_DEFAULT, - state.omitDefault); + final boolean omitDefaultOn = getFrameFlag(propertyFrame, + JsonLdConsts.OMIT_DEFAULT, state.omitDefault); if (!omitDefaultOn && !output.containsKey(prop)) { Object def = "@null"; if (propertyFrame.containsKey(JsonLdConsts.DEFAULT)) { @@ -1537,7 +1555,8 @@ private static void removeEmbed(FramingContext state, String id) { final List oldvals = (List) ((Map) parent) .get(property); for (final Object v : oldvals) { - if (v instanceof Map && Obj.equals(((Map) v).get(JsonLdConsts.ID), id)) { + if (v instanceof Map + && Obj.equals(((Map) v).get(JsonLdConsts.ID), id)) { newvals.add(node); } else { newvals.add(v); @@ -1754,19 +1773,22 @@ public boolean isWellFormedListNode() { int keys = 0; if (containsKey(RDF_FIRST)) { keys++; - if (!(get(RDF_FIRST) instanceof List && ((List) get(RDF_FIRST)).size() == 1)) { + if (!(get(RDF_FIRST) instanceof List + && ((List) get(RDF_FIRST)).size() == 1)) { return false; } } if (containsKey(RDF_REST)) { keys++; - if (!(get(RDF_REST) instanceof List && ((List) get(RDF_REST)).size() == 1)) { + if (!(get(RDF_REST) instanceof List + && ((List) get(RDF_REST)).size() == 1)) { return false; } } if (containsKey(JsonLdConsts.TYPE)) { keys++; - if (!(get(JsonLdConsts.TYPE) instanceof List && ((List) get(JsonLdConsts.TYPE)).size() == 1) + if (!(get(JsonLdConsts.TYPE) instanceof List + && ((List) get(JsonLdConsts.TYPE)).size() == 1) && RDF_LIST.equals(((List) get(JsonLdConsts.TYPE)).get(0))) { return false; } @@ -1797,10 +1819,28 @@ public Map serialize() { * If there was an error during conversion from RDF to JSON-LD. */ public List fromRDF(final RDFDataset dataset) throws JsonLdError { + return fromRDF(dataset, false); + } + + /** + * Converts RDF statements into JSON-LD, presuming that there are no duplicates in the dataset. + * + * @param dataset + * the RDF statements. + * @param noDuplicatesInDataset + * True if there are no duplicates in the dataset and false otherwise. + * @return A list of JSON-LD objects found in the given dataset. + * @throws JsonLdError + * If there was an error during conversion from RDF to JSON-LD. + * @deprecated Experimental method, only use if you are sure you need to use this method. Most users will need to use {@link #fromRDF(RDFDataset)}. + */ + @Deprecated + public List fromRDF(final RDFDataset dataset, boolean noDuplicatesInDataset) throws JsonLdError { // 1) final Map defaultGraph = new LinkedHashMap(4); // 2) - final Map> graphMap = new LinkedHashMap>(4); + final Map> graphMap = new LinkedHashMap>( + 4); graphMap.put(JsonLdConsts.DEFAULT, defaultGraph); // 3/3.1) @@ -1854,9 +1894,12 @@ public List fromRDF(final RDFDataset dataset) throws JsonLdError { final Map value = object.toObject(opts.getUseNativeTypes()); // 3.5.6+7) - JsonLdUtils.mergeValue(node, predicate, value); - // JsonLdUtils.laxMergeValue(node, predicate, value); - + if(noDuplicatesInDataset) { + JsonLdUtils.laxMergeValue(node, predicate, value); + } else { + JsonLdUtils.mergeValue(node, predicate, value); + } + // 3.5.8) if (object.isBlankNode() || object.isIRI()) { // 3.5.8.1-3) @@ -2049,9 +2092,8 @@ public Object normalize(Map dataset) throws JsonLdError { final String[] attrs = new String[] { "subject", "object", "name" }; for (final String attr : attrs) { - if (quad.containsKey(attr) - && "blank node".equals(((Map) quad.get(attr)) - .get("type"))) { + if (quad.containsKey(attr) && "blank node" + .equals(((Map) quad.get(attr)).get("type"))) { final String id = (String) ((Map) quad.get(attr)) .get("value"); if (!bnodes.containsKey(id)) { @@ -2069,8 +2111,8 @@ public Object normalize(Map dataset) throws JsonLdError { } // mapping complete, start canonical naming - final NormalizeUtils normalizeUtils = new NormalizeUtils(quads, bnodes, new UniqueNamer( - "_:c14n"), opts); + final NormalizeUtils normalizeUtils = new NormalizeUtils(quads, bnodes, + new UniqueNamer("_:c14n"), opts); return normalizeUtils.hashBlankNodes(bnodes.keySet()); } diff --git a/core/src/main/java/com/github/jsonldjava/core/JsonLdUtils.java b/core/src/main/java/com/github/jsonldjava/core/JsonLdUtils.java index 10310001..10955298 100644 --- a/core/src/main/java/com/github/jsonldjava/core/JsonLdUtils.java +++ b/core/src/main/java/com/github/jsonldjava/core/JsonLdUtils.java @@ -127,12 +127,12 @@ static void laxMergeValue(Map obj, String key, Object value) { values = new ArrayList(); obj.put(key, values); } - if ("@list".equals(key) - || (value instanceof Map && ((Map) value).containsKey("@list")) + //if ("@list".equals(key) + // || (value instanceof Map && ((Map) value).containsKey("@list")) //|| !deepContains(values, value) - ) { + // ) { values.add(value); - } + //} } static void mergeCompactedValue(Map obj, String key, Object value) { From 0d19da22087a47d83ed29462cbce1c932d014265 Mon Sep 17 00:00:00 2001 From: Peter Ansell Date: Wed, 18 May 2016 00:37:14 -0400 Subject: [PATCH 10/11] Add performance tests for the rdf parsing methods --- .../core/JsonLdPerformanceTest.java | 249 +++++++++++++++++- 1 file changed, 246 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java b/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java index e7b8e4f0..7fa4ec20 100644 --- a/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java +++ b/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.LongSummaryStatistics; import java.util.Random; +import java.util.function.Function; import java.util.zip.GZIPInputStream; import org.apache.commons.io.FileUtils; @@ -113,11 +114,11 @@ private void testCompaction(String label, InputStream nextInputStream) System.out.println("(" + label + ") Compact average : " + compactStats.getAverage()); } - @Ignore("Disable performance tests by default") + // @Ignore("Disable performance tests by default") @Test public final void testPerformanceRandom() throws Exception { Random prng = new Random(); - int rounds = 2000; + int rounds = 10000; String exNs = "http://example.org/"; @@ -157,7 +158,7 @@ public final void testPerformanceRandom() throws Exception { RDFDataset testData = new RDFDataset(); - for (int i = 0; i < 8000; i++) { + for (int i = 0; i < 2000; i++) { String nextObject = potentialObjects.get(prng.nextInt(potentialObjects.size())); boolean isLiteral = true; if (nextObject.startsWith("_:") || nextObject.startsWith("http://")) { @@ -222,6 +223,38 @@ public final void testPerformanceRandom() throws Exception { System.out.println("Minimum: " + stats.getMin() / 100000); System.out.println("Count: " + stats.getCount()); + System.out.println( + "RDF triples to JSON-LD (internal objects, not parsed from a document), using laxMergeValue..."); + JsonLdOptions optionsLax = new JsonLdOptions(); + JsonLdApi jsonLdApiLax = new JsonLdApi(optionsLax); + int[] hashCodesLax = new int[rounds]; + LongSummaryStatistics statsLaxFirst5000 = new LongSummaryStatistics(); + LongSummaryStatistics statsLax = new LongSummaryStatistics(); + for (int i = 0; i < rounds; i++) { + long start = System.nanoTime(); + Object fromRDF = jsonLdApiLax.fromRDF(testData, true); + if (i < 5000) { + statsLaxFirst5000.accept(System.nanoTime() - start); + } else { + statsLax.accept(System.nanoTime() - start); + } + hashCodesLax[i] = fromRDF.hashCode(); + fromRDF = null; + } + System.out.println("First 5000 out of " + rounds); + System.out.println("Average: " + statsLaxFirst5000.getAverage() / 100000); + System.out.println("Sum: " + statsLaxFirst5000.getSum() / 100000); + System.out.println("Maximum: " + statsLaxFirst5000.getMax() / 100000); + System.out.println("Minimum: " + statsLaxFirst5000.getMin() / 100000); + System.out.println("Count: " + statsLaxFirst5000.getCount()); + + System.out.println("Post 5000 out of " + rounds); + System.out.println("Average: " + statsLax.getAverage() / 100000); + System.out.println("Sum: " + statsLax.getSum() / 100000); + System.out.println("Maximum: " + statsLax.getMax() / 100000); + System.out.println("Minimum: " + statsLax.getMin() / 100000); + System.out.println("Count: " + statsLax.getCount()); + System.out.println("Non-pretty print benchmarking..."); JsonLdOptions options2 = new JsonLdOptions(); JsonLdApi jsonLdApi2 = new JsonLdApi(options2); @@ -310,4 +343,214 @@ public final void testPerformanceRandom() throws Exception { System.out.println("Count: " + statsPart4.getCount()); } + + /** + * many triples with same subject and prop: current implementation is slow + * + * @author fpservant + */ + @Test + public final void slowVsFast5Predicates() throws Exception { + + final String ns = "http://www.example.com/foo/"; + + Function subjectGenerator = new Function() { + public String apply(Integer index) { + return ns + "s"; + } + }; + Function predicateGenerator = new Function() { + public String apply(Integer index) { + return ns + "p" + Integer.toString(index % 5); + } + }; + Function objectGenerator = new Function() { + public String apply(Integer index) { + return ns + "o" + Integer.toString(index); + } + }; + int tripleCount = 2000; + int warmingRounds = 200; + int rounds = 1000; + + runLaxVersusSlowToRDFTest("5 predicates", ns, subjectGenerator, predicateGenerator, + objectGenerator, tripleCount, warmingRounds, rounds); + + } + + /** + * many triples with same subject and prop: current implementation is slow + * + * @author fpservant + */ + @Test + public final void slowVsFast2Predicates() throws Exception { + + final String ns = "http://www.example.com/foo/"; + + Function subjectGenerator = new Function() { + public String apply(Integer index) { + return ns + "s"; + } + }; + Function predicateGenerator = new Function() { + public String apply(Integer index) { + return ns + "p" + Integer.toString(index % 2); + } + }; + Function objectGenerator = new Function() { + public String apply(Integer index) { + return ns + "o" + Integer.toString(index); + } + }; + int tripleCount = 2000; + int warmingRounds = 200; + int rounds = 1000; + + runLaxVersusSlowToRDFTest("2 predicates", ns, subjectGenerator, predicateGenerator, + objectGenerator, tripleCount, warmingRounds, rounds); + + } + + /** + * many triples with same subject and prop: current implementation is slow + * + * @author fpservant + */ + @Test + public final void slowVsFast1Predicate() throws Exception { + + final String ns = "http://www.example.com/foo/"; + + Function subjectGenerator = new Function() { + public String apply(Integer index) { + return ns + "s"; + } + }; + Function predicateGenerator = new Function() { + public String apply(Integer index) { + return ns + "p"; + } + }; + Function objectGenerator = new Function() { + public String apply(Integer index) { + return ns + "o" + Integer.toString(index); + } + }; + int tripleCount = 2000; + int warmingRounds = 200; + int rounds = 1000; + + runLaxVersusSlowToRDFTest("1 predicate", ns, subjectGenerator, predicateGenerator, + objectGenerator, tripleCount, warmingRounds, rounds); + + } + + /** + * Run a test on lax versus slow methods for toRDF. + * + * @param ns + * The namespace to assign + * @param subjectGenerator + * A {@link Function} used to generate the subject IRIs + * @param predicateGenerator + * A {@link Function} used to generate the predicate IRIs + * @param objectGenerator + * A {@link Function} used to generate the object IRIs + * @param tripleCount + * The number of triples to create for the dataset + * @param warmingRounds + * The number of warming rounds to use + * @param rounds + * The number of test rounds to use + * @throws JsonLdError + * If there is an error with the JSONLD processing. + */ + public void runLaxVersusSlowToRDFTest(final String label, final String ns, + Function subjectGenerator, + Function predicateGenerator, Function objectGenerator, + int tripleCount, int warmingRounds, int rounds) throws JsonLdError { + + System.out.println("Running test for lax versus slow for " + label); + + RDFDataset inputRdf = new RDFDataset(); + inputRdf.setNamespace("ex", ns); + + for (int i = 0; i < tripleCount; i++) { + inputRdf.addTriple(subjectGenerator.apply(i), predicateGenerator.apply(i), + objectGenerator.apply(i)); + } + + final JsonLdOptions options = new JsonLdOptions(); + options.useNamespaces = true; + + // warming + for (int i = 0; i < warmingRounds; i++) { + new JsonLdApi(options).fromRDF(inputRdf); + // JsonLdProcessor.expand(new JsonLdApi(options).fromRDF(inputRdf)); + } + + for (int i = 0; i < warmingRounds; i++) { + new JsonLdApi(options).fromRDF(inputRdf, true); + // JsonLdProcessor.expand(new JsonLdApi(options).fromRDF(inputRdf, + // true)); + } + + System.out.println("Average time to parse a dataset containing one subject with " + + tripleCount + " different triples:"); + long startLax = System.currentTimeMillis(); + for (int i = 0; i < rounds; i++) { + new JsonLdApi(options).fromRDF(inputRdf, true); + // JsonLdProcessor.expand(new JsonLdApi(options).fromRDF(inputRdf, + // true)); + } + System.out.println("\t- Assuming no duplicates: " + + (((System.currentTimeMillis() - startLax)) / rounds)); + + long start = System.currentTimeMillis(); + for (int i = 0; i < rounds; i++) { + new JsonLdApi(options).fromRDF(inputRdf); + // JsonLdProcessor.expand(new JsonLdApi(options).fromRDF(inputRdf)); + } + System.out.println( + "\t- Assuming duplicates: " + (((System.currentTimeMillis() - start)) / rounds)); + } + + /** + * @author fpservant + */ + @Test + public final void duplicatedTriplesInAnRDFDataset() throws Exception { + RDFDataset inputRdf = new RDFDataset(); + String ns = "http://www.example.com/foo/"; + inputRdf.setNamespace("ex", ns); + inputRdf.addTriple(ns + "s", ns + "p", ns + "o"); + inputRdf.addTriple(ns + "s", ns + "p", ns + "o"); + + System.out.println("Twice the same triple in RDFDataset:/n"); + for (Quad quad : inputRdf.getQuads("@default")) { + System.out.println(quad); + } + + final JsonLdOptions options = new JsonLdOptions(); + options.useNamespaces = true; + + Object fromRDF; + String jsonld; + + System.out.println("\nJSON-LD output is OK:\n"); + fromRDF = JsonLdProcessor.compact(new JsonLdApi(options).fromRDF(inputRdf), + inputRdf.getContext(), options); + + jsonld = JsonUtils.toPrettyString(fromRDF); + System.out.println(jsonld); + + System.out.println( + "\nWouldn't be the case assuming there is no duplicated triple in RDFDataset:\n"); + fromRDF = JsonLdProcessor.compact(new JsonLdApi(options).fromRDF(inputRdf, true), + inputRdf.getContext(), options); + jsonld = JsonUtils.toPrettyString(fromRDF); + System.out.println(jsonld); + + } } From 462d4aaceaeab197ad7eac7ff2ddf2f467fccc17 Mon Sep 17 00:00:00 2001 From: Peter Ansell Date: Wed, 18 May 2016 00:54:00 -0400 Subject: [PATCH 11/11] Add some more tests of the lax versus full merge situation --- .../core/JsonLdPerformanceTest.java | 79 ++++++++++++++++++- 1 file changed, 76 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java b/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java index 7fa4ec20..7fdd527a 100644 --- a/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java +++ b/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java @@ -114,7 +114,7 @@ private void testCompaction(String label, InputStream nextInputStream) System.out.println("(" + label + ") Compact average : " + compactStats.getAverage()); } - // @Ignore("Disable performance tests by default") + @Ignore("Disable performance tests by default") @Test public final void testPerformanceRandom() throws Exception { Random prng = new Random(); @@ -349,6 +349,7 @@ public final void testPerformanceRandom() throws Exception { * * @author fpservant */ + @Ignore("Disable performance tests by default") @Test public final void slowVsFast5Predicates() throws Exception { @@ -383,6 +384,7 @@ public String apply(Integer index) { * * @author fpservant */ + @Ignore("Disable performance tests by default") @Test public final void slowVsFast2Predicates() throws Exception { @@ -417,6 +419,7 @@ public String apply(Integer index) { * * @author fpservant */ + @Ignore("Disable performance tests by default") @Test public final void slowVsFast1Predicate() throws Exception { @@ -446,6 +449,76 @@ public String apply(Integer index) { } + /** + * many triples with same subject and prop: current implementation is slow + * + * @author fpservant + */ + @Ignore("Disable performance tests by default") + @Test + public final void slowVsFastMultipleSubjects1Predicate() throws Exception { + + final String ns = "http://www.example.com/foo/"; + + Function subjectGenerator = new Function() { + public String apply(Integer index) { + return ns + "s" + Integer.toString(index % 100); + } + }; + Function predicateGenerator = new Function() { + public String apply(Integer index) { + return ns + "p"; + } + }; + Function objectGenerator = new Function() { + public String apply(Integer index) { + return ns + "o" + Integer.toString(index); + } + }; + int tripleCount = 2000; + int warmingRounds = 200; + int rounds = 1000; + + runLaxVersusSlowToRDFTest("100 subjects and 1 predicate", ns, subjectGenerator, predicateGenerator, + objectGenerator, tripleCount, warmingRounds, rounds); + + } + + /** + * many triples with same subject and prop: current implementation is slow + * + * @author fpservant + */ + @Ignore("Disable performance tests by default") + @Test + public final void slowVsFastMultipleSubjects5Predicates() throws Exception { + + final String ns = "http://www.example.com/foo/"; + + Function subjectGenerator = new Function() { + public String apply(Integer index) { + return ns + "s" + Integer.toString(index % 1000); + } + }; + Function predicateGenerator = new Function() { + public String apply(Integer index) { + return ns + "p" + Integer.toString(index % 5); + } + }; + Function objectGenerator = new Function() { + public String apply(Integer index) { + return ns + "o" + Integer.toString(index); + } + }; + int tripleCount = 2000; + int warmingRounds = 200; + int rounds = 1000; + + runLaxVersusSlowToRDFTest("1000 subjects and 5 predicates", ns, subjectGenerator, predicateGenerator, + objectGenerator, tripleCount, warmingRounds, rounds); + + } + /** * Run a test on lax versus slow methods for toRDF. * @@ -466,7 +539,7 @@ public String apply(Integer index) { * @throws JsonLdError * If there is an error with the JSONLD processing. */ - public void runLaxVersusSlowToRDFTest(final String label, final String ns, + private void runLaxVersusSlowToRDFTest(final String label, final String ns, Function subjectGenerator, Function predicateGenerator, Function objectGenerator, int tripleCount, int warmingRounds, int rounds) throws JsonLdError { @@ -496,7 +569,7 @@ public void runLaxVersusSlowToRDFTest(final String label, final String ns, // true)); } - System.out.println("Average time to parse a dataset containing one subject with " + System.out.println("Average time to parse a dataset containing " + tripleCount + " different triples:"); long startLax = System.currentTimeMillis(); for (int i = 0; i < rounds; i++) {