diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/RioConfig.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/RioConfig.java index da2ce98544..cfeb053ede 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/RioConfig.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/RioConfig.java @@ -11,6 +11,10 @@ package org.eclipse.rdf4j.rio; import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -152,4 +156,8 @@ public RioConfig useDefaults() { systemPropertyCache.clear(); return this; } + + public Map, Object> getSettings() { + return Collections.unmodifiableMap(new HashMap<>(settings)); + } } diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/JSONLDSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/JSONLDSettings.java index 70b3763255..637451953e 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/JSONLDSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/JSONLDSettings.java @@ -35,7 +35,6 @@ public class JSONLDSettings { * Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.jsonld.compact_arrays}. * * @see JSONLD Data Structures - * */ public static final BooleanRioSetting COMPACT_ARRAYS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.jsonld.compact_arrays", "Compact arrays", Boolean.TRUE); @@ -43,7 +42,6 @@ public class JSONLDSettings { /** * If specified, it is used to retrieve remote documents and contexts; otherwise the processor's built-in loader is * used. - * */ public static final RioSetting DOCUMENT_LOADER = new ClassRioSetting<>( "org.eclipse.rdf4j.rio.jsonld.document_loader", "Document loader", null); @@ -66,7 +64,6 @@ public class JSONLDSettings { /** * The JSON-LD processor will throw an exception if a warning is encountered during processing. - * */ public static final BooleanRioSetting EXCEPTION_ON_WARNING = new BooleanRioSetting( "org.eclipse.rdf4j.rio.jsonld.exception_on_warning", @@ -83,7 +80,6 @@ public class JSONLDSettings { * Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.jsonld.optimize}. * * @see JSONLD Data Structures - * */ public static final BooleanRioSetting OPTIMIZE = new BooleanRioSetting("org.eclipse.rdf4j.rio.jsonld.optimize", "Optimize output", Boolean.FALSE); @@ -99,7 +95,6 @@ public class JSONLDSettings { * Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.jsonld.produce_generalized_rdf}. * * @see JSONLD Data Structures - * */ public static final BooleanRioSetting PRODUCE_GENERALIZED_RDF = new BooleanRioSetting( "org.eclipse.rdf4j.rio.jsonld.produce_generalized_rdf", "Produce generalized RDF", Boolean.FALSE); @@ -114,7 +109,6 @@ public class JSONLDSettings { * Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.jsonld.use_native_types}. * * @see JSONLD Data Structures - * */ public static final BooleanRioSetting USE_NATIVE_TYPES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.jsonld.use_native_types", "Use Native JSON Types", Boolean.FALSE); @@ -128,7 +122,6 @@ public class JSONLDSettings { * Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.jsonld.use_rdf_type}. * * @see JSONLD Data Structures - * */ public static final BooleanRioSetting USE_RDF_TYPE = new BooleanRioSetting( "org.eclipse.rdf4j.rio.jsonld.use_rdf_type", "Use RDF Type", Boolean.FALSE); @@ -139,7 +132,6 @@ public class JSONLDSettings { * Defaults to {@link JSONLDMode#EXPAND} to provide maximum RDF compatibility. * * @see JSONLD Features - * */ public static final RioSetting JSONLD_MODE = new RioSettingImpl<>( "org.eclipse.rdf4j.rio.jsonld_mode", "JSONLD Mode", JSONLDMode.EXPAND); @@ -150,7 +142,6 @@ public class JSONLDSettings { * Default to false *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.jsonld.hierarchical_view}. - * */ public static final BooleanRioSetting HIERARCHICAL_VIEW = new BooleanRioSetting( "org.eclipse.rdf4j.rio.jsonld.hierarchical_view", "Hierarchical representation of the JSON", Boolean.FALSE); @@ -161,38 +152,88 @@ public class JSONLDSettings { * array of the desired values. *

* Default: - * {@code Set.of("http://www.w3.org/ns/anno.jsonld", "http://www.w3.org/ns/activitystreams.jsonld", "http://www.w3.org/ns/ldp.jsonld", "http://www.w3.org/ns/oa.jsonld", "http://www.w3.org/ns/hydra/context.jsonld", "http://schema.org/", "https://w3id.org/security/v1", "https://w3c.github.io/json-ld-rc/context.jsonld", "https://www.w3.org/2018/credentials/v1", "https://health-lifesci.schema.org/", "https://auto.schema.org/", "https://bib.schema.org/", "http://xmlns.com/foaf/spec/index.jsonld", "https://pending.schema.org/", "https://schema.org/", "https://schema.org/docs/jsonldcontext.jsonld", "https://schema.org/version/latest/schemaorg-current-https.jsonld", "https://schema.org/version/latest/schemaorg-all-http.jsonld", "https://schema.org/version/latest/schemaorg-all-https.jsonld", "https://schema.org/version/latest/schemaorg-current-http.jsonld", "https://schema.org/version/latest/schemaorg-all.jsonld", "https://schema.org/version/latest/schemaorg-current.jsonld", "https://project-open-data.cio.gov/v1.1/schema/catalog.jsonld", "https://geojson.org/geojson-ld/geojson-context.jsonld", "https://www.w3.org/2019/wot/td/v1"); - * + * {@code Set.of("http://www.w3.org/ns/anno.jsonld", "https://www.w3.org/ns/anno.jsonld", "http://www.w3.org/ns/anno", "https://www.w3.org/ns/anno", "http://www.w3.org/ns/activitystreams.jsonld", "https://www.w3.org/ns/activitystreams.jsonld", "http://www.w3.org/ns/activitystreams", "https://www.w3.org/ns/activitystreams", "http://www.w3.org/ns/ldp.jsonld", "https://www.w3.org/ns/ldp.jsonld", "http://www.w3.org/ns/ldp", "https://www.w3.org/ns/ldp", "http://www.w3.org/ns/oa.jsonld", "https://www.w3.org/ns/oa.jsonld", "http://www.w3.org/ns/oa", "https://www.w3.org/ns/oa", "http://www.w3.org/ns/hydra/context.jsonld", "https://www.w3.org/ns/hydra/context.jsonld", "http://www.w3.org/ns/hydra/context", "https://www.w3.org/ns/hydra/context", "http://www.w3.org/2018/credentials/v1.jsonld", "https://www.w3.org/2018/credentials/v1.jsonld", "http://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/v1", "http://www.w3.org/2019/wot/td/v1.jsonld", "https://www.w3.org/2019/wot/td/v1.jsonld", "http://www.w3.org/2019/wot/td/v1", "https://www.w3.org/2019/wot/td/v1", "http://w3c.github.io/json-ld-rc/context.jsonld", "https://w3c.github.io/json-ld-rc/context.jsonld", "http://schema.org/", "https://schema.org/", "http://health-lifesci.schema.org/", "https://health-lifesci.schema.org/", "http://auto.schema.org/", "https://auto.schema.org/", "http://bib.schema.org/", "https://bib.schema.org/", "http://pending.schema.org/", "https://pending.schema.org/", "http://schema.org", "https://schema.org", "http://health-lifesci.schema.org", "https://health-lifesci.schema.org", "http://auto.schema.org", "https://auto.schema.org", "http://bib.schema.org", "https://bib.schema.org", "http://pending.schema.org", "https://pending.schema.org", "http://schema.org/docs/jsonldcontext.jsonld", "https://schema.org/docs/jsonldcontext.jsonld", "http://schema.org/version/latest/schemaorg-current-https.jsonld", "https://schema.org/version/latest/schemaorg-current-https.jsonld", "http://schema.org/version/latest/schemaorg-all-http.jsonld", "https://schema.org/version/latest/schemaorg-all-http.jsonld", "http://schema.org/version/latest/schemaorg-all-https.jsonld", "https://schema.org/version/latest/schemaorg-all-https.jsonld", "http://schema.org/version/latest/schemaorg-current-http.jsonld", "https://schema.org/version/latest/schemaorg-current-http.jsonld", "http://schema.org/version/latest/schemaorg-all.jsonld", "https://schema.org/version/latest/schemaorg-all.jsonld", "http://schema.org/version/latest/schemaorg-current.jsonld", "https://schema.org/version/latest/schemaorg-current.jsonld", "http://project-open-data.cio.gov/v1.1/schema/catalog.jsonld", "https://project-open-data.cio.gov/v1.1/schema/catalog.jsonld", "http://geojson.org/geojson-ld/geojson-context.jsonld", "https://geojson.org/geojson-ld/geojson-context.jsonld", "http://w3id.org/security/v1", "https://w3id.org/security/v1", "http://xmlns.com/foaf/spec/index.jsonld", "https://xmlns.com/foaf/spec/index.jsonld", "http://xmlns.com/foaf/spec/", "https://xmlns.com/foaf/spec/", "http://xmlns.com/foaf/spec", "https://xmlns.com/foaf/spec")} */ public static final SetRioSetting WHITELIST = new SetRioSetting<>( "org.eclipse.rdf4j.rio.jsonld_whitelist", "Whitelist of remote/local resources that the JSON-LD parser can retrieve. Set of URIs as strings.", Set.of( "http://www.w3.org/ns/anno.jsonld", + "https://www.w3.org/ns/anno.jsonld", + "http://www.w3.org/ns/anno", + "https://www.w3.org/ns/anno", "http://www.w3.org/ns/activitystreams.jsonld", + "https://www.w3.org/ns/activitystreams.jsonld", + "http://www.w3.org/ns/activitystreams", + "https://www.w3.org/ns/activitystreams", "http://www.w3.org/ns/ldp.jsonld", + "https://www.w3.org/ns/ldp.jsonld", + "http://www.w3.org/ns/ldp", + "https://www.w3.org/ns/ldp", "http://www.w3.org/ns/oa.jsonld", + "https://www.w3.org/ns/oa.jsonld", + "http://www.w3.org/ns/oa", + "https://www.w3.org/ns/oa", "http://www.w3.org/ns/hydra/context.jsonld", - "http://schema.org/", - "https://w3id.org/security/v1", - "https://w3c.github.io/json-ld-rc/context.jsonld", + "https://www.w3.org/ns/hydra/context.jsonld", + "http://www.w3.org/ns/hydra/context", + "https://www.w3.org/ns/hydra/context", + "http://www.w3.org/2018/credentials/v1.jsonld", + "https://www.w3.org/2018/credentials/v1.jsonld", + "http://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/v1", + "http://www.w3.org/2019/wot/td/v1.jsonld", + "https://www.w3.org/2019/wot/td/v1.jsonld", + "http://www.w3.org/2019/wot/td/v1", + "https://www.w3.org/2019/wot/td/v1", + "http://w3c.github.io/json-ld-rc/context.jsonld", + "https://w3c.github.io/json-ld-rc/context.jsonld", + "http://schema.org/", + "https://schema.org/", + "http://health-lifesci.schema.org/", "https://health-lifesci.schema.org/", + "http://auto.schema.org/", "https://auto.schema.org/", + "http://bib.schema.org/", "https://bib.schema.org/", - "http://xmlns.com/foaf/spec/index.jsonld", + "http://pending.schema.org/", "https://pending.schema.org/", - "https://schema.org/", + "http://schema.org", + "https://schema.org", + "http://health-lifesci.schema.org", + "https://health-lifesci.schema.org", + "http://auto.schema.org", + "https://auto.schema.org", + "http://bib.schema.org", + "https://bib.schema.org", + "http://pending.schema.org", + "https://pending.schema.org", + "http://schema.org/docs/jsonldcontext.jsonld", "https://schema.org/docs/jsonldcontext.jsonld", + "http://schema.org/version/latest/schemaorg-current-https.jsonld", "https://schema.org/version/latest/schemaorg-current-https.jsonld", + "http://schema.org/version/latest/schemaorg-all-http.jsonld", "https://schema.org/version/latest/schemaorg-all-http.jsonld", + "http://schema.org/version/latest/schemaorg-all-https.jsonld", "https://schema.org/version/latest/schemaorg-all-https.jsonld", + "http://schema.org/version/latest/schemaorg-current-http.jsonld", "https://schema.org/version/latest/schemaorg-current-http.jsonld", + "http://schema.org/version/latest/schemaorg-all.jsonld", "https://schema.org/version/latest/schemaorg-all.jsonld", + "http://schema.org/version/latest/schemaorg-current.jsonld", "https://schema.org/version/latest/schemaorg-current.jsonld", + "http://project-open-data.cio.gov/v1.1/schema/catalog.jsonld", "https://project-open-data.cio.gov/v1.1/schema/catalog.jsonld", + "http://geojson.org/geojson-ld/geojson-context.jsonld", "https://geojson.org/geojson-ld/geojson-context.jsonld", - "https://www.w3.org/2019/wot/td/v1" + "http://w3id.org/security/v1", + "https://w3id.org/security/v1", + "http://xmlns.com/foaf/spec/index.jsonld", + "https://xmlns.com/foaf/spec/index.jsonld", + "http://xmlns.com/foaf/spec/", + "https://xmlns.com/foaf/spec/", + "http://xmlns.com/foaf/spec", + "https://xmlns.com/foaf/spec" )); /** diff --git a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/CachingDocumentLoader.java b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/CachingDocumentLoader.java index c4363a1f4d..aea446626c 100644 --- a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/CachingDocumentLoader.java +++ b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/CachingDocumentLoader.java @@ -37,7 +37,7 @@ public class CachingDocumentLoader implements DocumentLoader { private static final LoadingCache cache = CacheBuilder.newBuilder() .maximumSize(1000) // Maximum 1000 documents in cache .expireAfterWrite(1, TimeUnit.HOURS) // Expire after 1 hour - .concurrencyLevel(8) // Optimize for 8 concurrent threads + .concurrencyLevel(Runtime.getRuntime().availableProcessors()) .build(new CacheLoader<>() { @Override public Document load(URI url) throws Exception { diff --git a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDMode.java b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDMode.java new file mode 100644 index 0000000000..819ecf284d --- /dev/null +++ b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDMode.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.rio.jsonld; + +/** + * Specifies constants to identify various modes that are relevant to JSONLD documents. + * + * @author Peter Ansell + * @see JSONLD Features + * + */ +public enum JSONLDMode { + + EXPAND("Expansion", "http://json-ld.org/spec/latest/json-ld-api/index.html#expansion"), + + COMPACT("Compaction", "http://json-ld.org/spec/latest/json-ld-api/index.html#compaction"), + + FLATTEN("Flattening", "http://json-ld.org/spec/latest/json-ld-api/index.html#flattening"), + + FRAME("Framing", "https://www.w3.org/TR/json-ld11-framing/"), + + ; + + private final String label; + + private final String reference; + + JSONLDMode(String label, String reference) { + this.label = label; + this.reference = reference; + } + + /** + * @return Returns the label. + */ + public String getLabel() { + return label; + } + + /** + * @return Returns the reference URL for the given mode. + */ + public String getReference() { + return reference; + } +} diff --git a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParser.java b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParser.java index 69c09c9290..646b47958d 100644 --- a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParser.java +++ b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParser.java @@ -10,16 +10,13 @@ *******************************************************************************/ package org.eclipse.rdf4j.rio.jsonld; -import static org.eclipse.rdf4j.rio.helpers.JSONLDSettings.DOCUMENT_LOADER_CACHE; -import static org.eclipse.rdf4j.rio.helpers.JSONLDSettings.SECURE_MODE; -import static org.eclipse.rdf4j.rio.helpers.JSONLDSettings.WHITELIST; - import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.net.URI; import java.net.URISyntaxException; import java.util.Collection; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.BiConsumer; @@ -35,10 +32,11 @@ import org.eclipse.rdf4j.rio.RDFHandlerException; import org.eclipse.rdf4j.rio.RDFParseException; import org.eclipse.rdf4j.rio.RDFParser; +import org.eclipse.rdf4j.rio.RioConfig; import org.eclipse.rdf4j.rio.RioSetting; import org.eclipse.rdf4j.rio.helpers.AbstractRDFParser; import org.eclipse.rdf4j.rio.helpers.BasicParserSettings; -import org.eclipse.rdf4j.rio.helpers.JSONLDSettings; +import org.eclipse.rdf4j.rio.helpers.BasicWriterSettings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -91,6 +89,13 @@ public Collection> getSupportedSettings() { result.add(JSONLDSettings.EXPAND_CONTEXT); result.add(JSONLDSettings.EXCEPTION_ON_WARNING); + result.add(JSONLDSettings.SECURE_MODE); + result.add(JSONLDSettings.WHITELIST); + result.add(JSONLDSettings.DOCUMENT_LOADER); + result.add(JSONLDSettings.DOCUMENT_LOADER_CACHE); + result.add(org.eclipse.rdf4j.rio.helpers.JSONLDSettings.SECURE_MODE); + result.add(org.eclipse.rdf4j.rio.helpers.JSONLDSettings.WHITELIST); + result.add(org.eclipse.rdf4j.rio.helpers.JSONLDSettings.DOCUMENT_LOADER_CACHE); return result; } @@ -133,9 +138,9 @@ private void parse(InputStream in, Reader reader, String baseURI) BasicParserSettings.FAIL_ON_UNKNOWN_LANGUAGES); } - boolean secureMode = getParserConfig().get(SECURE_MODE); - Set whitelist = getParserConfig().get(WHITELIST); - boolean documentLoaderCache = getParserConfig().get(DOCUMENT_LOADER_CACHE); + boolean secureMode = getParserConfig().get(JSONLDSettings.SECURE_MODE); + Set whitelist = getParserConfig().get(JSONLDSettings.WHITELIST); + boolean documentLoaderCache = getParserConfig().get(JSONLDSettings.DOCUMENT_LOADER_CACHE); JsonLdOptions opts = new JsonLdOptions(); opts.setUriValidation(false); @@ -144,8 +149,13 @@ private void parse(InputStream in, Reader reader, String baseURI) Document context = getParserConfig().get(JSONLDSettings.EXPAND_CONTEXT); DocumentLoader defaultDocumentLoader = opts.getDocumentLoader(); - CachingDocumentLoader cachingDocumentLoader = new CachingDocumentLoader(secureMode, whitelist, - documentLoaderCache); + + DocumentLoader documentLoader; + if (getParserConfig().get(JSONLDSettings.DOCUMENT_LOADER) == null) { + documentLoader = new CachingDocumentLoader(secureMode, whitelist, documentLoaderCache); + } else { + documentLoader = getParserConfig().get(JSONLDSettings.DOCUMENT_LOADER); + } if (context != null) { @@ -162,14 +172,14 @@ private void parse(InputStream in, Reader reader, String baseURI) return context; } - return cachingDocumentLoader.loadDocument(uri, options); + return documentLoader.loadDocument(uri, options); }); } } - if (secureMode && opts.getDocumentLoader() == defaultDocumentLoader) { - opts.setDocumentLoader(cachingDocumentLoader); + if (opts.getDocumentLoader() == defaultDocumentLoader) { + opts.setDocumentLoader(documentLoader); } if (baseURI != null && !baseURI.isEmpty()) { diff --git a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDSettings.java b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDSettings.java new file mode 100644 index 0000000000..d777415895 --- /dev/null +++ b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDSettings.java @@ -0,0 +1,204 @@ +/******************************************************************************* + * Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.rio.jsonld; + +import org.eclipse.rdf4j.rio.RioSetting; +import org.eclipse.rdf4j.rio.helpers.BooleanRioSetting; +import org.eclipse.rdf4j.rio.helpers.ClassRioSetting; +import org.eclipse.rdf4j.rio.helpers.RioSettingImpl; +import org.eclipse.rdf4j.rio.helpers.SetRioSetting; + +import no.hasmac.jsonld.document.Document; +import no.hasmac.jsonld.loader.DocumentLoader; + +/** + * Settings that can be passed to JSONLD Parsers and Writers. + * + * @author Peter Ansell + * @see JSONLD Data Structures + */ +public class JSONLDSettings { + + /** + * If set to true, the JSON-LD processor replaces arrays with just one element with that element during compaction. + * If set to false, all arrays will remain arrays even if they have just one element. + *

+ * Defaults to true. + *

+ * Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.jsonld.compact_arrays}. + * + * @see JSONLD Data Structures + * + */ + public static final BooleanRioSetting COMPACT_ARRAYS = new BooleanRioSetting( + "org.eclipse.rdf4j.rio.jsonld.compact_arrays", "Compact arrays", Boolean.TRUE); + + /** + * If specified, it is used to retrieve remote documents and contexts; otherwise the processor's built-in loader is + * used. + * + */ + public static final RioSetting DOCUMENT_LOADER = new ClassRioSetting<>( + "org.eclipse.rdf4j.rio.jsonld.document_loader", "Document loader", null); + + /** + * The JSON-LD context to use when expanding JSON-LD + * + * @see https://www.w3.org/TR/json-ld11-api/#dom-jsonldoptions-expandcontext. + */ + public static final RioSetting EXPAND_CONTEXT = new ClassRioSetting<>( + "org.eclipse.rdf4j.rio.jsonld.expand_context", + "A no.hasmac.jsonld.document.Document that contains the expanded context as specified in https://www.w3.org/TR/json-ld11-api/#dom-jsonldoptions-expandcontext", + null); + + public static final RioSetting FRAME = new ClassRioSetting<>( + "org.eclipse.rdf4j.rio.jsonld.frame_document", + "A no.hasmac.jsonld.document.Document that contains the frame used for framing as specified in https://www.w3.org/TR/json-ld11-framing/", + null);; + + /** + * The JSON-LD processor will throw an exception if a warning is encountered during processing. + * + */ + public static final BooleanRioSetting EXCEPTION_ON_WARNING = new BooleanRioSetting( + "org.eclipse.rdf4j.rio.jsonld.exception_on_warning", + "Throw an exception when logging a warning.", + Boolean.FALSE); + + /** + * If set to true, the JSON-LD processor is allowed to optimize the output of the + * Compaction algorithm to produce + * even compacter representations. + *

+ * Defaults to false. + *

+ * Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.jsonld.optimize}. + * + * @see JSONLD Data Structures + * + */ + public static final BooleanRioSetting OPTIMIZE = new BooleanRioSetting("org.eclipse.rdf4j.rio.jsonld.optimize", + "Optimize output", Boolean.FALSE); + + /** + * If set to true, the JSON-LD processor may emit blank nodes for triple predicates, otherwise they will be omitted. + *

+ * Note: the use of blank node identifiers to label properties is obsolete, and may be removed in a future version + * of JSON-LD, + *

+ * Defaults to false. + *

+ * Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.jsonld.produce_generalized_rdf}. + * + * @see JSONLD Data Structures + * + */ + public static final BooleanRioSetting PRODUCE_GENERALIZED_RDF = new BooleanRioSetting( + "org.eclipse.rdf4j.rio.jsonld.produce_generalized_rdf", "Produce generalized RDF", Boolean.FALSE); + + /** + * If set to true, the JSON-LD processor will try to convert typed values to JSON native types instead of using the + * expanded object form when converting from RDF. xsd:boolean values will be converted to true or false. xsd:integer + * and xsd:double values will be converted to JSON numbers. + *

+ * Defaults to false for RDF compatibility. + *

+ * Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.jsonld.use_native_types}. + * + * @see JSONLD Data Structures + * + */ + public static final BooleanRioSetting USE_NATIVE_TYPES = new BooleanRioSetting( + "org.eclipse.rdf4j.rio.jsonld.use_native_types", "Use Native JSON Types", Boolean.FALSE); + + /** + * If set to true, the JSON-LD processor will use the expanded rdf:type IRI as the property instead of @type when + * converting from RDF. + *

+ * Defaults to false. + *

+ * Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.jsonld.use_rdf_type}. + * + * @see JSONLD Data Structures + * + */ + public static final BooleanRioSetting USE_RDF_TYPE = new BooleanRioSetting( + "org.eclipse.rdf4j.rio.jsonld.use_rdf_type", "Use RDF Type", Boolean.FALSE); + + /** + * The {@link JSONLDMode} that the writer will use to reorganise the JSONLD document after it is created. + *

+ * Defaults to {@link JSONLDMode#EXPAND} to provide maximum RDF compatibility. + * + * @see JSONLD Features + * + */ + public static final RioSetting JSONLD_MODE = new RioSettingImpl<>( + "org.eclipse.rdf4j.rio.jsonld_mode", "JSONLD Mode", JSONLDMode.EXPAND); + + /** + * If set to true, the JSON-LD processor will try to represent the JSON-LD object in a hierarchical view. + *

+ * Default to false + *

+ * Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.jsonld.hierarchical_view}. + * + */ + public static final BooleanRioSetting HIERARCHICAL_VIEW = new BooleanRioSetting( + "org.eclipse.rdf4j.rio.jsonld.hierarchical_view", "Hierarchical representation of the JSON", Boolean.FALSE); + + /** + * Whitelist of remote/local resources that the JSON-LD parser can retrieve. Set of URIs as strings. This can be + * overridden by setting a system property with the key {@code org.eclipse.rdf4j.rio.jsonld_whitelist} and a JSON + * array of the desired values. + *

+ * Default: + * {@code Set.of("http://www.w3.org/ns/anno.jsonld", "https://www.w3.org/ns/anno.jsonld", "http://www.w3.org/ns/anno", "https://www.w3.org/ns/anno", "http://www.w3.org/ns/activitystreams.jsonld", "https://www.w3.org/ns/activitystreams.jsonld", "http://www.w3.org/ns/activitystreams", "https://www.w3.org/ns/activitystreams", "http://www.w3.org/ns/ldp.jsonld", "https://www.w3.org/ns/ldp.jsonld", "http://www.w3.org/ns/ldp", "https://www.w3.org/ns/ldp", "http://www.w3.org/ns/oa.jsonld", "https://www.w3.org/ns/oa.jsonld", "http://www.w3.org/ns/oa", "https://www.w3.org/ns/oa", "http://www.w3.org/ns/hydra/context.jsonld", "https://www.w3.org/ns/hydra/context.jsonld", "http://www.w3.org/ns/hydra/context", "https://www.w3.org/ns/hydra/context", "http://www.w3.org/2018/credentials/v1.jsonld", "https://www.w3.org/2018/credentials/v1.jsonld", "http://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/v1", "http://www.w3.org/2019/wot/td/v1.jsonld", "https://www.w3.org/2019/wot/td/v1.jsonld", "http://www.w3.org/2019/wot/td/v1", "https://www.w3.org/2019/wot/td/v1", "http://w3c.github.io/json-ld-rc/context.jsonld", "https://w3c.github.io/json-ld-rc/context.jsonld", "http://schema.org/", "https://schema.org/", "http://health-lifesci.schema.org/", "https://health-lifesci.schema.org/", "http://auto.schema.org/", "https://auto.schema.org/", "http://bib.schema.org/", "https://bib.schema.org/", "http://pending.schema.org/", "https://pending.schema.org/", "http://schema.org/docs/jsonldcontext.jsonld", "https://schema.org/docs/jsonldcontext.jsonld", "http://schema.org/version/latest/schemaorg-current-https.jsonld", "https://schema.org/version/latest/schemaorg-current-https.jsonld", "http://schema.org/version/latest/schemaorg-all-http.jsonld", "https://schema.org/version/latest/schemaorg-all-http.jsonld", "http://schema.org/version/latest/schemaorg-all-https.jsonld", "https://schema.org/version/latest/schemaorg-all-https.jsonld", "http://schema.org/version/latest/schemaorg-current-http.jsonld", "https://schema.org/version/latest/schemaorg-current-http.jsonld", "http://schema.org/version/latest/schemaorg-all.jsonld", "https://schema.org/version/latest/schemaorg-all.jsonld", "http://schema.org/version/latest/schemaorg-current.jsonld", "https://schema.org/version/latest/schemaorg-current.jsonld", "http://project-open-data.cio.gov/v1.1/schema/catalog.jsonld", "https://project-open-data.cio.gov/v1.1/schema/catalog.jsonld", "http://geojson.org/geojson-ld/geojson-context.jsonld", "https://geojson.org/geojson-ld/geojson-context.jsonld", "http://w3id.org/security/v1", "https://w3id.org/security/v1", "http://xmlns.com/foaf/spec/index.jsonld", "https://xmlns.com/foaf/spec/index.jsonld", "http://xmlns.com/foaf/spec/", "https://xmlns.com/foaf/spec/")} + * + */ + public static final SetRioSetting WHITELIST = new SetRioSetting<>( + "org.eclipse.rdf4j.rio.jsonld_whitelist", + "Whitelist of remote/local resources that the JSON-LD parser can retrieve. Set of URIs as strings.", + org.eclipse.rdf4j.rio.helpers.JSONLDSettings.WHITELIST.getDefaultValue() + ); + + /** + * Secure mode only allows loading remote/local resources (ex. context from url) that are whitelisted. This can be + * overridden by setting a system property with the key {@code org.eclipse.rdf4j.rio.jsonld_secure_mode} and a + * boolean value. + *

+ * Default: true + */ + public static final BooleanRioSetting SECURE_MODE = new BooleanRioSetting( + "org.eclipse.rdf4j.rio.jsonld_secure_mode", + "Secure mode only allows loading remote/local resources (ex. context from url) that are whitelisted.", + Boolean.TRUE); + + /** + * The document loader cache is enabled by default. All loaded documents, such as remote contexts, are cached for 1 + * hour, or until the cache is full. The cache holds up to 1000 documents. The cache is shared between all + * JSONLDParsers. The cache can be disabled by setting this value to false. + *

+ * Default: true + */ + public static final BooleanRioSetting DOCUMENT_LOADER_CACHE = new BooleanRioSetting( + "org.eclipse.rdf4j.rio.jsonld_document_loader_cache", + "The document loader cache is enabled by default. All loaded documents, such as remote contexts, are cached for 1 hour, or until the cache is full. The cache holds up to 1000 documents. The cache is shared between all JSONLDParsers. The cache can be disabled by setting this value to false.", + Boolean.TRUE); + + /** + * Private default constructor. + */ + private JSONLDSettings() { + } + +} diff --git a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriter.java b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriter.java index d55f611841..5633a9c6fc 100644 --- a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriter.java +++ b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriter.java @@ -42,8 +42,6 @@ import org.eclipse.rdf4j.rio.WriterConfig; import org.eclipse.rdf4j.rio.helpers.AbstractRDFWriter; import org.eclipse.rdf4j.rio.helpers.BasicWriterSettings; -import org.eclipse.rdf4j.rio.helpers.JSONLDMode; -import org.eclipse.rdf4j.rio.helpers.JSONLDSettings; import org.eclipse.rdf4j.rio.helpers.StatementCollector; import com.github.jsonldjava.core.JsonLdConsts; @@ -252,7 +250,7 @@ public int size() { .useRdfType(writerConfig.get(JSONLDSettings.USE_RDF_TYPE)) .build(); - JSONLDMode mode = getWriterConfig().get(JSONLDSettings.JSONLD_MODE); + JSONLDMode mode = mapJsonLdMode(getWriterConfig().get(JSONLDSettings.JSONLD_MODE)); switch (mode) { case EXPAND: @@ -298,6 +296,16 @@ public int size() { } } + private JSONLDMode mapJsonLdMode(Object jsonldMode) { + if (jsonldMode instanceof JSONLDMode) { + return (JSONLDMode) jsonldMode; + } + if (jsonldMode instanceof org.eclipse.rdf4j.rio.helpers.JSONLDMode) { + return JSONLDMode.valueOf(jsonldMode.toString()); + } + throw new IllegalArgumentException("Unknown JSONLDMode: " + jsonldMode); + } + private static RdfNQuad toRdfNQuad(Statement statement) { return new RdfNQuadAdapter(statement); } @@ -398,6 +406,15 @@ public Collection> getSupportedSettings() { result.add(JSONLDSettings.USE_NATIVE_TYPES); result.add(JSONLDSettings.PRODUCE_GENERALIZED_RDF); result.add(JSONLDSettings.EXCEPTION_ON_WARNING); + result.add(JSONLDSettings.FRAME); + + result.add(org.eclipse.rdf4j.rio.helpers.JSONLDSettings.COMPACT_ARRAYS); + result.add(org.eclipse.rdf4j.rio.helpers.JSONLDSettings.JSONLD_MODE); + result.add(org.eclipse.rdf4j.rio.helpers.JSONLDSettings.USE_RDF_TYPE); + result.add(org.eclipse.rdf4j.rio.helpers.JSONLDSettings.USE_NATIVE_TYPES); + result.add(org.eclipse.rdf4j.rio.helpers.JSONLDSettings.PRODUCE_GENERALIZED_RDF); + result.add(org.eclipse.rdf4j.rio.helpers.JSONLDSettings.EXCEPTION_ON_WARNING); + result.add(org.eclipse.rdf4j.rio.helpers.JSONLDSettings.FRAME); return result; } diff --git a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParserCustomOldTest.java b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParserCustomOldTest.java new file mode 100644 index 0000000000..6a2cdcd012 --- /dev/null +++ b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParserCustomOldTest.java @@ -0,0 +1,375 @@ +/******************************************************************************* + * Copyright (c) 2018 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.rio.jsonld; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.eclipse.rdf4j.rio.helpers.JSONLDSettings.SECURE_MODE; +import static org.eclipse.rdf4j.rio.helpers.JSONLDSettings.WHITELIST; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.io.StringReader; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.commons.io.FileUtils; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Literal; +import org.eclipse.rdf4j.model.Model; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.impl.LinkedHashModel; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.model.vocabulary.FOAF; +import org.eclipse.rdf4j.model.vocabulary.XSD; +import org.eclipse.rdf4j.rio.RDFFormat; +import org.eclipse.rdf4j.rio.RDFParseException; +import org.eclipse.rdf4j.rio.RDFParser; +import org.eclipse.rdf4j.rio.Rio; +import org.eclipse.rdf4j.rio.helpers.ContextStatementCollector; +import org.eclipse.rdf4j.rio.helpers.JSONLDSettings; +import org.eclipse.rdf4j.rio.helpers.ParseErrorCollector; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Test; + +import jakarta.json.spi.JsonProvider; +import no.hasmac.jsonld.document.Document; +import no.hasmac.jsonld.document.JsonDocument; + +/** + * Custom (non-manifest) tests for JSON-LD parser. + * + * Tests with the old JSONLDSettings class + * + * @author Peter Ansell + */ +public class JSONLDParserCustomOldTest { + + /** + * Backslash escaped "h" in "http" + */ + private static final String BACKSLASH_ESCAPED_TEST_STRING = "[{\"@id\": \"\\http://example.com/Subj1\",\"http://example.com/prop1\": [{\"@id\": \"http://example.com/Obj1\"}]}]"; + + /** + * Java/C++ style comments + */ + private static final String COMMENTS_TEST_STRING = "[{/*This is a non-standard java/c++ style comment\n*/\"@id\": \"http://example.com/Subj1\",\"http://example.com/prop1\": [{\"@id\": \"http://example.com/Obj1\"}]}]"; + + /** + * Tests for NaN + */ + private static final String NON_NUMERIC_NUMBERS_TEST_STRING = "[{\"@id\": \"http://example.com/Subj1\",\"http://example.com/prop1\": NaN}]"; + + /** + * Tests for numeric leading zeroes + */ + private static final String NUMERIC_LEADING_ZEROES_TEST_STRING = "[{\"@id\": \"http://example.com/Subj1\",\"http://example.com/prop1\": 000042}]"; + + /** + * Tests for single-quotes + */ + private static final String SINGLE_QUOTES_TEST_STRING = "[{\'@id\': \"http://example.com/Subj1\",\'http://example.com/prop1\': 42}]"; + + /** + * Tests for unquoted control char + */ + private static final String UNQUOTED_CONTROL_CHARS_TEST_STRING = "[{\"@id\": \"http://example.com/Subj1\",\"http://example.com/prop1\": \"42\u0009\"}]"; + + /** + * Tests for unquoted field names + */ + private static final String UNQUOTED_FIELD_NAMES_TEST_STRING = "[{@id: \"http://example.com/Subj1\",\"http://example.com/prop1\": 42}]"; + + /** + * YAML style comments + */ + private static final String YAML_COMMENTS_TEST_STRING = "[\n{#This is a non-standard yaml style comment/*\n\"@id\": \"http://example.com/Subj1\",\"http://example.com/prop1\": [{\"@id\": \"http://example.com/Obj1\"}]}]"; + + /** + * Trailing comma + */ + private static final String TRAILING_COMMA_TEST_STRING = "[{\"@id\": \"http://example.com/Subj1\",\"http://example.com/prop1\": [{\"@id\": \"http://example.com/Obj1\"},]}]"; + + /** + * Strict duplicate detection + */ + private static final String STRICT_DUPLICATE_DETECTION_TEST_STRING = "[{\"@context\": {}, \"@context\": {}, \"@id\": \"http://example.com/Subj1\",\"http://example.com/prop1\": [{\"@id\": \"http://example.com/Obj1\"}]}]"; + + /** + * Used for custom document loader + */ + private static final String LOADER_CONTEXT = "{ \"@context\": {\"prop\": \"http://example.com/prop1\"} }"; + private static final String LOADER_JSONLD = "{ \"@context\": \"http://example.com/context.jsonld\", \"@id\": \"http://example.com/Subj1\", \"prop\": \"Property\" }"; + + private RDFParser parser; + + private ParseErrorCollector errors; + + private Model model; + + private final SimpleValueFactory F = SimpleValueFactory.getInstance(); + + private final IRI testSubjectIRI = F.createIRI("http://example.com/Subj1"); + private final IRI testPredicate = F.createIRI("http://example.com/prop1"); + private final IRI testObjectIRI = F.createIRI("http://example.com/Obj1"); + + private final Literal testObjectLiteralNotANumber = F.createLiteral("NaN", XSD.DOUBLE); + private final Literal testObjectLiteralNumber = F.createLiteral("42", XSD.INTEGER); + private final Literal testObjectLiteralUnquotedControlChar = F.createLiteral("42\u0009", XSD.STRING); + + @BeforeEach + public void setUp() { + parser = Rio.createParser(RDFFormat.JSONLD); + errors = new ParseErrorCollector(); + model = new LinkedHashModel(); + parser.setParseErrorListener(errors); + parser.setRDFHandler(new ContextStatementCollector(model, F)); + } + + private void verifyParseResults(Resource nextSubject, IRI nextPredicate, Value nextObject) { + assertEquals(0, errors.getWarnings().size()); + assertEquals(0, errors.getErrors().size()); + assertEquals(0, errors.getFatalErrors().size()); + + assertEquals(1, model.size()); + assertTrue(model.contains(nextSubject, nextPredicate, nextObject), + "model was not as expected: " + model.toString()); + } + + @Test + public void testSupportedSettings() { + assertEquals(19, parser.getSupportedSettings().size()); + } + + @Test + public void testAllowBackslashEscapingAnyCharacterDefault() { + assertThatThrownBy(() -> parser.parse(new StringReader(BACKSLASH_ESCAPED_TEST_STRING), "")) + .isInstanceOf(RDFParseException.class) + .hasMessageContaining("Could not parse JSONLD"); + } + + @Test + public void testAllowBackslashEscapingAnyCharacterDisabled() { + assertThatThrownBy(() -> parser.parse(new StringReader(BACKSLASH_ESCAPED_TEST_STRING), "")) + .isInstanceOf(RDFParseException.class) + .hasMessageContaining("Could not parse JSONLD"); + + } + + @Test + public void testAllowCommentsDefault() { + assertThatThrownBy(() -> parser.parse(new StringReader(COMMENTS_TEST_STRING), "")) + .isInstanceOf(RDFParseException.class) + .hasMessageContaining("Could not parse JSONLD"); + } + + @Test + public void testAllowNonNumericNumbersDefault() { + assertThatThrownBy(() -> parser.parse(new StringReader(NON_NUMERIC_NUMBERS_TEST_STRING), "")) + .isInstanceOf(RDFParseException.class) + .hasMessageContaining("Could not parse JSONLD"); + } + + @Test + public void testAllowNumericLeadingZeroesDefault() { + assertThatThrownBy(() -> parser.parse(new StringReader(NUMERIC_LEADING_ZEROES_TEST_STRING), "")) + .isInstanceOf(RDFParseException.class) + .hasMessageContaining("Could not parse JSONLD"); + } + + @Test + public void testAllowSingleQuotesDefault() { + assertThatThrownBy(() -> parser.parse(new StringReader(SINGLE_QUOTES_TEST_STRING), "")) + .isInstanceOf(RDFParseException.class) + .hasMessageContaining("Could not parse JSONLD"); + } + + @Test + public void testAllowUnquotedControlCharactersDefault() { + assertThatThrownBy(() -> parser.parse(new StringReader(UNQUOTED_CONTROL_CHARS_TEST_STRING), "")) + .isInstanceOf(RDFParseException.class) + .hasMessageContaining("Could not parse JSONLD"); + } + + @Test + public void testAllowUnquotedFieldNamesDefault() { + assertThatThrownBy(() -> parser.parse(new StringReader(UNQUOTED_FIELD_NAMES_TEST_STRING), "")) + .isInstanceOf(RDFParseException.class) + .hasMessageContaining("Could not parse JSONLD"); + } + + @Test + public void testAllowYamlCommentsDefault() { + assertThatThrownBy(() -> parser.parse(new StringReader(YAML_COMMENTS_TEST_STRING), "")) + .isInstanceOf(RDFParseException.class) + .hasMessageContaining("Could not parse JSONLD"); + } + + @Test + public void testAllowTrailingCommaDefault() { + assertThatThrownBy(() -> parser.parse(new StringReader(TRAILING_COMMA_TEST_STRING), "")) + .isInstanceOf(RDFParseException.class) + .hasMessageContaining("Could not parse JSONLD"); + } + + @Test + public void testStrictDuplicateDetectionDefault() throws Exception { + parser.parse(new StringReader(STRICT_DUPLICATE_DETECTION_TEST_STRING), ""); + verifyParseResults(testSubjectIRI, testPredicate, testObjectIRI); + } + + @Test + public void testContext() throws Exception { + + Document jsonDocument = JsonDocument.of(new StringReader(LOADER_CONTEXT)); + jsonDocument.setDocumentUrl(URI.create("http://example.com/context.jsonld")); + + parser.getParserConfig().set(JSONLDSettings.EXPAND_CONTEXT, jsonDocument); + parser.parse(new StringReader(LOADER_JSONLD), ""); + assertTrue(model.predicates().contains(testPredicate)); + } + + @Test + public void testLocalFileSecurity() throws Exception { + + String contextUri = JSONLDParserCustomOldTest.class.getClassLoader() + .getResource("testcases/jsonld/localFileContext/context.jsonld") + .toString(); + + String jsonld = FileUtils + .readFileToString(new File(JSONLDParserCustomOldTest.class.getClassLoader() + .getResource("testcases/jsonld/localFileContext/data.jsonld") + .getFile()), StandardCharsets.UTF_8) + .replace("file:./context.jsonld", contextUri); + + // expect exception + RDFParseException rdfParseException = Assertions.assertThrowsExactly(RDFParseException.class, () -> { + parser.parse(new StringReader(jsonld), ""); + }); + + Assertions.assertEquals("Could not load document from " + contextUri + + " because it is not whitelisted. See: JSONLDSettings.WHITELIST and JSONLDSettings.SECURE_MODE which can also be set as system properties.", + rdfParseException.getMessage()); + } + + @Test + public void testLocalFileSecurityWhiteList() throws Exception { + String jsonld = FileUtils.readFileToString(new File(JSONLDParserCustomOldTest.class.getClassLoader() + .getResource("testcases/jsonld/localFileContext/data.jsonld") + .getFile()), StandardCharsets.UTF_8); + String contextUri = JSONLDParserCustomOldTest.class.getClassLoader() + .getResource("testcases/jsonld/localFileContext/context.jsonld") + .toString(); + jsonld = jsonld.replace("file:./context.jsonld", contextUri); + + parser.getParserConfig().set(WHITELIST, Set.of(contextUri)); + + parser.parse(new StringReader(jsonld), ""); + assertTrue(model.objects().contains(FOAF.PERSON)); + } + + @Test + public void testLocalFileSecurityDisableSecurity() throws Exception { + String jsonld = FileUtils.readFileToString(new File(JSONLDParserCustomOldTest.class.getClassLoader() + .getResource("testcases/jsonld/localFileContext/data.jsonld") + .getFile()), StandardCharsets.UTF_8); + jsonld = jsonld.replace("file:./context.jsonld", + JSONLDParserCustomOldTest.class.getClassLoader() + .getResource("testcases/jsonld/localFileContext/context.jsonld") + .toString()); + + parser.getParserConfig().set(SECURE_MODE, false); + + parser.parse(new StringReader(jsonld), ""); + assertTrue(model.objects().contains(FOAF.PERSON)); + } + + @Test + public void testLocalFileSecurityDisableSecuritySystemProperty() throws Exception { + String jsonld = FileUtils.readFileToString(new File(JSONLDParserCustomOldTest.class.getClassLoader() + .getResource("testcases/jsonld/localFileContext/data.jsonld") + .getFile()), StandardCharsets.UTF_8); + jsonld = jsonld.replace("file:./context.jsonld", + JSONLDParserCustomOldTest.class.getClassLoader() + .getResource("testcases/jsonld/localFileContext/context.jsonld") + .toString()); + + try { + System.setProperty(SECURE_MODE.getKey(), "false"); + parser.parse(new StringReader(jsonld), ""); + assertTrue(model.objects().contains(FOAF.PERSON)); + } finally { + System.clearProperty(SECURE_MODE.getKey()); + } + + } + + @RepeatedTest(10) + public void testRemoteContext() throws Exception { + String jsonld = FileUtils.readFileToString(new File(JSONLDParserCustomOldTest.class.getClassLoader() + .getResource("testcases/jsonld/remoteContext/data.jsonld") + .getFile()), StandardCharsets.UTF_8); + + parser.getParserConfig().set(WHITELIST, Set.of("https://schema.org")); + parser.parse(new StringReader(jsonld), ""); + assertEquals(59, model.size()); + } + + @Test + public void testRemoteContextSystemProperty() throws Exception { + String jsonld = FileUtils.readFileToString(new File(JSONLDParserCustomOldTest.class.getClassLoader() + .getResource("testcases/jsonld/remoteContext/data.jsonld") + .getFile()), StandardCharsets.UTF_8); + + try { + System.setProperty(WHITELIST.getKey(), "[\"https://schema.org\"]"); + parser.parse(new StringReader(jsonld), ""); + assertEquals(59, model.size()); + } finally { + System.clearProperty(WHITELIST.getKey()); + } + + } + + @Test + public void testRemoteContextException() throws Exception { + String jsonld = FileUtils.readFileToString(new File(JSONLDParserCustomOldTest.class.getClassLoader() + .getResource("testcases/jsonld/remoteContextException/data.jsonld") + .getFile()), StandardCharsets.UTF_8); + + parser.getParserConfig().set(WHITELIST, Set.of("https://example.org/context.jsonld")); + RDFParseException rdfParseException = Assertions.assertThrowsExactly(RDFParseException.class, () -> { + parser.parse(new StringReader(jsonld), ""); + }); + + assertEquals("Could not load document from https://example.org/context.jsonld", rdfParseException.getMessage()); + } + + @Test + public void testSPI() { + ServiceLoader load = ServiceLoader.load(JsonProvider.class); + List collect = load.stream() + .map(ServiceLoader.Provider::get) + .map(t -> t.getClass().getName()) + .collect(Collectors.toList()); + assertFalse(collect.isEmpty()); + assertEquals("org.glassfish.json.JsonProviderImpl", collect.stream().findFirst().orElse("")); + } + +} diff --git a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParserCustomTest.java b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParserCustomTest.java index f0fb28e05f..0d36fbc0e4 100644 --- a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParserCustomTest.java +++ b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParserCustomTest.java @@ -11,8 +11,6 @@ package org.eclipse.rdf4j.rio.jsonld; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import static org.eclipse.rdf4j.rio.helpers.JSONLDSettings.SECURE_MODE; -import static org.eclipse.rdf4j.rio.helpers.JSONLDSettings.WHITELIST; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -24,6 +22,7 @@ import java.util.List; import java.util.ServiceLoader; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; @@ -41,7 +40,6 @@ import org.eclipse.rdf4j.rio.RDFParser; import org.eclipse.rdf4j.rio.Rio; import org.eclipse.rdf4j.rio.helpers.ContextStatementCollector; -import org.eclipse.rdf4j.rio.helpers.JSONLDSettings; import org.eclipse.rdf4j.rio.helpers.ParseErrorCollector; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -49,8 +47,12 @@ import org.junit.jupiter.api.Test; import jakarta.json.spi.JsonProvider; +import no.hasmac.jsonld.JsonLdError; import no.hasmac.jsonld.document.Document; import no.hasmac.jsonld.document.JsonDocument; +import no.hasmac.jsonld.loader.DocumentLoader; +import no.hasmac.jsonld.loader.DocumentLoaderOptions; +import no.hasmac.jsonld.loader.SchemeRouter; /** * Custom (non-manifest) tests for JSON-LD parser. @@ -152,7 +154,7 @@ private void verifyParseResults(Resource nextSubject, IRI nextPredicate, Value n @Test public void testSupportedSettings() { - assertEquals(15, parser.getSupportedSettings().size()); + assertEquals(19, parser.getSupportedSettings().size()); } @Test @@ -276,7 +278,7 @@ public void testLocalFileSecurityWhiteList() throws Exception { .toString(); jsonld = jsonld.replace("file:./context.jsonld", contextUri); - parser.getParserConfig().set(WHITELIST, Set.of(contextUri)); + parser.getParserConfig().set(JSONLDSettings.WHITELIST, Set.of(contextUri)); parser.parse(new StringReader(jsonld), ""); assertTrue(model.objects().contains(FOAF.PERSON)); @@ -292,12 +294,56 @@ public void testLocalFileSecurityDisableSecurity() throws Exception { .getResource("testcases/jsonld/localFileContext/context.jsonld") .toString()); - parser.getParserConfig().set(SECURE_MODE, false); + parser.getParserConfig().set(JSONLDSettings.SECURE_MODE, false); parser.parse(new StringReader(jsonld), ""); assertTrue(model.objects().contains(FOAF.PERSON)); } + @Test + public void testLocalFileSecurityCustomDocumentLoader() throws Exception { + String jsonld = FileUtils.readFileToString(new File(JSONLDParserCustomTest.class.getClassLoader() + .getResource("testcases/jsonld/localFileContext/data.jsonld") + .getFile()), StandardCharsets.UTF_8); + jsonld = jsonld.replace("file:./context.jsonld", + JSONLDParserCustomTest.class.getClassLoader() + .getResource("testcases/jsonld/localFileContext/context.jsonld") + .toString()); + + AtomicBoolean called = new AtomicBoolean(false); + parser.getParserConfig().set(JSONLDSettings.DOCUMENT_LOADER, (url, options) -> { + called.set(true); + return new CachingDocumentLoader(false, Set.of(), true).loadDocument(url, options); + }); + + parser.parse(new StringReader(jsonld), ""); + assertTrue(model.objects().contains(FOAF.PERSON)); + assertTrue(called.get()); + } + + @Test + public void testLocalFileSecurityCustomDocumentLoader2() throws Exception { + String jsonld = FileUtils.readFileToString(new File(JSONLDParserCustomTest.class.getClassLoader() + .getResource("testcases/jsonld/localFileContext/data.jsonld") + .getFile()), StandardCharsets.UTF_8); + jsonld = jsonld.replace("file:./context.jsonld", + JSONLDParserCustomTest.class.getClassLoader() + .getResource("testcases/jsonld/localFileContext/context.jsonld") + .toString()); + + AtomicBoolean called = new AtomicBoolean(false); + parser.getParserConfig().set(JSONLDSettings.DOCUMENT_LOADER, (url, options) -> { + called.set(true); + return new CachingDocumentLoader(false, Set.of(), true).loadDocument(url, options); + }); + + parser.getParserConfig().set(JSONLDSettings.SECURE_MODE, false); + + parser.parse(new StringReader(jsonld), ""); + assertTrue(model.objects().contains(FOAF.PERSON)); + assertTrue(called.get()); + } + @Test public void testLocalFileSecurityDisableSecuritySystemProperty() throws Exception { String jsonld = FileUtils.readFileToString(new File(JSONLDParserCustomTest.class.getClassLoader() @@ -309,22 +355,32 @@ public void testLocalFileSecurityDisableSecuritySystemProperty() throws Exceptio .toString()); try { - System.setProperty(SECURE_MODE.getKey(), "false"); + System.setProperty(JSONLDSettings.SECURE_MODE.getKey(), "false"); parser.parse(new StringReader(jsonld), ""); assertTrue(model.objects().contains(FOAF.PERSON)); } finally { - System.clearProperty(SECURE_MODE.getKey()); + System.clearProperty(JSONLDSettings.SECURE_MODE.getKey()); } } - @RepeatedTest(10) + @RepeatedTest(100) + public void testRemoteContextDefaultWhitelist() throws Exception { + String jsonld = FileUtils.readFileToString(new File(JSONLDParserCustomTest.class.getClassLoader() + .getResource("testcases/jsonld/remoteContext/data.jsonld") + .getFile()), StandardCharsets.UTF_8); + + parser.parse(new StringReader(jsonld), ""); + assertEquals(59, model.size()); + } + + @RepeatedTest(100) public void testRemoteContext() throws Exception { String jsonld = FileUtils.readFileToString(new File(JSONLDParserCustomTest.class.getClassLoader() .getResource("testcases/jsonld/remoteContext/data.jsonld") .getFile()), StandardCharsets.UTF_8); - parser.getParserConfig().set(WHITELIST, Set.of("https://schema.org")); + parser.getParserConfig().set(JSONLDSettings.WHITELIST, Set.of("https://schema.org")); parser.parse(new StringReader(jsonld), ""); assertEquals(59, model.size()); } @@ -336,11 +392,12 @@ public void testRemoteContextSystemProperty() throws Exception { .getFile()), StandardCharsets.UTF_8); try { - System.setProperty(WHITELIST.getKey(), "[\"https://schema.org\"]"); + System.setProperty(JSONLDSettings.WHITELIST.getKey(), + "[\"https://schema.org\",\"https://example.org/context.jsonld\"]"); parser.parse(new StringReader(jsonld), ""); assertEquals(59, model.size()); } finally { - System.clearProperty(WHITELIST.getKey()); + System.clearProperty(JSONLDSettings.WHITELIST.getKey()); } } @@ -351,7 +408,7 @@ public void testRemoteContextException() throws Exception { .getResource("testcases/jsonld/remoteContextException/data.jsonld") .getFile()), StandardCharsets.UTF_8); - parser.getParserConfig().set(WHITELIST, Set.of("https://example.org/context.jsonld")); + parser.getParserConfig().set(JSONLDSettings.WHITELIST, Set.of("https://example.org/context.jsonld")); RDFParseException rdfParseException = Assertions.assertThrowsExactly(RDFParseException.class, () -> { parser.parse(new StringReader(jsonld), ""); }); diff --git a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriterBackgroundTest.java b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriterBackgroundTest.java index 3caeafc648..daa70f68ae 100644 --- a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriterBackgroundTest.java +++ b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriterBackgroundTest.java @@ -16,6 +16,8 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; +import java.util.Collection; +import java.util.HashSet; import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Literal; @@ -121,12 +123,14 @@ protected RioSetting[] getExpectedSupportedSettings() { return new RioSetting[] { BasicWriterSettings.BASE_DIRECTIVE, BasicWriterSettings.PRETTY_PRINT, - JSONLDSettings.COMPACT_ARRAYS, - JSONLDSettings.JSONLD_MODE, - JSONLDSettings.PRODUCE_GENERALIZED_RDF, - JSONLDSettings.USE_RDF_TYPE, - JSONLDSettings.USE_NATIVE_TYPES, - JSONLDSettings.EXCEPTION_ON_WARNING + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.COMPACT_ARRAYS, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.JSONLD_MODE, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.USE_RDF_TYPE, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.USE_NATIVE_TYPES, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.PRODUCE_GENERALIZED_RDF, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.EXCEPTION_ON_WARNING, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.FRAME + }; } } diff --git a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriterOldTest.java b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriterOldTest.java new file mode 100644 index 0000000000..263cd0eba2 --- /dev/null +++ b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriterOldTest.java @@ -0,0 +1,259 @@ +/******************************************************************************* + * Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.rio.jsonld; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; + +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Literal; +import org.eclipse.rdf4j.model.Model; +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.impl.LinkedHashModel; +import org.eclipse.rdf4j.model.vocabulary.DCTERMS; +import org.eclipse.rdf4j.model.vocabulary.XSD; +import org.eclipse.rdf4j.rio.ParserConfig; +import org.eclipse.rdf4j.rio.RDFFormat; +import org.eclipse.rdf4j.rio.RDFHandlerException; +import org.eclipse.rdf4j.rio.RDFParseException; +import org.eclipse.rdf4j.rio.RDFParser; +import org.eclipse.rdf4j.rio.RDFWriter; +import org.eclipse.rdf4j.rio.RDFWriterTest; +import org.eclipse.rdf4j.rio.Rio; +import org.eclipse.rdf4j.rio.RioSetting; +import org.eclipse.rdf4j.rio.WriterConfig; +import org.eclipse.rdf4j.rio.helpers.BasicParserSettings; +import org.eclipse.rdf4j.rio.helpers.BasicWriterSettings; +import org.eclipse.rdf4j.rio.helpers.JSONLDMode; +import org.eclipse.rdf4j.rio.helpers.JSONLDSettings; +import org.eclipse.rdf4j.rio.helpers.StatementCollector; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import no.hasmac.jsonld.JsonLdError; +import no.hasmac.jsonld.document.Document; +import no.hasmac.jsonld.document.JsonDocument; + +/** + * @author Peter Ansell + * + * Tests with the old JSONLDSettings class + */ +public class JSONLDWriterOldTest extends RDFWriterTest { + private final String exNs = "http://example.org/"; + + public JSONLDWriterOldTest() { + super(new JSONLDWriterFactory(), new JSONLDParserFactory()); + } + + @Override + protected void setupWriterConfig(WriterConfig config) { + super.setupWriterConfig(config); + config.set(JSONLDSettings.JSONLD_MODE, JSONLDMode.COMPACT); + } + + @Override + protected void setupParserConfig(ParserConfig config) { + super.setupParserConfig(config); + config.set(BasicParserSettings.FAIL_ON_UNKNOWN_DATATYPES, true); + config.set(BasicParserSettings.FAIL_ON_UNKNOWN_LANGUAGES, true); + } + + @Test + @Override + @Disabled("TODO: Determine why this test is breaking") + public void testIllegalPrefix() throws RDFHandlerException, RDFParseException { + } + + @Test + public void testEmptyNamespace() { + IRI uri1 = vf.createIRI(exNs, "uri1"); + IRI uri2 = vf.createIRI(exNs, "uri2"); + + StringWriter w = new StringWriter(); + + RDFWriter rdfWriter = rdfWriterFactory.getWriter(w); + rdfWriter.getWriterConfig().set(JSONLDSettings.JSONLD_MODE, JSONLDMode.COMPACT); + rdfWriter.startRDF(); + rdfWriter.handleNamespace("", exNs); + rdfWriter.handleNamespace(DCTERMS.PREFIX, DCTERMS.NAMESPACE); + rdfWriter.handleStatement(vf.createStatement(uri1, DCTERMS.TITLE, vf.createBNode())); + rdfWriter.handleStatement(vf.createStatement(uri1, uri2, vf.createBNode())); + rdfWriter.endRDF(); + + assertTrue(w.toString().contains("@vocab"), "Does not contain @vocab"); + } + + @Test + public void testRoundTripNamespaces() throws Exception { + IRI uri1 = vf.createIRI(exNs, "uri1"); + IRI uri2 = vf.createIRI(exNs, "uri2"); + Literal plainLit = vf.createLiteral("plain", XSD.STRING); + + Statement st1 = vf.createStatement(uri1, uri2, plainLit); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + RDFWriter rdfWriter = rdfWriterFactory.getWriter(out); + rdfWriter.getWriterConfig().set(JSONLDSettings.JSONLD_MODE, JSONLDMode.COMPACT); + rdfWriter.startRDF(); + rdfWriter.handleNamespace("ex", exNs); + rdfWriter.handleStatement(st1); + rdfWriter.endRDF(); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + RDFParser rdfParser = rdfParserFactory.getParser(); + ParserConfig config = new ParserConfig(); + config.set(BasicParserSettings.FAIL_ON_UNKNOWN_DATATYPES, true); + config.set(BasicParserSettings.FAIL_ON_UNKNOWN_LANGUAGES, true); + rdfParser.setParserConfig(config); + rdfParser.setValueFactory(vf); + Model model = new LinkedHashModel(); + rdfParser.setRDFHandler(new StatementCollector(model)); + + rdfParser.parse(in, "foo:bar"); + + assertEquals(1, model.size(), "Unexpected number of statements, found " + model.size()); + + assertTrue(model.contains(st1), "missing namespaced statement"); + + if (rdfParser.getRDFFormat().supportsNamespaces()) { + assertTrue(!model.getNamespaces().isEmpty(), + "Expected at least one namespace, found " + model.getNamespaces().size()); + assertEquals(exNs, model.getNamespace("ex").get().getName()); + } + } + + /** + * Test if the JSON-LD writer honors the "native RDF type" setting. + */ + @Test + public void testNativeRDFTypes() { + IRI subject = vf.createIRI(exNs, "uri1"); + IRI predicate = vf.createIRI(exNs, "uri2"); + Literal object = vf.createLiteral(true); + Statement stmt = vf.createStatement(subject, predicate, object); + + StringWriter w = new StringWriter(); + RDFWriter rdfWriter = rdfWriterFactory.getWriter(w); + rdfWriter.getWriterConfig().set(JSONLDSettings.JSONLD_MODE, JSONLDMode.COMPACT); + rdfWriter.getWriterConfig().set(JSONLDSettings.COMPACT_ARRAYS, true); + rdfWriter.getWriterConfig().set(JSONLDSettings.USE_NATIVE_TYPES, true); + + rdfWriter.startRDF(); + rdfWriter.handleStatement(stmt); + rdfWriter.endRDF(); + + assertTrue(!w.toString().contains("@type"), "Does contain @type"); + } + + @Test + public void testFraming() throws IOException, JsonLdError { + Model data = Rio.parse(new StringReader("{\n" + + " \"@context\": {\n" + + " \"dc11\": \"http://purl.org/dc/elements/1.1/\",\n" + + " \"ex\": \"http://example.org/vocab#\",\n" + + " \"xsd\": \"http://www.w3.org/2001/XMLSchema#\",\n" + + " \"ex:contains\": {\n" + + " \"@type\": \"@id\"\n" + + " }\n" + + " },\n" + + " \"@graph\": [\n" + + " {\n" + + " \"@id\": \"http://example.org/library\",\n" + + " \"@type\": \"ex:Library\",\n" + + " \"ex:contains\": \"http://example.org/library/the-republic\"\n" + + " },\n" + + " {\n" + + " \"@id\": \"http://example.org/library/the-republic\",\n" + + " \"@type\": \"ex:Book\",\n" + + " \"dc11:creator\": \"Plato\",\n" + + " \"dc11:title\": \"The Republic\",\n" + + " \"ex:contains\": \"http://example.org/library/the-republic#introduction\"\n" + + " },\n" + + " {\n" + + " \"@id\": \"http://example.org/library/the-republic#introduction\",\n" + + " \"@type\": \"ex:Chapter\",\n" + + " \"dc11:description\": \"An introductory chapter on The Republic.\",\n" + + " \"dc11:title\": \"The Introduction\"\n" + + " }\n" + + " ]\n" + + "}"), "", RDFFormat.JSONLD); + + Document frame = JsonDocument.of(new StringReader("{\n" + + " \"@context\": {\n" + + " \"dc11\": \"http://purl.org/dc/elements/1.1/\",\n" + + " \"ex\": \"http://example.org/vocab#\"\n" + + " },\n" + + " \"@type\": \"ex:Library\",\n" + + " \"ex:contains\": {\n" + + " \"@type\": \"ex:Book\",\n" + + " \"ex:contains\": {\n" + + " \"@type\": \"ex:Chapter\"\n" + + " }\n" + + " }\n" + + "}")); + + StringWriter stringWriter = new StringWriter(); + RDFWriter rdfWriter = rdfWriterFactory.getWriter(stringWriter); + rdfWriter.getWriterConfig().set(JSONLDSettings.JSONLD_MODE, JSONLDMode.FRAME); + rdfWriter.getWriterConfig().set(JSONLDSettings.FRAME, frame); + rdfWriter.getWriterConfig().set(BasicWriterSettings.PRETTY_PRINT, true); + + rdfWriter.startRDF(); + data.forEach(rdfWriter::handleStatement); + rdfWriter.endRDF(); + + assertEquals( + "{\n" + + " \"@id\": \"http://example.org/library\",\n" + + " \"@type\": \"ex:Library\",\n" + + " \"ex:contains\": {\n" + + " \"@id\": \"http://example.org/library/the-republic\",\n" + + " \"@type\": \"ex:Book\",\n" + + " \"ex:contains\": {\n" + + " \"@id\": \"http://example.org/library/the-republic#introduction\",\n" + + " \"@type\": \"ex:Chapter\",\n" + + " \"dc11:description\": \"An introductory chapter on The Republic.\",\n" + + " \"dc11:title\": \"The Introduction\"\n" + + " },\n" + + " \"dc11:creator\": \"Plato\",\n" + + " \"dc11:title\": \"The Republic\"\n" + + " },\n" + + " \"@context\": {\n" + + " \"dc11\": \"http://purl.org/dc/elements/1.1/\",\n" + + " \"ex\": \"http://example.org/vocab#\"\n" + + " }\n" + + "}", + stringWriter.toString()); + + } + + @Override + protected RioSetting[] getExpectedSupportedSettings() { + return new RioSetting[] { + BasicWriterSettings.BASE_DIRECTIVE, + BasicWriterSettings.PRETTY_PRINT, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.COMPACT_ARRAYS, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.JSONLD_MODE, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.USE_RDF_TYPE, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.USE_NATIVE_TYPES, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.PRODUCE_GENERALIZED_RDF, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.EXCEPTION_ON_WARNING, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.FRAME + }; + } +} diff --git a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriterTest.java b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriterTest.java index 7eb23ffe41..0a904b5ab8 100644 --- a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriterTest.java +++ b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriterTest.java @@ -38,8 +38,6 @@ import org.eclipse.rdf4j.rio.WriterConfig; import org.eclipse.rdf4j.rio.helpers.BasicParserSettings; import org.eclipse.rdf4j.rio.helpers.BasicWriterSettings; -import org.eclipse.rdf4j.rio.helpers.JSONLDMode; -import org.eclipse.rdf4j.rio.helpers.JSONLDSettings; import org.eclipse.rdf4j.rio.helpers.StatementCollector; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -245,12 +243,13 @@ protected RioSetting[] getExpectedSupportedSettings() { return new RioSetting[] { BasicWriterSettings.BASE_DIRECTIVE, BasicWriterSettings.PRETTY_PRINT, - JSONLDSettings.COMPACT_ARRAYS, - JSONLDSettings.JSONLD_MODE, - JSONLDSettings.PRODUCE_GENERALIZED_RDF, - JSONLDSettings.USE_RDF_TYPE, - JSONLDSettings.USE_NATIVE_TYPES, - JSONLDSettings.EXCEPTION_ON_WARNING + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.COMPACT_ARRAYS, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.JSONLD_MODE, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.USE_RDF_TYPE, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.USE_NATIVE_TYPES, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.PRODUCE_GENERALIZED_RDF, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.EXCEPTION_ON_WARNING, + org.eclipse.rdf4j.rio.jsonld.JSONLDSettings.FRAME }; } } diff --git a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/ndjsonld/NDJSONLDWriterTest.java b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/ndjsonld/NDJSONLDWriterTest.java index 90c793e9dd..eed20b0bb6 100644 --- a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/ndjsonld/NDJSONLDWriterTest.java +++ b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/ndjsonld/NDJSONLDWriterTest.java @@ -58,7 +58,8 @@ protected RioSetting[] getExpectedSupportedSettings() { JSONLDSettings.PRODUCE_GENERALIZED_RDF, JSONLDSettings.USE_RDF_TYPE, JSONLDSettings.USE_NATIVE_TYPES, - JSONLDSettings.EXCEPTION_ON_WARNING + JSONLDSettings.EXCEPTION_ON_WARNING, + JSONLDSettings.FRAME }; } }