diff --git a/README.md b/README.md index 77d21811..7553c32e 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,8 @@ implementation("org.glassfish:jakarta.json:2.0.1") Titanium provides high-level [JsonLd](https://javadoc.io/doc/com.apicatalog/titanium-json-ld/latest/com/apicatalog/jsonld/JsonLd.html) API to interact with the processor. +#### Transformations + ```javascript // Expansion @@ -141,8 +143,9 @@ JsonLd.frame("https://example/document.jsonld", "https://example/frame.jsonld"). ``` +#### Local JSON Document + ```javascript -// Local document Document document = JsonDocument.of(InputStream) or JsonDocument.of(Reader) ... JsonLd.expand(document).get(); @@ -151,6 +154,16 @@ JsonLd.compact(document, contextDocument).get(); ... ``` +#### Processing Timeout +A processor gets terminated eventually after a specified time. Please note +the duration does not cover `DocumentLoader` processing time. +You have to set-up a read timeout separately. + +```javascript +// since 1.4.0 - +JsonLd.expand(...).timeout(duration)...get(); +``` + #### HTTP Document Loader Timeout Configure and set a custom HTTP document loader instance. diff --git a/src/main/java/com/apicatalog/jsonld/JsonLdOptions.java b/src/main/java/com/apicatalog/jsonld/JsonLdOptions.java index f49d5f78..c23591c7 100644 --- a/src/main/java/com/apicatalog/jsonld/JsonLdOptions.java +++ b/src/main/java/com/apicatalog/jsonld/JsonLdOptions.java @@ -16,6 +16,7 @@ package com.apicatalog.jsonld; import java.net.URI; +import java.time.Duration; import com.apicatalog.jsonld.context.cache.Cache; import com.apicatalog.jsonld.context.cache.LruCache; @@ -29,9 +30,11 @@ import jakarta.json.JsonValue; /** - * The {@link JsonLdOptions} type is used to pass various options to the processor. + * The {@link JsonLdOptions} type is used to pass various options to the + * processor. * - * @see The + * @see The * JsonLdOptions Specification. * */ @@ -48,35 +51,35 @@ public enum RdfDirection { public static final boolean DEFAULT_URI_VALIDATION = true; /** - * The base IRI to use when expanding or compacting the document. - * If set, this overrides the input document's IRI. + * The base IRI to use when expanding or compacting the document. If set, this + * overrides the input document's IRI. */ private URI base; /** - * 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. + * 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. */ private boolean compactArrays; /** - * Determines if IRIs are compacted relative to the base option - * or document location when compacting. + * Determines if IRIs are compacted relative to the base option or document + * location when compacting. */ private boolean compactToRelative; /** - * The callback of the loader to be used to retrieve remote documents and contexts, - * implementing the LoadDocumentCallback. If specified, it is used to retrieve - * remote documents and contexts; otherwise, if not specified, - * the processor's built-in loader is used. + * The callback of the loader to be used to retrieve remote documents and + * contexts, implementing the LoadDocumentCallback. If specified, it is used to + * retrieve remote documents and contexts; otherwise, if not specified, the + * processor's built-in loader is used. */ private DocumentLoader documentLoader; /** - * A context that is used to initialize the active context when expanding a document. + * A context that is used to initialize the active context when expanding a + * document. */ private Document expandContext; @@ -121,6 +124,8 @@ public enum RdfDirection { private Cache documentCache; private boolean uriValidation; + + private Duration timeout; public JsonLdOptions() { this(SchemeRouter.defaultInstance()); @@ -157,6 +162,7 @@ public JsonLdOptions(DocumentLoader loader) { this.contextCache = new LruCache<>(256); this.documentCache = null; this.uriValidation = DEFAULT_URI_VALIDATION; + this.timeout = null; } public JsonLdOptions(JsonLdOptions options) { @@ -188,6 +194,7 @@ public JsonLdOptions(JsonLdOptions options) { this.contextCache = options.contextCache; this.documentCache = options.documentCache; this.uriValidation = options.uriValidation; + this.timeout = options.timeout; } /** @@ -215,8 +222,8 @@ public boolean isCompactArrays() { } /** - * Determines if IRIs are compacted relative to the {@link #getBase()} option - * or document location when + * Determines if IRIs are compacted relative to the {@link #getBase()} option or + * document location when * compacting. * * @return true if IRI relativization is enabled @@ -227,9 +234,9 @@ public boolean isCompactToRelative() { /** * The callback of the loader to be used to retrieve remote documents and - * contexts, implementing the {@link DocumentLoader}. If specified, it is - * used to retrieve remote documents and contexts; otherwise, if not specified, - * the processor's built-in loader is used. + * contexts, implementing the {@link DocumentLoader}. If specified, it is used + * to retrieve remote documents and contexts; otherwise, if not specified, the + * processor's built-in loader is used. * * @return the loader or null is is not set */ @@ -446,7 +453,8 @@ public boolean isRdfStar() { } /** - * Experimental: Enables JSON-LD-STAR extension. Only expansion is supported. Disabled by default. + * Experimental: Enables JSON-LD-STAR extension. Only expansion is supported. + * Disabled by default. * * @see JSON-LD-STAR Draft * @@ -457,7 +465,8 @@ public void setRdfStar(boolean rdfStar) { /** * if disabled only URIs required for processing are parsed and validated. - * Disabling URI validation might improve performance depending on the number of processed URIs. + * Disabling URI validation might improve performance depending on the number of + * processed URIs. *

* Warning: Disabled validation could cause an invalid output. *

@@ -473,7 +482,8 @@ public boolean isUriValidation() { /** * if disabled only URIs required for processing are parsed and validated. - * Disabling URI validation might improve performance depending on the number of processed URIs. + * Disabling URI validation might improve performance depending on the number of + * processed URIs. *

* Warning: Disabled validation could cause an invalid output. *

@@ -486,4 +496,31 @@ public boolean isUriValidation() { public void setUriValidation(boolean enabled) { this.uriValidation = enabled; } + + /** + * A processing timeout. An exception is thrown when a processing time exceeds + * the duration, if set. There is no currency that processing gets terminated + * immediately, but eventually. + * + * Please note, the timeout does not include time consumed by + * {@link DocumentLoader}. + * + * @return a duration after which a processing is prematurely terminated. + */ + public Duration getTimeout() { + return timeout; + } + + /** + * Set a pressing timeout. A processing is eventually terminated after the + * specified duration. Set null for no timeout. + * + * Please note, the timeout does not include time consumed by + * {@link DocumentLoader}. + * + * @param timeout to limit processing time + */ + public void setTimeout(Duration timeout) { + this.timeout = timeout; + } } \ No newline at end of file diff --git a/src/main/java/com/apicatalog/jsonld/compaction/Compaction.java b/src/main/java/com/apicatalog/jsonld/compaction/Compaction.java index d6c61a29..606a0ce5 100644 --- a/src/main/java/com/apicatalog/jsonld/compaction/Compaction.java +++ b/src/main/java/com/apicatalog/jsonld/compaction/Compaction.java @@ -25,7 +25,6 @@ import com.apicatalog.jsonld.JsonLdError; import com.apicatalog.jsonld.JsonLdErrorCode; -import com.apicatalog.jsonld.JsonLdVersion; import com.apicatalog.jsonld.context.ActiveContext; import com.apicatalog.jsonld.context.TermDefinition; import com.apicatalog.jsonld.json.JsonMapBuilder; @@ -169,7 +168,7 @@ public JsonValue compact(final String activeProperty, final JsonValue element) t // 7. if ((elementObject.containsKey(Keywords.VALUE) || elementObject.containsKey(Keywords.ID)) - && (!activeContext.getOptions().isRdfStar() || !elementObject.containsKey(Keywords.ANNOTATION))) { + && (!activeContext.runtime().isRdfStar() || !elementObject.containsKey(Keywords.ANNOTATION))) { final JsonValue result = activeContext.valueCompaction().compact(elementObject, activeProperty); @@ -242,7 +241,7 @@ public JsonValue compact(final String activeProperty, final JsonValue element) t compactedValue = JsonUtils.toJsonValue(activeContext.uriCompaction().compact(((JsonString) expandedValue).getString())); // json-ld-star - } else if (activeContext.getOptions().isRdfStar() && NodeObject.isEmbeddedNode(expandedValue)) { + } else if (activeContext.runtime().isRdfStar() && NodeObject.isEmbeddedNode(expandedValue)) { compactedValue = Compaction.with(activeContext) .compactArrays(compactArrays) .ordered(ordered) @@ -294,7 +293,7 @@ public JsonValue compact(final String activeProperty, final JsonValue element) t // 12.2.4. final boolean asArray = !compactArrays - || (activeContext.inMode(JsonLdVersion.V1_1) + || (activeContext.runtime().isV11() && activeContext.getTerm(alias).filter(t -> t.hasContainerMapping(Keywords.SET)).isPresent()); // 12.2.5. diff --git a/src/main/java/com/apicatalog/jsonld/compaction/UriCompaction.java b/src/main/java/com/apicatalog/jsonld/compaction/UriCompaction.java index 31bafbec..c1840b80 100644 --- a/src/main/java/com/apicatalog/jsonld/compaction/UriCompaction.java +++ b/src/main/java/com/apicatalog/jsonld/compaction/UriCompaction.java @@ -24,7 +24,6 @@ import com.apicatalog.jsonld.JsonLdError; import com.apicatalog.jsonld.JsonLdErrorCode; -import com.apicatalog.jsonld.JsonLdVersion; import com.apicatalog.jsonld.context.ActiveContext; import com.apicatalog.jsonld.context.InverseContext; import com.apicatalog.jsonld.context.TermDefinition; @@ -43,7 +42,8 @@ /** * - * @see IRI Compaction + * @see IRI + * Compaction * */ public final class UriCompaction { @@ -149,7 +149,7 @@ public String compact(final String variable) throws JsonLdError { containers.add(Keywords.SET); - // 4.7. + // 4.7. } else if (ListObject.isListObject(value)) { // 4.7.1. @@ -163,8 +163,8 @@ public String compact(final String variable) throws JsonLdError { // 4.7.3. String commonType = null; String commonLanguage = list.isEmpty() - ? defaultLanguage - : null; + ? defaultLanguage + : null; // 4.7.4. for (JsonValue item : list) { @@ -186,22 +186,22 @@ public String compact(final String variable) throws JsonLdError { itemLanguage += "_".concat(item.asJsonObject().getString(Keywords.DIRECTION).toLowerCase()); - // 4.7.4.2.2. + // 4.7.4.2.2. } else if (item.asJsonObject().containsKey(Keywords.LANGUAGE)) { itemLanguage = item.asJsonObject().getString(Keywords.LANGUAGE).toLowerCase(); - // 4.7.4.2.3. + // 4.7.4.2.3. } else if (item.asJsonObject().containsKey(Keywords.TYPE)) { itemType = item.asJsonObject().getString(Keywords.TYPE); - // 4.7.4.2.4. + // 4.7.4.2.4. } else { itemLanguage = Keywords.NULL; } - // 4.7.4.3. + // 4.7.4.3. } else { itemType = Keywords.ID; } @@ -210,10 +210,9 @@ public String compact(final String variable) throws JsonLdError { if (commonLanguage == null) { commonLanguage = itemLanguage; - // 4.7.4.5. + // 4.7.4.5. } else if (!Objects.equals(itemLanguage, commonLanguage) - && JsonUtils.containsKey(item, Keywords.VALUE) - ) { + && JsonUtils.containsKey(item, Keywords.VALUE)) { commonLanguage = Keywords.NONE; } @@ -221,7 +220,7 @@ public String compact(final String variable) throws JsonLdError { if (commonType == null) { commonType = itemType; - // 4.7.4.7. + // 4.7.4.7. } else if (!Objects.equals(itemType, commonType)) { commonType = Keywords.NONE; } @@ -247,12 +246,12 @@ public String compact(final String variable) throws JsonLdError { typeLanguage = Keywords.TYPE; typeLanguageValue = commonType; - // 4.7.8. + // 4.7.8. } else { typeLanguageValue = commonLanguage; } - // 4.8. + // 4.8. } else if (GraphObject.isGraphObject(value)) { // 4.8.1. @@ -292,7 +291,7 @@ public String compact(final String variable) throws JsonLdError { typeLanguage = Keywords.TYPE; typeLanguageValue = Keywords.ID; - // 4.9. + // 4.9. } else { // 4.9.1. @@ -300,8 +299,7 @@ public String compact(final String variable) throws JsonLdError { // 4.9.1.1. if (JsonUtils.contains(Keywords.DIRECTION, value) - && !JsonUtils.contains(Keywords.INDEX, value) - ) { + && !JsonUtils.contains(Keywords.INDEX, value)) { typeLanguageValue = ""; @@ -310,36 +308,35 @@ public String compact(final String variable) throws JsonLdError { JsonValue language = value.asJsonObject().get(Keywords.LANGUAGE); if (JsonUtils.isString(language)) { - typeLanguageValue = ((JsonString)language).getString().toLowerCase(); + typeLanguageValue = ((JsonString) language).getString().toLowerCase(); } } JsonValue direction = value.asJsonObject().get(Keywords.DIRECTION); if (JsonUtils.isString(direction)) { - typeLanguageValue += "_".concat(((JsonString)direction).getString().toLowerCase()); + typeLanguageValue += "_".concat(((JsonString) direction).getString().toLowerCase()); } containers.add(Keywords.LANGUAGE); containers.add(Keywords.LANGUAGE.concat(Keywords.SET)); - // 4.9.1.2. + // 4.9.1.2. } else if (JsonUtils.contains(Keywords.LANGUAGE, value) - && !JsonUtils.contains(Keywords.INDEX, value) - ) { + && !JsonUtils.contains(Keywords.INDEX, value)) { if (JsonUtils.contains(Keywords.LANGUAGE, value)) { JsonValue language = value.asJsonObject().get(Keywords.LANGUAGE); if (JsonUtils.isString(language)) { - typeLanguageValue = ((JsonString)language).getString().toLowerCase(); + typeLanguageValue = ((JsonString) language).getString().toLowerCase(); } } containers.add(Keywords.LANGUAGE); containers.add(Keywords.LANGUAGE.concat(Keywords.SET)); - // 4.9.1.3. + // 4.9.1.3. } else if (JsonUtils.contains(Keywords.TYPE, value)) { typeLanguage = Keywords.TYPE; @@ -347,7 +344,7 @@ public String compact(final String variable) throws JsonLdError { } - // 4.9.2. + // 4.9.2. } else { typeLanguage = Keywords.TYPE; @@ -367,19 +364,17 @@ public String compact(final String variable) throws JsonLdError { containers.add(Keywords.NONE); // 4.11. - if (!activeContext.inMode(JsonLdVersion.V1_0) + if (!activeContext.runtime().isV10() && (JsonUtils.isNotObject(value) - || !value.asJsonObject().containsKey(Keywords.INDEX)) - ) { + || !value.asJsonObject().containsKey(Keywords.INDEX))) { containers.add(Keywords.INDEX); containers.add(Keywords.INDEX.concat(Keywords.SET)); } // 4.12. - if (!activeContext.inMode(JsonLdVersion.V1_0) + if (!activeContext.runtime().isV10() && JsonUtils.containsKey(value, Keywords.VALUE) - && value.asJsonObject().size() == 1 - ) { + && value.asJsonObject().size() == 1) { containers.add(Keywords.LANGUAGE); containers.add(Keywords.LANGUAGE.concat(Keywords.SET)); @@ -400,19 +395,18 @@ public String compact(final String variable) throws JsonLdError { // 4.16. if ((Keywords.REVERSE.equals(typeLanguageValue) || Keywords.ID.equals(typeLanguageValue)) - && JsonUtils.containsKey(value, Keywords.ID) - ) { + && JsonUtils.containsKey(value, Keywords.ID)) { final JsonValue idValue = value.asJsonObject().get(Keywords.ID); // json-ld-star - if (activeContext.getOptions().isRdfStar() && NodeObject.isEmbeddedNode(idValue)) { + if (activeContext.runtime().isRdfStar() && NodeObject.isEmbeddedNode(idValue)) { preferredValues.add(Keywords.ID); preferredValues.add(Keywords.VOCAB); } else if (JsonUtils.isString(idValue)) { // 4.16.1. - final String idString = ((JsonString)idValue).getString(); + final String idString = ((JsonString) idValue).getString(); final String compactedIdValue = activeContext.uriCompaction().vocab(true).compact(idString); @@ -421,24 +415,23 @@ public String compact(final String variable) throws JsonLdError { if (compactedIdValueTermDefinition .map(TermDefinition::getUriMapping) .filter(idString::equals) - .isPresent() - ) { + .isPresent()) { preferredValues.add(Keywords.VOCAB); preferredValues.add(Keywords.ID); - // 4.16.2. + // 4.16.2. } else { preferredValues.add(Keywords.ID); preferredValues.add(Keywords.VOCAB); } } else { - throw new JsonLdError(JsonLdErrorCode.INVALID_KEYWORD_ID_VALUE, "An @id entry was encountered whose value was not a string but [" + idValue + "]."); + throw new JsonLdError(JsonLdErrorCode.INVALID_KEYWORD_ID_VALUE, "An @id entry was encountered whose value was not a string but [" + idValue + "]."); } preferredValues.add(Keywords.NONE); - // 4.17. + // 4.17. } else { preferredValues.add(typeLanguageValue); @@ -478,7 +471,7 @@ public String compact(final String variable) throws JsonLdError { // 5., 5.1. if ((vocab && activeContext.getVocabularyMapping() != null) && (variable.startsWith(activeContext.getVocabularyMapping()) - && variable.length() > activeContext.getVocabularyMapping().length())) { + && variable.length() > activeContext.getVocabularyMapping().length())) { String suffix = variable.substring(activeContext.getVocabularyMapping().length()); @@ -499,28 +492,24 @@ public String compact(final String variable) throws JsonLdError { if (termDefinition.getUriMapping() == null || variable.equals(termDefinition.getUriMapping()) || !variable.startsWith(termDefinition.getUriMapping()) - || termDefinition.isNotPrefix() - ) { + || termDefinition.isNotPrefix()) { continue; } // 7.2. - String compactUriCandidate = - termEntry.getKey() - .concat(":") - .concat(variable.substring(termDefinition.getUriMapping().length())); + String compactUriCandidate = termEntry.getKey() + .concat(":") + .concat(variable.substring(termDefinition.getUriMapping().length())); // 7.3. if (((compactUri == null || (compactUriCandidate.compareTo(compactUri) < 0)) - && !activeContext.containsTerm(compactUriCandidate)) + && !activeContext.containsTerm(compactUriCandidate)) || (activeContext - .getTerm(compactUriCandidate) - .map(TermDefinition::getUriMapping) - .filter(u -> u.equals(variable)) - .isPresent() - && JsonUtils.isNull(value) - ) - ) { + .getTerm(compactUriCandidate) + .map(TermDefinition::getUriMapping) + .filter(u -> u.equals(variable)) + .isPresent() + && JsonUtils.isNull(value))) { compactUri = compactUriCandidate; } } @@ -538,17 +527,17 @@ public String compact(final String variable) throws JsonLdError { && uri.isAbsolute() && uri.getScheme() != null && uri.getAuthority() == null - && activeContext.getTerm(uri.getScheme()).filter(TermDefinition::isPrefix).isPresent() - ) { - throw new JsonLdError(JsonLdErrorCode.IRI_CONFUSED_WITH_PREFIX); + && activeContext.getTerm(uri.getScheme()).filter(TermDefinition::isPrefix).isPresent()) { + throw new JsonLdError(JsonLdErrorCode.IRI_CONFUSED_WITH_PREFIX); } - } catch (IllegalArgumentException e) { /* variable is not URI */ } + } catch (IllegalArgumentException e) { + /* variable is not URI */ } // 10. if (!vocab && activeContext.getBaseUri() != null && !BlankNode.hasPrefix(variable)) { - final String relativeUri = UriRelativizer.relativize(activeContext.getBaseUri(), variable); + final String relativeUri = UriRelativizer.relativize(activeContext.getBaseUri(), variable); - return Keywords.matchForm(relativeUri) ? "./".concat(relativeUri) : relativeUri; + return Keywords.matchForm(relativeUri) ? "./".concat(relativeUri) : relativeUri; } // 11. diff --git a/src/main/java/com/apicatalog/jsonld/context/ActiveContext.java b/src/main/java/com/apicatalog/jsonld/context/ActiveContext.java index 9138ea11..d17018de 100644 --- a/src/main/java/com/apicatalog/jsonld/context/ActiveContext.java +++ b/src/main/java/com/apicatalog/jsonld/context/ActiveContext.java @@ -21,13 +21,12 @@ import java.util.Map; import java.util.Optional; -import com.apicatalog.jsonld.JsonLdOptions; -import com.apicatalog.jsonld.JsonLdVersion; import com.apicatalog.jsonld.compaction.UriCompaction; import com.apicatalog.jsonld.compaction.ValueCompaction; import com.apicatalog.jsonld.expansion.UriExpansion; import com.apicatalog.jsonld.expansion.ValueExpansion; import com.apicatalog.jsonld.lang.DirectionType; +import com.apicatalog.jsonld.processor.ProcessingRuntime; import jakarta.json.JsonObject; @@ -61,23 +60,23 @@ public final class ActiveContext { // an optional default base direction ("ltr" or "rtl") private DirectionType defaultBaseDirection; + + private final ProcessingRuntime runtime; - private final JsonLdOptions options; - - public ActiveContext(final JsonLdOptions options) { - this(null, null, null, options); + public ActiveContext(final ProcessingRuntime runtime) { + this(null, null, null, runtime); } - public ActiveContext(final URI baseUri, final URI baseUrl, JsonLdOptions options) { - this(baseUri, baseUrl, null, options); + public ActiveContext(final URI baseUri, final URI baseUrl, ProcessingRuntime runtime) { + this(baseUri, baseUrl, null, runtime); } - public ActiveContext(final URI baseUri, final URI baseUrl, final ActiveContext previousContext, final JsonLdOptions options) { + public ActiveContext(final URI baseUri, final URI baseUrl, final ActiveContext previousContext, final ProcessingRuntime runtime) { this.baseUri = baseUri; this.baseUrl = baseUrl; this.previousContext = previousContext; this.terms = new LinkedHashMap<>(); - this.options = options; + this.runtime = runtime; } // copy constructor @@ -90,7 +89,7 @@ public ActiveContext(final ActiveContext origin) { this.vocabularyMapping = origin.vocabularyMapping; this.defaultLanguage = origin.defaultLanguage; this.defaultBaseDirection = origin.defaultBaseDirection; - this.options = origin.options; + this.runtime = origin.runtime; } public void createInverseContext() { @@ -132,10 +131,6 @@ public String getVocabularyMapping() { return vocabularyMapping; } - public boolean inMode(final JsonLdVersion version) { - return options.getProcessingMode() != null && options.getProcessingMode().equals(version); - } - public ActiveContext getPreviousContext() { return previousContext; } @@ -165,7 +160,7 @@ public ActiveContextBuilder newContext() { } public UriExpansion uriExpansion() { - return UriExpansion.with(this).uriValidation(options.isUriValidation()); + return UriExpansion.with(this).uriValidation(runtime.isUriValidation()); } public ValueExpansion valueExpansion() { @@ -188,10 +183,6 @@ public TermSelector termSelector(final String variable, final Collection return TermSelector.with(this, variable, containerMapping, typeLanguage); } - public JsonLdOptions getOptions() { - return options; - } - protected void setDefaultBaseDirection(final DirectionType defaultBaseDirection) { this.defaultBaseDirection = defaultBaseDirection; } @@ -224,4 +215,8 @@ protected void setTerm(final String term, final TermDefinition definition) { public String toString() { return "ActiveContext[terms=" + terms + ", previousContext=" + previousContext + "]"; } + + public ProcessingRuntime runtime() { + return runtime; + } } diff --git a/src/main/java/com/apicatalog/jsonld/context/ActiveContextBuilder.java b/src/main/java/com/apicatalog/jsonld/context/ActiveContextBuilder.java index 10b3d9fc..8e38489d 100644 --- a/src/main/java/com/apicatalog/jsonld/context/ActiveContextBuilder.java +++ b/src/main/java/com/apicatalog/jsonld/context/ActiveContextBuilder.java @@ -25,7 +25,6 @@ import com.apicatalog.jsonld.JsonLdError; import com.apicatalog.jsonld.JsonLdErrorCode; -import com.apicatalog.jsonld.JsonLdVersion; import com.apicatalog.jsonld.StringUtils; import com.apicatalog.jsonld.document.Document; import com.apicatalog.jsonld.http.ProfileConstants; @@ -160,8 +159,8 @@ public ActiveContext create(final JsonValue localContext, final URI baseUrl) thr // and, if propagate is false, previous context in result to the previous value // of result. result = propagate - ? new ActiveContext(activeContext.getBaseUrl(), activeContext.getBaseUrl(), activeContext.getOptions()) - : new ActiveContext(activeContext.getBaseUrl(), activeContext.getBaseUrl(), result.getPreviousContext(), activeContext.getOptions()); + ? new ActiveContext(activeContext.getBaseUrl(), activeContext.getBaseUrl(), activeContext.runtime()) + : new ActiveContext(activeContext.getBaseUrl(), activeContext.getBaseUrl(), result.getPreviousContext(), activeContext.runtime()); // 5.1.3. Continue with the next context continue; @@ -206,7 +205,7 @@ public ActiveContext create(final JsonValue localContext, final URI baseUrl) thr } // 5.5.2. - if (activeContext.inMode(JsonLdVersion.V1_0)) { + if (activeContext.runtime().isV10()) { throw new JsonLdError(JsonLdErrorCode.PROCESSING_MODE_CONFLICT); } } @@ -215,7 +214,7 @@ public ActiveContext create(final JsonValue localContext, final URI baseUrl) thr if (contextDefinition.containsKey(Keywords.IMPORT)) { // 5.6.1. - if (activeContext.inMode(JsonLdVersion.V1_0)) { + if (activeContext.runtime().isV10()) { throw new JsonLdError(JsonLdErrorCode.INVALID_CONTEXT_ENTRY); } @@ -230,7 +229,7 @@ public ActiveContext create(final JsonValue localContext, final URI baseUrl) thr final URI contextImportUri = UriResolver.resolveAsUri(baseUrl, ((JsonString) contextImport).getString()); // 5.6.4. - if (activeContext.getOptions().getDocumentLoader() == null) { + if (activeContext.runtime().getDocumentLoader() == null) { throw new JsonLdError(JsonLdErrorCode.LOADING_REMOTE_CONTEXT_FAILED); } @@ -242,7 +241,7 @@ public ActiveContext create(final JsonValue localContext, final URI baseUrl) thr try { - final Document importedDocument = activeContext.getOptions().getDocumentLoader().loadDocument(contextImportUri, loaderOptions); + final Document importedDocument = activeContext.runtime().getDocumentLoader().loadDocument(contextImportUri, loaderOptions); if (importedDocument == null) { throw new JsonLdError(JsonLdErrorCode.INVALID_REMOTE_CONTEXT, "Imported context[" + contextImportUri + "] is null."); @@ -394,7 +393,7 @@ public ActiveContext create(final JsonValue localContext, final URI baseUrl) thr if (contextDefinition.containsKey(Keywords.DIRECTION)) { // 5.10.1. - if (activeContext.inMode(JsonLdVersion.V1_0)) { + if (activeContext.runtime().isV10()) { throw new JsonLdError(JsonLdErrorCode.INVALID_CONTEXT_ENTRY); } @@ -428,7 +427,7 @@ public ActiveContext create(final JsonValue localContext, final URI baseUrl) thr // 5.11. if (contextDefinition.containsKey(Keywords.PROPAGATE)) { // 5.11.1. - if (activeContext.inMode(JsonLdVersion.V1_0)) { + if (activeContext.runtime().isV10()) { throw new JsonLdError(JsonLdErrorCode.INVALID_CONTEXT_ENTRY); } // 5.11.2. @@ -494,11 +493,10 @@ private void fetch(final String context, final URI baseUrl) throws JsonLdError { remoteContexts.add(contextKey); // 5.2.4 - if (activeContext.getOptions() != null - && activeContext.getOptions().getContextCache() != null - && activeContext.getOptions().getContextCache().containsKey(contextKey) && !validateScopedContext) { + if (activeContext.runtime().getContextCache() != null + && activeContext.runtime().getContextCache().containsKey(contextKey) && !validateScopedContext) { - JsonValue cachedContext = activeContext.getOptions().getContextCache().get(contextKey); + JsonValue cachedContext = activeContext.runtime().getContextCache().get(contextKey); result = result .newContext() .remoteContexts(new ArrayList<>(remoteContexts)) @@ -508,17 +506,16 @@ private void fetch(final String context, final URI baseUrl) throws JsonLdError { } // 5.2.5. - if (activeContext.getOptions().getDocumentLoader() == null) { + if (activeContext.runtime().getDocumentLoader() == null) { throw new JsonLdError(JsonLdErrorCode.LOADING_REMOTE_CONTEXT_FAILED, "Document loader is null. Cannot fetch [" + contextUri + "]."); } Document remoteImport = null; - if (activeContext.getOptions() != null - && activeContext.getOptions().getDocumentCache() != null - && activeContext.getOptions().getDocumentCache().containsKey(contextKey)) { + if (activeContext.runtime().getDocumentCache() != null + && activeContext.runtime().getDocumentCache().containsKey(contextKey)) { - remoteImport = activeContext.getOptions().getDocumentCache().get(contextKey); + remoteImport = activeContext.runtime().getDocumentCache().get(contextKey); } if (remoteImport == null) { @@ -529,7 +526,7 @@ private void fetch(final String context, final URI baseUrl) throws JsonLdError { try { - remoteImport = activeContext.getOptions().getDocumentLoader().loadDocument(contextUri, loaderOptions); + remoteImport = activeContext.runtime().getDocumentLoader().loadDocument(contextUri, loaderOptions); // 5.2.5.1. } catch (JsonLdError e) { @@ -563,10 +560,8 @@ private void fetch(final String context, final URI baseUrl) throws JsonLdError { importedContext = JsonProvider.instance().createObjectBuilder(importedContext.asJsonObject()).remove(Keywords.BASE).build(); } - if (activeContext.getOptions() != null - && activeContext.getOptions().getDocumentCache() != null) { - - activeContext.getOptions().getDocumentCache().put(contextKey, remoteImport); + if (activeContext.runtime().getDocumentCache() != null) { + activeContext.runtime().getDocumentCache().put(contextKey, remoteImport); } // 5.2.6 @@ -577,8 +572,8 @@ private void fetch(final String context, final URI baseUrl) throws JsonLdError { .validateScopedContext(validateScopedContext) .create(importedContext, remoteImport.getDocumentUrl()); - if (result.getOptions() != null && result.getOptions().getContextCache() != null && !validateScopedContext) { - result.getOptions().getContextCache().put(contextKey, importedContext); + if (result.runtime().getContextCache() != null && !validateScopedContext) { + result.runtime().getContextCache().put(contextKey, importedContext); } } catch (JsonLdError e) { diff --git a/src/main/java/com/apicatalog/jsonld/context/TermDefinitionBuilder.java b/src/main/java/com/apicatalog/jsonld/context/TermDefinitionBuilder.java index 2ddf0d9e..238afa75 100644 --- a/src/main/java/com/apicatalog/jsonld/context/TermDefinitionBuilder.java +++ b/src/main/java/com/apicatalog/jsonld/context/TermDefinitionBuilder.java @@ -26,7 +26,6 @@ import com.apicatalog.jsonld.JsonLdError; import com.apicatalog.jsonld.JsonLdErrorCode; -import com.apicatalog.jsonld.JsonLdVersion; import com.apicatalog.jsonld.StringUtils; import com.apicatalog.jsonld.json.JsonUtils; import com.apicatalog.jsonld.lang.BlankNode; @@ -150,7 +149,7 @@ public void create(final String term) throws JsonLdError { // 4. if (Keywords.TYPE.equals(term)) { - if (activeContext.inMode(JsonLdVersion.V1_0)) { + if (activeContext.runtime().isV10()) { throw new JsonLdError(JsonLdErrorCode.KEYWORD_REDEFINITION); } @@ -231,7 +230,7 @@ public void create(final String term) throws JsonLdError { // 11. if (valueObject.containsKey(Keywords.PROTECTED)) { - if (activeContext.inMode(JsonLdVersion.V1_0)) { + if (activeContext.runtime().isV10()) { throw new JsonLdError(JsonLdErrorCode.INVALID_TERM_DEFINITION); } @@ -268,7 +267,7 @@ public void create(final String term) throws JsonLdError { // 12.3. if (((Keywords.JSON.equals(expandedTypeString) || Keywords.NONE.equals(expandedTypeString)) - && activeContext.inMode(JsonLdVersion.V1_0)) + && activeContext.runtime().isV10()) // 12.4. || (Keywords.noneMatch(expandedTypeString, Keywords.ID, Keywords.JSON, Keywords.NONE, Keywords.VOCAB) && UriUtils.isNotAbsoluteUri(expandedTypeString, true))) { @@ -504,7 +503,7 @@ public void create(final String term) throws JsonLdError { if (valueObject.containsKey(Keywords.INDEX)) { // 20.1. - if (activeContext.inMode(JsonLdVersion.V1_0) || !definition.getContainerMapping().contains(Keywords.INDEX)) { + if (activeContext.runtime().isV10() || !definition.getContainerMapping().contains(Keywords.INDEX)) { throw new JsonLdError(JsonLdErrorCode.INVALID_TERM_DEFINITION); } @@ -535,7 +534,7 @@ public void create(final String term) throws JsonLdError { if (valueObject.containsKey(Keywords.CONTEXT)) { // 21.1. - if (activeContext.inMode(JsonLdVersion.V1_0)) { + if (activeContext.runtime().isV10()) { throw new JsonLdError(JsonLdErrorCode.INVALID_TERM_DEFINITION); } @@ -610,7 +609,7 @@ public void create(final String term) throws JsonLdError { if (valueObject.containsKey(Keywords.NEST)) { // 24.1 - if (activeContext.inMode(JsonLdVersion.V1_0)) { + if (activeContext.runtime().isV10()) { throw new JsonLdError(JsonLdErrorCode.INVALID_TERM_DEFINITION); } @@ -633,7 +632,7 @@ public void create(final String term) throws JsonLdError { if (valueObject.containsKey(Keywords.PREFIX)) { // 25.1. - if (activeContext.inMode(JsonLdVersion.V1_0) || term.contains(":") || term.contains("/")) { + if (activeContext.runtime().isV10() || term.contains(":") || term.contains("/")) { throw new JsonLdError(JsonLdErrorCode.INVALID_TERM_DEFINITION); } @@ -687,7 +686,7 @@ private final boolean validateContainer(final JsonValue value) { return false; } - if (activeContext.inMode(JsonLdVersion.V1_0)) { + if (activeContext.runtime().isV10()) { return JsonUtils.isString(container) && Keywords.noneMatch( diff --git a/src/main/java/com/apicatalog/jsonld/expansion/ArrayExpansion.java b/src/main/java/com/apicatalog/jsonld/expansion/ArrayExpansion.java index 2bf7025c..a63c5c3e 100644 --- a/src/main/java/com/apicatalog/jsonld/expansion/ArrayExpansion.java +++ b/src/main/java/com/apicatalog/jsonld/expansion/ArrayExpansion.java @@ -88,6 +88,8 @@ public JsonArray expand() throws JsonLdError { // 5.2. for (final JsonValue item : element) { + + activeContext.runtime().tick(); // 5.2.1 JsonValue expanded = Expansion diff --git a/src/main/java/com/apicatalog/jsonld/expansion/ObjectExpansion.java b/src/main/java/com/apicatalog/jsonld/expansion/ObjectExpansion.java index 1e217c36..e87de309 100644 --- a/src/main/java/com/apicatalog/jsonld/expansion/ObjectExpansion.java +++ b/src/main/java/com/apicatalog/jsonld/expansion/ObjectExpansion.java @@ -170,6 +170,8 @@ private void initPreviousContext() throws JsonLdError { for (final String key : Utils.index(element.keySet(), true)) { + activeContext.runtime().tick(); + final String expandedKey = activeContext .uriExpansion() .vocab(true) @@ -203,6 +205,8 @@ private String processTypeScoped(final ActiveContext typeContext) throws JsonLdE // 11. for (final String key : Utils.index(element.keySet(), true)) { + activeContext.runtime().tick(); + final String expandedKey = activeContext .uriExpansion() .vocab(true) @@ -226,6 +230,8 @@ private String processTypeScoped(final ActiveContext typeContext) throws JsonLdE while (terms.hasNext()) { + activeContext.runtime().tick(); + final String term = terms.next(); final Optional localContext = typeContext.getTerm(term).map(TermDefinition::getLocalContext); diff --git a/src/main/java/com/apicatalog/jsonld/expansion/ObjectExpansion1314.java b/src/main/java/com/apicatalog/jsonld/expansion/ObjectExpansion1314.java index 5c892796..6343f76e 100644 --- a/src/main/java/com/apicatalog/jsonld/expansion/ObjectExpansion1314.java +++ b/src/main/java/com/apicatalog/jsonld/expansion/ObjectExpansion1314.java @@ -27,7 +27,6 @@ import com.apicatalog.jsonld.JsonLdError; import com.apicatalog.jsonld.JsonLdErrorCode; -import com.apicatalog.jsonld.JsonLdVersion; import com.apicatalog.jsonld.context.ActiveContext; import com.apicatalog.jsonld.context.TermDefinition; import com.apicatalog.jsonld.json.JsonMapBuilder; @@ -135,6 +134,8 @@ public void expand() throws JsonLdError { continue; } + activeContext.runtime().tick(); + // 13.2. String expandedProperty = activeContext .uriExpansion() @@ -173,10 +174,10 @@ public void expand() throws JsonLdError { if (Keywords.ID.equals(expandedProperty)) { // Extension: JSON-LD-STAR (Experimental) - if (!activeContext.getOptions().isRdfStar() && Keywords.ANNOTATION.equals(activeProperty)) { + if (!activeContext.runtime().isRdfStar() && Keywords.ANNOTATION.equals(activeProperty)) { throw new JsonLdError(JsonLdErrorCode.INVALID_ANNOTATION); - } else if (activeContext.getOptions().isRdfStar() && JsonUtils.isNonEmptyObject(value)) { + } else if (activeContext.runtime().isRdfStar() && JsonUtils.isNonEmptyObject(value)) { expandedValue = Expansion .with(activeContext, value, null, baseUrl) @@ -189,7 +190,7 @@ public void expand() throws JsonLdError { } // 13.4.3.1 - } else if (!frameExpansion && JsonUtils.isNotString(value) && (!activeContext.getOptions().isNumericId() || JsonUtils.isNotNumber(value)) + } else if (!frameExpansion && JsonUtils.isNotString(value) && (!activeContext.runtime().isNumericId() || JsonUtils.isNotNumber(value)) || frameExpansion && JsonUtils.isNotString(value) && JsonUtils.isNonEmptyObject(value) @@ -377,7 +378,7 @@ else if (Keywords.GRAPH.equals(expandedProperty)) { else if (Keywords.INCLUDED.equals(expandedProperty)) { // 13.4.6.1 - if (activeContext.inMode(JsonLdVersion.V1_0)) { + if (activeContext.runtime().isV10()) { continue; } @@ -428,7 +429,7 @@ else if (Keywords.INCLUDED.equals(expandedProperty)) { // 13.4.7.1 if (Keywords.JSON.equals(inputType)) { - if (activeContext.inMode(JsonLdVersion.V1_0)) { + if (activeContext.runtime().isV10()) { throw new JsonLdError(JsonLdErrorCode.INVALID_VALUE_OBJECT_VALUE); } @@ -490,7 +491,7 @@ else if (Keywords.INCLUDED.equals(expandedProperty)) { // 13.4.9. if (Keywords.DIRECTION.equals(expandedProperty)) { // 13.4.9.1. - if (activeContext.inMode(JsonLdVersion.V1_0)) { + if (activeContext.runtime().isV10()) { continue; } @@ -641,7 +642,7 @@ else if (Keywords.INCLUDED.equals(expandedProperty)) { // Extension: JSON-LD-STAR (Experimental) if (Keywords.ANNOTATION.equals(expandedProperty)) { - if (!activeContext.getOptions().isRdfStar()) { + if (!activeContext.runtime().isRdfStar()) { continue; } @@ -975,6 +976,8 @@ else if (Keywords.INCLUDED.equals(expandedProperty)) { private void recurse() throws JsonLdError { + activeContext.runtime().tick(); + // step 3 final Optional propertyContext = activeContext .getTerm(activeProperty) diff --git a/src/main/java/com/apicatalog/jsonld/expansion/ValueExpansion.java b/src/main/java/com/apicatalog/jsonld/expansion/ValueExpansion.java index 1cb247bb..93aad9e0 100644 --- a/src/main/java/com/apicatalog/jsonld/expansion/ValueExpansion.java +++ b/src/main/java/com/apicatalog/jsonld/expansion/ValueExpansion.java @@ -70,7 +70,7 @@ public JsonObject expand(final JsonValue value, final String activeProperty) thr idValue = ((JsonString) value).getString(); // custom extension allowing to process numeric ids - } else if (activeContext.getOptions().isNumericId() && JsonUtils.isNumber(value)) { + } else if (activeContext.runtime().isNumericId() && JsonUtils.isNumber(value)) { idValue = ((JsonNumber) value).toString(); } diff --git a/src/main/java/com/apicatalog/jsonld/processor/CompactionProcessor.java b/src/main/java/com/apicatalog/jsonld/processor/CompactionProcessor.java index 8c86d65e..fc669d5d 100644 --- a/src/main/java/com/apicatalog/jsonld/processor/CompactionProcessor.java +++ b/src/main/java/com/apicatalog/jsonld/processor/CompactionProcessor.java @@ -34,7 +34,8 @@ /** * - * @see JsonLdProcessor.compact() + * @see JsonLdProcessor.compact() * */ public final class CompactionProcessor { @@ -108,11 +109,12 @@ public static final JsonObject compact(final Document input, final Document cont // 6. final JsonValue contextValue = context.getJsonContent() - .map(ctx -> JsonUtils.flatten(ctx, Keywords.CONTEXT)) - .orElse(JsonValue.EMPTY_JSON_OBJECT); + .map(ctx -> JsonUtils.flatten(ctx, Keywords.CONTEXT)) + .orElse(JsonValue.EMPTY_JSON_OBJECT); // 7. - final ActiveContext activeContext = new ActiveContext(options).newContext().create(contextValue, contextBase); + final ActiveContext activeContext = new ActiveContext( + ProcessingRuntime.of(options)).newContext().create(contextValue, contextBase); // 8. if (activeContext.getBaseUri() == null) { @@ -127,23 +129,22 @@ public static final JsonObject compact(final Document input, final Document cont // 9. JsonValue compactedOutput = Compaction - .with(activeContext) - .compactArrays(options.isCompactArrays()) - .ordered(options.isOrdered()) - .compact(expandedInput); + .with(activeContext) + .compactArrays(options.isCompactArrays()) + .ordered(options.isOrdered()) + .compact(expandedInput); // 9.1. if (JsonUtils.isEmptyArray(compactedOutput)) { compactedOutput = JsonValue.EMPTY_JSON_OBJECT; - // 9.2. + // 9.2. } else if (JsonUtils.isArray(compactedOutput)) { compactedOutput = JsonProvider.instance().createObjectBuilder() - .add( - activeContext.uriCompaction().vocab(true).compact(Keywords.GRAPH), - compactedOutput - ) - .build(); + .add( + activeContext.uriCompaction().vocab(true).compact(Keywords.GRAPH), + compactedOutput) + .build(); } if (JsonUtils.isNull(compactedOutput) || compactedOutput.asJsonObject().isEmpty()) { @@ -153,11 +154,10 @@ public static final JsonObject compact(final Document input, final Document cont // 9.3. if (JsonUtils.isNotNull(contextValue) && !JsonUtils.isEmptyArray(contextValue) - && !JsonUtils.isEmptyObject(contextValue) - ) { + && !JsonUtils.isEmptyObject(contextValue)) { compactedOutput = JsonProvider.instance().createObjectBuilder(compactedOutput.asJsonObject()) - .add(Keywords.CONTEXT, contextValue) - .build(); + .add(Keywords.CONTEXT, contextValue) + .build(); } return compactedOutput.asJsonObject(); diff --git a/src/main/java/com/apicatalog/jsonld/processor/ExpansionProcessor.java b/src/main/java/com/apicatalog/jsonld/processor/ExpansionProcessor.java index 6b615374..55231837 100644 --- a/src/main/java/com/apicatalog/jsonld/processor/ExpansionProcessor.java +++ b/src/main/java/com/apicatalog/jsonld/processor/ExpansionProcessor.java @@ -93,7 +93,7 @@ public static final JsonArray expand(Document input, final JsonLdOptions options baseUri = options.getBase(); } - ActiveContext activeContext = new ActiveContext(baseUri, baseUrl, options); + ActiveContext activeContext = new ActiveContext(baseUri, baseUrl, ProcessingRuntime.of(options)); // 6. If the expandContext option in options is set, update the active context // using the Context Processing algorithm, passing the expandContext as diff --git a/src/main/java/com/apicatalog/jsonld/processor/FramingProcessor.java b/src/main/java/com/apicatalog/jsonld/processor/FramingProcessor.java index 6b4cc9fe..46a672e9 100644 --- a/src/main/java/com/apicatalog/jsonld/processor/FramingProcessor.java +++ b/src/main/java/com/apicatalog/jsonld/processor/FramingProcessor.java @@ -27,7 +27,6 @@ import com.apicatalog.jsonld.JsonLdError; import com.apicatalog.jsonld.JsonLdErrorCode; import com.apicatalog.jsonld.JsonLdOptions; -import com.apicatalog.jsonld.JsonLdVersion; import com.apicatalog.jsonld.compaction.Compaction; import com.apicatalog.jsonld.context.ActiveContext; import com.apicatalog.jsonld.document.Document; @@ -53,7 +52,8 @@ /** * - * @see JsonLdProcessor.frame() + * @see JsonLdProcessor.frame() * */ public final class FramingProcessor { @@ -98,10 +98,9 @@ public static final JsonObject frame(final Document input, final Document frame, throw new JsonLdError(JsonLdErrorCode.LOADING_DOCUMENT_FAILED, "Frame or Frame.Document is null."); } - final JsonStructure frameStructure = - frame - .getJsonContent() - .orElseThrow(() -> new JsonLdError(JsonLdErrorCode.LOADING_DOCUMENT_FAILED, "Frame is not JSON object but null.")); + final JsonStructure frameStructure = frame + .getJsonContent() + .orElseThrow(() -> new JsonLdError(JsonLdErrorCode.LOADING_DOCUMENT_FAILED, "Frame is not JSON object but null.")); if (JsonUtils.isNotObject(frameStructure)) { throw new JsonLdError(JsonLdErrorCode.LOADING_DOCUMENT_FAILED, "Frame is not JSON object but [" + frameStructure + "]."); @@ -118,31 +117,28 @@ public static final JsonObject frame(final Document input, final Document frame, // 7. JsonArray expandedFrame = ExpansionProcessor.expand(frame, expansionOptions, true); - JsonValue context = JsonValue.EMPTY_JSON_OBJECT; - if (frameObject.containsKey(Keywords.CONTEXT) - ) { + if (frameObject.containsKey(Keywords.CONTEXT)) { context = frameObject.get(Keywords.CONTEXT); } // 9. final URI contextBase = (frame.getContextUrl() != null) - ? frame.getDocumentUrl() - : options.getBase(); + ? frame.getDocumentUrl() + : options.getBase(); // 10-11. - final ActiveContext activeContext = - new ActiveContext(input.getDocumentUrl(), input.getDocumentUrl(), options) - .newContext() - .create(context, contextBase); + final ActiveContext activeContext = new ActiveContext(input.getDocumentUrl(), input.getDocumentUrl(), ProcessingRuntime.of(options)) + .newContext() + .create(context, contextBase); final String graphKey = activeContext.uriCompaction().vocab(true).compact(Keywords.GRAPH); // 13. boolean frameDefault = false; for (final String key : frameObject.keySet()) { - if(key.equals(graphKey)) { + if (key.equals(graphKey)) { frameDefault = true; break; } @@ -151,13 +147,13 @@ public static final JsonObject frame(final Document input, final Document frame, // 14. final FramingState state = new FramingState(); - state.setEmbed(options.getEmbed()); // 14.1. - state.setEmbedded(false); // 14.2. - state.setExplicitInclusion(options.isExplicit()); // 14.3. - state.setRequireAll(options.isRequiredAll()); // 14.4. - state.setOmitDefault(options.isOmitDefault()); // 14.5. + state.setEmbed(options.getEmbed()); // 14.1. + state.setEmbedded(false); // 14.2. + state.setExplicitInclusion(options.isExplicit()); // 14.3. + state.setRequireAll(options.isRequiredAll()); // 14.4. + state.setOmitDefault(options.isOmitDefault()); // 14.5. - state.setGraphMap(NodeMapBuilder.with(expandedInput, new NodeMap()).build()); // 14.7. + state.setGraphMap(NodeMapBuilder.with(expandedInput, new NodeMap()).build()); // 14.7. if (frameDefault) { state.setGraphName(Keywords.DEFAULT); // 14.6. @@ -172,18 +168,17 @@ public static final JsonObject frame(final Document input, final Document frame, // 16. Framing.with(state, - new ArrayList<>(state.getGraphMap().subjects(state.getGraphName())), - Frame.of(expandedFrame), - resultMap, - null - ) + new ArrayList<>(state.getGraphMap().subjects(state.getGraphName())), + Frame.of(expandedFrame), + resultMap, + null) .ordered(options.isOrdered()) .frame(); Stream result = resultMap.valuesToArray().stream(); // 17. - remove blank @id - if (!activeContext.inMode(JsonLdVersion.V1_0)) { + if (!activeContext.runtime().isV10()) { final List remove = findBlankNodes(resultMap.valuesToArray()); @@ -199,20 +194,20 @@ public static final JsonObject frame(final Document input, final Document frame, // 19. JsonValue compactedResults = Compaction - .with(activeContext) - .compactArrays(options.isCompactArrays()) - .ordered(options.isOrdered()) - .compact(filtered.build()); + .with(activeContext) + .compactArrays(options.isCompactArrays()) + .ordered(options.isOrdered()) + .compact(filtered.build()); // 19.1. if (JsonUtils.isEmptyArray(compactedResults)) { compactedResults = JsonValue.EMPTY_JSON_OBJECT; - // 19.2. + // 19.2. } else if (JsonUtils.isArray(compactedResults)) { compactedResults = JsonProvider.instance().createObjectBuilder() - .add(graphKey, compactedResults).build(); + .add(graphKey, compactedResults).build(); } @@ -223,7 +218,7 @@ public static final JsonObject frame(final Document input, final Document frame, if (options.isOmitGraph() == null) { - omitGraph = activeContext.inMode(JsonLdVersion.V1_1); + omitGraph = activeContext.runtime().isV11(); } else { omitGraph = options.isOmitGraph(); @@ -234,14 +229,12 @@ public static final JsonObject frame(final Document input, final Document frame, if (compactedResults.asJsonObject().isEmpty()) { compactedResults = JsonProvider.instance().createObjectBuilder().add(graphKey, - JsonValue.EMPTY_JSON_ARRAY - ).build(); + JsonValue.EMPTY_JSON_ARRAY).build(); } else { compactedResults = JsonProvider.instance().createObjectBuilder().add(graphKey, - JsonProvider.instance().createArrayBuilder().add(compactedResults) - ).build(); + JsonProvider.instance().createArrayBuilder().add(compactedResults)).build(); } } @@ -306,7 +299,7 @@ private static final JsonValue removePreserve(JsonValue value) { private static final JsonValue replaceNull(JsonValue value) { - if (JsonUtils.isString(value) && Keywords.NULL.equals(((JsonString)value).getString())) { + if (JsonUtils.isString(value) && Keywords.NULL.equals(((JsonString) value).getString())) { return JsonValue.NULL; } else if (JsonUtils.isScalar(value)) { @@ -351,9 +344,9 @@ private static final JsonValue removeBlankIdKey(JsonValue value, List bl if (Keywords.ID.equals(entry.getKey()) && JsonUtils.isString(entry.getValue()) - && blankNodes.contains(((JsonString)entry.getValue()).getString())) { + && blankNodes.contains(((JsonString) entry.getValue()).getString())) { - continue; + continue; } object.add(entry.getKey(), removeBlankIdKey(entry.getValue(), blankNodes)); @@ -375,9 +368,9 @@ private static final void findBlankNodes(JsonValue value, final Map 0); - blankNodes.put(((JsonString)value).getString(), ++count); + if (BlankNode.isWellFormed(((JsonString) value).getString())) { + Integer count = blankNodes.computeIfAbsent(((JsonString) value).getString(), x -> 0); + blankNodes.put(((JsonString) value).getString(), ++count); } return; diff --git a/src/main/java/com/apicatalog/jsonld/processor/ProcessingRuntime.java b/src/main/java/com/apicatalog/jsonld/processor/ProcessingRuntime.java new file mode 100644 index 00000000..451e7dda --- /dev/null +++ b/src/main/java/com/apicatalog/jsonld/processor/ProcessingRuntime.java @@ -0,0 +1,82 @@ +package com.apicatalog.jsonld.processor; + +import com.apicatalog.jsonld.JsonLdError; +import com.apicatalog.jsonld.JsonLdOptions; +import com.apicatalog.jsonld.JsonLdVersion; +import com.apicatalog.jsonld.context.cache.Cache; +import com.apicatalog.jsonld.document.Document; +import com.apicatalog.jsonld.loader.DocumentLoader; + +import jakarta.json.JsonValue; + +/** + * A runtime context used during a transformation processing. + * + * @since 1.4.0 + */ +public class ProcessingRuntime { + + protected final JsonLdOptions options; + + protected ProcessingRuntime(JsonLdOptions options) { + this.options = options; + } + + public static ProcessingRuntime of(JsonLdOptions options) { + return options.getTimeout() != null + ? new Ticker(options) + : new ProcessingRuntime(options); + } + + /** + * Called in multiple places during a processing to check processing timeout if + * set. Does nothing if timeout is not set. + * + * When hit for the first time a timestamp is set, otherwise a duration is + * decreased by timestamps difference. + * + * @throws JsonLdError if a processing has exceeded + */ + public void tick() throws JsonLdError {/* NOP does nothing if timeout is not set */} + + /** + * Resume ticker, a next ping decreases remaining time if timeout is set. Is + * used after an external method call, to exclude time consumed by + * the external call. e.g. when calling HTTP client. + * + * Does nothing if timeout is not set. + */ + public void resetTicker() {/* NOP does nothing if timeout is not set */} + + public boolean isUriValidation() { + return options.isUriValidation(); + } + + public boolean isV10() { + return options.getProcessingMode() != null && options.getProcessingMode().equals(JsonLdVersion.V1_0); + } + + public boolean isV11() { + return options.getProcessingMode() != null && options.getProcessingMode().equals(JsonLdVersion.V1_1); + } + + public DocumentLoader getDocumentLoader() { + return options.getDocumentLoader(); + } + + public Cache getContextCache() { + return options.getContextCache(); + } + + public Cache getDocumentCache() { + return options.getDocumentCache(); + } + + public boolean isRdfStar() { + return options.isRdfStar(); + } + + public boolean isNumericId() { + return options.isNumericId(); + } +} diff --git a/src/main/java/com/apicatalog/jsonld/processor/Ticker.java b/src/main/java/com/apicatalog/jsonld/processor/Ticker.java new file mode 100644 index 00000000..34d17c39 --- /dev/null +++ b/src/main/java/com/apicatalog/jsonld/processor/Ticker.java @@ -0,0 +1,38 @@ +package com.apicatalog.jsonld.processor; + +import java.time.Duration; +import java.time.Instant; + +import com.apicatalog.jsonld.JsonLdError; +import com.apicatalog.jsonld.JsonLdErrorCode; +import com.apicatalog.jsonld.JsonLdOptions; + +class Ticker extends ProcessingRuntime { + + Instant ticker; + Duration ttl; + + Ticker(JsonLdOptions options) { + super(options); + this.ttl = options.getTimeout(); + this.ticker = Instant.now(); + } + + @Override + public void tick() throws JsonLdError { + + final Instant now = Instant.now(); + + ttl = ttl.minus(Duration.between(now, ticker).abs()); + + if (ttl.isNegative()) { + throw new JsonLdError(JsonLdErrorCode.PROCESSING_TIMEOUT_EXCEEDED); + } + ticker = now; + } + + @Override + public void resetTicker() { + ticker = Instant.now(); + } +}