Skip to content

Commit

Permalink
[Enhancement #43] Properly handle term mapping conflicts in contexts.
Browse files Browse the repository at this point in the history
  • Loading branch information
ledsoft committed Feb 7, 2023
1 parent 1842067 commit ac25191
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import cz.cvut.kbss.jsonld.ConfigParam;
import cz.cvut.kbss.jsonld.Configuration;
import cz.cvut.kbss.jsonld.JsonLd;
import cz.cvut.kbss.jsonld.exception.AmbiguousTermMappingException;
import cz.cvut.kbss.jsonld.serialization.context.JsonLdContext;
import cz.cvut.kbss.jsonld.serialization.context.JsonLdContextFactory;
import cz.cvut.kbss.jsonld.serialization.context.MappingJsonLdContextFactory;
import cz.cvut.kbss.jsonld.serialization.model.CollectionNode;
import cz.cvut.kbss.jsonld.serialization.model.CompositeNode;
import cz.cvut.kbss.jsonld.serialization.model.JsonNode;
import cz.cvut.kbss.jsonld.serialization.model.ObjectNode;
import cz.cvut.kbss.jsonld.serialization.serializer.LiteralValueSerializers;
Expand All @@ -24,6 +26,7 @@
import java.time.*;
import java.util.Collection;
import java.util.Date;
import java.util.Optional;

/**
* JSON-LD serializer outputting compacted JSON-LD with context.
Expand Down Expand Up @@ -77,10 +80,20 @@ protected JsonNode buildJsonTree(Object root) {
final JsonLdTreeBuilder treeBuilder = initTreeBuilder(traverser, jsonLdContextFactory);
traverser.setVisitor(treeBuilder);
traverser.traverse(root);
ensureContextNodeNotPresent(treeBuilder.getTreeRoot(), rootContext.getContextNode());
treeBuilder.getTreeRoot().prependItem(rootContext.getContextNode());
return treeBuilder.getTreeRoot();
}

private void ensureContextNodeNotPresent(CompositeNode<?> root, JsonNode rootCtx) {
final Optional<JsonNode> ctxNode =
root.getItems().stream().filter(n -> JsonLd.CONTEXT.equals(n.getName())).findAny();
if (ctxNode.isPresent()) {
throw new AmbiguousTermMappingException(
"Unable to build context hierarchy. Attempted to add two root contexts. Original root context: " + rootCtx + ", conflicting: " + ctxNode.get());
}
}

private JsonLdTreeBuilder initTreeBuilder(ObjectGraphTraverser traverser,
JsonLdContextFactory jsonLdContextFactory) {
return new JsonLdTreeBuilder(new ObjectGraphValueSerializers(serializers,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,9 @@ boolean hasTermMapping(String term, JsonNode mappedNode) {
boolean isEmpty() {
return true;
}

@Override
boolean isRoot() {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cz.cvut.kbss.jsonld.serialization.context;

import cz.cvut.kbss.jsonld.exception.AmbiguousTermMappingException;
import cz.cvut.kbss.jsonld.serialization.model.JsonNode;

import java.util.*;
Expand Down Expand Up @@ -28,14 +29,21 @@ void registerTermMapping(String term, JsonNode mappedNode) {
if (!isRoot() && !parentContext.hasTermMapping(term)) {
parentContext.registerTermMapping(term, mappedNode);
} else {
verifyMappingUnique(term, mappedNode);
mapping.put(term, mappedNode);
}
}

private boolean isRoot() {
boolean isRoot() {
return parentContext == DummyTermMappingHolder.INSTANCE;
}

private void verifyMappingUnique(String term, JsonNode value) {
if (mapping.containsKey(term) && !Objects.equals(mapping.get(term), value)) {
throw new AmbiguousTermMappingException("Context already contains mapping for term '" + term + "'.");
}
}

@Override
public Map<String, JsonNode> getMapping() {
return Collections.unmodifiableMap(mapping);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ public MappingJsonLdContext(JsonLdContext parent) {
@Override
public void registerTermMapping(String term, String iri) {
final JsonNode value = JsonNodeFactory.createStringLiteralNode(term, iri);
if (!mappingHolder.canRegisterTermMapping(term, value)) {
if (!mappingHolder.canRegisterTermMapping(term, value) && !mappingHolder.isRoot()) {
this.mappingHolder = new EmbeddedTermMappingHolder(mappingHolder);
}
mappingHolder.registerTermMapping(term, value);
}

@Override
public void registerTermMapping(String term, ObjectNode mappedNode) {
if (!mappingHolder.canRegisterTermMapping(term, mappedNode)) {
if (!mappingHolder.canRegisterTermMapping(term, mappedNode) && !mappingHolder.isRoot()) {
this.mappingHolder = new EmbeddedTermMappingHolder(mappingHolder);
}
mappingHolder.registerTermMapping(term, mappedNode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ abstract class TermMappingHolder {

abstract boolean isEmpty();

abstract boolean isRoot();

public Optional<String> getMappedTerm(String iri) {
Objects.requireNonNull(iri);
for (Map.Entry<String, JsonNode> e : getMapping().entrySet()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,9 @@ Map<String, JsonNode> getMapping() {
boolean isEmpty() {
return true;
}

@Override
boolean isRoot() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package cz.cvut.kbss.jsonld.serialization.context;

import cz.cvut.kbss.jopa.vocabulary.DC;
import cz.cvut.kbss.jopa.vocabulary.RDFS;
import cz.cvut.kbss.jsonld.exception.AmbiguousTermMappingException;
import cz.cvut.kbss.jsonld.serialization.JsonNodeFactory;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class EmbeddedTermMappingHolderTest {

private final EmbeddedTermMappingHolder sut = new EmbeddedTermMappingHolder();

@Test
void registerTermMappingThrowsAmbiguousTermMappingExceptionWhenRootHolderAlreadyContainsTermMapping() {
final String term = "name";
sut.registerTermMapping(term, JsonNodeFactory.createStringLiteralNode(term, RDFS.LABEL));
assertThrows(AmbiguousTermMappingException.class,
() -> sut.registerTermMapping(term,
JsonNodeFactory.createStringLiteralNode(term, DC.Terms.TITLE)));
}

}

0 comments on commit ac25191

Please sign in to comment.