From 014407c379ee0046224137e3c3d10787a0a01f8c Mon Sep 17 00:00:00 2001 From: Ken Wenzel Date: Fri, 17 Jan 2025 10:53:32 +0100 Subject: [PATCH] Introduce to control resolution of references. --- .../linkedfactory/core/rdf4j/aas/AAS.java | 63 ++++++------------- .../aas/query/AasEvaluationStrategy.java | 40 ++++++++---- .../core/rdf4j/aas/AasServiceTest.java | 6 +- 3 files changed, 50 insertions(+), 59 deletions(-) diff --git a/bundles/io.github.linkedfactory.core/src/main/java/io/github/linkedfactory/core/rdf4j/aas/AAS.java b/bundles/io.github.linkedfactory.core/src/main/java/io/github/linkedfactory/core/rdf4j/aas/AAS.java index 4afedca..057547a 100644 --- a/bundles/io.github.linkedfactory.core/src/main/java/io/github/linkedfactory/core/rdf4j/aas/AAS.java +++ b/bundles/io.github.linkedfactory.core/src/main/java/io/github/linkedfactory/core/rdf4j/aas/AAS.java @@ -22,49 +22,33 @@ public interface AAS { IRI API_SHELLS = SimpleValueFactory.getInstance().createIRI("aas-api:shells"); IRI API_SUBMODELS = SimpleValueFactory.getInstance().createIRI("aas-api:submodels"); + IRI API_RESOLVED = SimpleValueFactory.getInstance().createIRI("aas-api:resolved"); + String ASSETADMINISTRATIONSHELL_PREFIX = "urn:aas:AssetAdministrationShell:"; String SUBMODEL_PREFIX = "urn:aas:Submodel:"; - static Value toRdfValue(Object value, ValueFactory vf) { + static Value resolveReference(Object value, ValueFactory vf) { if (value instanceof Record) { Record r = (Record) value; - Record id = r.first(URIs.createURI(AAS_NAMESPACE + "id")); String type = null; String idStr = null; - if (id != Record.NULL) { - idStr = id.getValue() != null ? id.getValue().toString() : null; + URI keysProperty = URIs.createURI(AAS_NAMESPACE + "keys"); + Record keys = r.first(keysProperty); + Record firstKey = null; + // there is exactly one aas:keys element + if (keys.getValue() instanceof Record && keys.next().first(keysProperty).getValue() == null) { + firstKey = (Record) keys.getValue(); + } else if (keys.getValue() instanceof List && ((List) keys.getValue()).size() == 1) { + firstKey = (Record) ((List) keys.getValue()).get(0); } - if (idStr == null) { - URI keysProperty = URIs.createURI(AAS_NAMESPACE + "keys"); - Record keys = r.first(keysProperty); - Record firstKey = null; - // there is exactly one aas:keys element - if (keys.getValue() instanceof Record && keys.next().first(keysProperty).getValue() == null) { - firstKey = (Record) keys.getValue(); - } else if (keys.getValue() instanceof List && ((List) keys.getValue()).size() == 1) { - firstKey = (Record) ((List) keys.getValue()).get(0); - } - if (firstKey != null) { - Object typeValue = firstKey.first(URIs.createURI(AAS_NAMESPACE + "type")).getValue(); - if (typeValue != null) { - type = typeValue.toString(); - } - Object keyValue = firstKey.first(URIs.createURI(AAS_NAMESPACE + "value")).getValue(); - if (keyValue != null) { - idStr = keyValue.toString(); - } + if (firstKey != null) { + Object typeValue = firstKey.first(URIs.createURI(AAS_NAMESPACE + "type")).getValue(); + if (typeValue != null) { + type = typeValue.toString(); } - - if (idStr != null) { - switch (type) { - case "Submodel": - return vf.createIRI("urn:aas:" + type + ":" + - Base64.getEncoder().encodeToString(idStr.getBytes(StandardCharsets.UTF_8))); - default: - // do not convert the reference to an IRI - // drop id string - idStr = null; - } + Object keyValue = firstKey.first(URIs.createURI(AAS_NAMESPACE + "value")).getValue(); + if (keyValue != null) { + idStr = keyValue.toString(); } } @@ -73,13 +57,6 @@ static Value toRdfValue(Object value, ValueFactory vf) { Object modelTypeValue = r.first(URIs.createURI(AAS_NAMESPACE + "modelType")).getValue(); if (modelTypeValue != null) { type = modelTypeValue.toString(); - } else { - Object kindValue = r.first(URIs.createURI(AAS_NAMESPACE + "kind")).getValue(); - if ("Instance".equals(kindValue)) { - type = "Submodel"; - } else if ("Template".equals(kindValue)) { - type = "Template"; - } } } @@ -87,12 +64,12 @@ static Value toRdfValue(Object value, ValueFactory vf) { case "Submodel": String iriStr = "urn:aas:" + type + ":" + Base64.getEncoder().encodeToString(idStr.getBytes(StandardCharsets.UTF_8)); - return IRIWithValue.create(iriStr, value); + return vf.createIRI(iriStr); default: // do not convert the reference to an IRI } } } - return Conversions.toRdfValue(value, vf, true); + return null; } } diff --git a/bundles/io.github.linkedfactory.core/src/main/java/io/github/linkedfactory/core/rdf4j/aas/query/AasEvaluationStrategy.java b/bundles/io.github.linkedfactory.core/src/main/java/io/github/linkedfactory/core/rdf4j/aas/query/AasEvaluationStrategy.java index 5598f5c..a4abe71 100644 --- a/bundles/io.github.linkedfactory.core/src/main/java/io/github/linkedfactory/core/rdf4j/aas/query/AasEvaluationStrategy.java +++ b/bundles/io.github.linkedfactory.core/src/main/java/io/github/linkedfactory/core/rdf4j/aas/query/AasEvaluationStrategy.java @@ -3,6 +3,7 @@ import io.github.linkedfactory.core.kvin.Record; import io.github.linkedfactory.core.rdf4j.aas.AAS; import io.github.linkedfactory.core.rdf4j.aas.AasClient; +import io.github.linkedfactory.core.rdf4j.common.Conversions; import io.github.linkedfactory.core.rdf4j.common.HasValue; import io.github.linkedfactory.core.rdf4j.common.query.CompositeBindingSet; import io.github.linkedfactory.core.rdf4j.common.query.InnerJoinIteratorEvaluationStep; @@ -25,9 +26,9 @@ import org.eclipse.rdf4j.query.algebra.evaluation.QueryBindingSet; import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep; import org.eclipse.rdf4j.query.algebra.evaluation.federation.FederatedServiceResolver; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.DefaultEvaluationStrategy; import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext.Minimal; -import org.eclipse.rdf4j.query.algebra.evaluation.impl.StrictEvaluationStrategy; import org.eclipse.rdf4j.query.algebra.evaluation.iterator.HashJoinIteration; import java.io.IOException; @@ -39,7 +40,7 @@ import static io.github.linkedfactory.core.rdf4j.common.query.Helpers.compareAndBind; import static io.github.linkedfactory.core.rdf4j.common.query.Helpers.findFirstFetch; -public class AasEvaluationStrategy extends StrictEvaluationStrategy { +public class AasEvaluationStrategy extends DefaultEvaluationStrategy { final AasClient client; final ParameterScanner scanner; @@ -61,7 +62,7 @@ public CloseableIteration evaluate(Stateme throws QueryEvaluationException { // System.out.println("Stmt: " + stmt); final Var subjectVar = stmt.getSubjectVar(); - final Value subjectValue = StrictEvaluationStrategy.getVarValue(subjectVar, bs); + final Value subjectValue = getVarValue(subjectVar, bs); if (subjectValue == null) { // this happens for patterns like (:subject :property [ ?someValue ]) @@ -74,7 +75,22 @@ public CloseableIteration evaluate(Stateme Object data = subjectValue instanceof HasValue ? ((HasValue) subjectValue).getValue() : valueCache.get(subjectValue); if (data instanceof Record) { - Value predValue = StrictEvaluationStrategy.getVarValue(stmt.getPredicateVar(), bs); + Var objectVar = stmt.getObjectVar(); + Value objectValue = getVarValue(objectVar, bs); + + final Value predValue = getVarValue(stmt.getPredicateVar(), bs); + if (AAS.API_RESOLVED.equals(predValue)) { + Value resolved = AAS.resolveReference(data, vf); + if (resolved == null || objectValue != null && !objectValue.equals(resolved)) { + return new EmptyIteration<>(); + } + CompositeBindingSet newBs = new CompositeBindingSet(bs); + if (objectValue == null) { + newBs.addBinding(objectVar.getName(), resolved); + } + return new SingletonIteration<>(newBs); + } + final Iterator it; if (predValue != null) { String predValueStr = predValue.stringValue(); @@ -84,8 +100,6 @@ public CloseableIteration evaluate(Stateme it = ((Record) data).iterator(); } - Var variable = stmt.getObjectVar(); - Value objectValue = StrictEvaluationStrategy.getVarValue(variable, bs); return new AbstractCloseableIteration<>() { CompositeBindingSet next; @@ -104,7 +118,7 @@ public boolean hasNext() throws QueryEvaluationException { newBs.addBinding(stmt.getPredicateVar().getName(), toRdfValue(r.getProperty())); } if (objectValue == null) { - newBs.addBinding(variable.getName(), newObjectValue); + newBs.addBinding(objectVar.getName(), newObjectValue); } next = newBs; } @@ -132,10 +146,10 @@ public void remove() throws QueryEvaluationException { } else if (data instanceof Object[] || data instanceof List) { List list = data instanceof Object[] ? Arrays.asList((Object[]) data) : (List) data; Var predVar = stmt.getPredicateVar(); - Value predValue = StrictEvaluationStrategy.getVarValue(predVar, bs); + Value predValue = getVarValue(predVar, bs); if (predValue == null) { Iterator it = list.iterator(); - Value objValue = StrictEvaluationStrategy.getVarValue(stmt.getObjectVar(), bs); + Value objValue = getVarValue(stmt.getObjectVar(), bs); return new AbstractCloseableIteration<>() { BindingSet next = null; int i = 0; @@ -176,7 +190,7 @@ public void remove() throws QueryEvaluationException { if (localName.matches("_[0-9]+")) { int index = Integer.parseInt(localName.substring(1)); if (index > 0 && index <= list.size()) { - return compareAndBind(bs, stmt.getObjectVar(), AAS.toRdfValue(list.get(index - 1), vf)); + return compareAndBind(bs, stmt.getObjectVar(), toRdfValue(list.get(index - 1))); } } } @@ -226,8 +240,8 @@ public CloseableIteration evaluateFetch(Bi final Var predVar = stmt.getPredicateVar(); final Var objectVar = stmt.getObjectVar(); - final Value subjValue = StrictEvaluationStrategy.getVarValue(stmt.getSubjectVar(), bs); - final Value predValue = StrictEvaluationStrategy.getVarValue(predVar, bs); + final Value subjValue = getVarValue(stmt.getSubjectVar(), bs); + final Value predValue = getVarValue(predVar, bs); if (subjValue != null) { final CloseableIteration iteration = new AbstractCloseableIteration<>() { @@ -373,7 +387,7 @@ public ValueFactory getValueFactory() { } public Value toRdfValue(Object value) { - Value rdfValue = AAS.toRdfValue(value, getValueFactory()); + Value rdfValue = Conversions.toRdfValue(value, getValueFactory(), true); if (rdfValue instanceof HasValue) { valueCache.putIfAbsent(rdfValue, ((HasValue) rdfValue).getValue()); } diff --git a/bundles/io.github.linkedfactory.core/src/test/java/io/github/linkedfactory/core/rdf4j/aas/AasServiceTest.java b/bundles/io.github.linkedfactory.core/src/test/java/io/github/linkedfactory/core/rdf4j/aas/AasServiceTest.java index 4f65898..208cf50 100644 --- a/bundles/io.github.linkedfactory.core/src/test/java/io/github/linkedfactory/core/rdf4j/aas/AasServiceTest.java +++ b/bundles/io.github.linkedfactory.core/src/test/java/io/github/linkedfactory/core/rdf4j/aas/AasServiceTest.java @@ -64,12 +64,12 @@ public FederatedService createService(String url) { "service {" + " ?shell ." + " ?shell aas:idShort ?shellId ." + - " ?shell aas:submodels ?sm." + - " ?sm aas:semanticId/aas:keys/aas:value \"https://admin-shell.io/ZVEI/TechnicalData/Submodel/1/2\"." + + " ?shell aas:submodels [ ?sm ] ." + + " ?sm aas:semanticId/aas:keys/aas:value \"https://admin-shell.io/ZVEI/TechnicalData/Submodel/1/2\" ." + " ?sm (!<:>)+ ?element ." + " { ?element a aas:Property } union { ?element a aas:MultiLanguageProperty }\n" + " ?element aas:idShort \"ProductClassId\" ; " + - " aas:valueId/aas:keys/aas:value ?productClassId." + + " aas:valueId/aas:keys/aas:value ?productClassId ." + "}" + "}"); try (var result = query.evaluate()) {