Skip to content
This repository was archived by the owner on Feb 15, 2024. It is now read-only.

Commit 4593897

Browse files
author
Matthias Böckmann
committed
Not yet working on collections of blank nodes, as the group_concat fails
1 parent f2ff551 commit 4593897

2 files changed

Lines changed: 87 additions & 19 deletions

File tree

  • dataformat-jsonld/src

dataformat-jsonld/src/main/java/io/adminshell/aas/v3/dataformat/jsonld/Parser.java

Lines changed: 83 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
import java.lang.reflect.*;
2323
import java.math.BigDecimal;
2424
import java.math.BigInteger;
25+
import java.net.MalformedURLException;
2526
import java.net.URI;
2627
import java.net.URISyntaxException;
28+
import java.net.URL;
2729
import java.text.ParseException;
2830
import java.text.SimpleDateFormat;
2931
import java.time.ZonedDateTime;
@@ -39,6 +41,8 @@ class Parser {
3941

4042
private final Logger logger = LoggerFactory.getLogger(Parser.class);
4143

44+
private static final URI blankNodeIdPropertyUri = URI.create("https://admin-shell.io/aas/blankNodeId");
45+
4246
static Map<String, String> knownNamespaces = new HashMap<>();
4347

4448
/**
@@ -55,20 +59,30 @@ private <T> T handleObject(Model inputModel, String objectUri, Class<T> targetCl
5559

5660
//if(!targetClass.getSimpleName().endsWith("Impl")) //This would not work for "TypedLiteral", "RdfResource" and so on
5761
//Check whether we are dealing with an instantiable class (i.e. no interface and no abstract class)
62+
boolean currentObjectIsBlankNode = false;
5863
if (targetClass.isInterface() || Modifier.isAbstract(targetClass.getModifiers())) {
5964
//We don't know the desired class yet (current targetClass is not instantiable). This is only known for the root object
6065
ArrayList<Class<?>> implementingClasses = getImplementingClasses(targetClass);
61-
66+
String queryString;
6267
//Get a list of all "rdf:type" statements in our model
6368
//In case of a blank node, the "object URI" will just be a string and no valid URI. In that case, we need a different query syntax
6469
try {
65-
new URI(objectUri).toURL();
70+
new URL(objectUri);
6671
}
67-
catch (URISyntaxException | IllegalArgumentException e)
72+
catch (MalformedURLException e)
6873
{
69-
e.printStackTrace();
74+
currentObjectIsBlankNode = true;
75+
}
76+
if(currentObjectIsBlankNode)
77+
{
78+
//Object is a blank node, so the subject URI cannot be used
79+
queryString = "SELECT ?type { ?s <" + blankNodeIdPropertyUri + "> \"" + objectUri + "\" ; a ?type . }";
80+
}
81+
else
82+
{
83+
//Not a blank node, so we can work with the subject URI
84+
queryString = "SELECT ?type { BIND(<" + objectUri + "> AS ?s). ?s a ?type . }";
7085
}
71-
String queryString = "SELECT ?type { BIND(<" + objectUri + "> AS ?s). ?s a ?type . }";
7286
Query query = QueryFactory.create(queryString);
7387
QueryExecution queryExecution = QueryExecutionFactory.create(query, inputModel);
7488
ResultSet resultSet = queryExecution.execSelect();
@@ -215,9 +229,19 @@ private <T> T handleObject(Model inputModel, String objectUri, Class<T> targetCl
215229
//Start the "WHERE" part - Fuseki does not expect the "WHERE" keyword, but just an "{"
216230
queryStringBuilder.append(" { ");
217231

232+
//In case of blank nodes, we can't work with the subject URI
233+
if(currentObjectIsBlankNode)
234+
{
235+
queryStringBuilder.append("?s <").append(blankNodeIdPropertyUri).append("> \"").append(objectUri).append("\" ;");
236+
}
237+
else
238+
{
239+
queryStringBuilder.append(" <").append(objectUri).append(">");
240+
}
241+
218242
//Make sure that the object is of the correct type
219243
//This is particularly relevant in case of all fields being optional -- then one could simply parse a random object
220-
queryStringBuilder.append(" <").append(objectUri).append("> a ").append(wrapIfUri(targetClass.getAnnotation(IRI.class).value()[0])).append(". ");
244+
queryStringBuilder.append(" a ").append(wrapIfUri(targetClass.getAnnotation(IRI.class).value()[0])).append(". ");
221245

222246
for (Map.Entry<String, Method> entry : methodMap.entrySet()) {
223247
//Is this a field which is annotated by NOT NULL?
@@ -229,7 +253,13 @@ private <T> T handleObject(Model inputModel, String objectUri, Class<T> targetCl
229253
//In AAS, every field is optional, as there are no validation annotations in the model
230254
queryStringBuilder.append(" OPTIONAL {");
231255

232-
queryStringBuilder.append(" <").append(objectUri).append("> "); //subject, as passed to the function
256+
if(currentObjectIsBlankNode)
257+
{
258+
queryStringBuilder.append(" ?s ");
259+
}
260+
else {
261+
queryStringBuilder.append(" <").append(objectUri).append("> "); //subject, as passed to the function
262+
}
233263
//For the field, get the JsonAlias annotation (present for all classes generated by the CodeGen tool)
234264
//Find the annotation value containing a colon and interpret this as "prefix:predicate"
235265
boolean foundAnnotation = false;
@@ -276,13 +306,6 @@ private <T> T handleObject(Model inputModel, String objectUri, Class<T> targetCl
276306
queryForOtherProperties.append(" <").append(entry.getValue()).append(">\n");
277307
}
278308

279-
for (Map.Entry<String, String> entry : knownNamespaces.entrySet()) {
280-
queryForOtherProperties.append("PREFIX ").append(entry.getKey());
281-
if (!entry.getKey().endsWith(":")) {
282-
queryForOtherProperties.append(":");
283-
}
284-
queryForOtherProperties.append(" <").append(entry.getValue()).append(">\n");
285-
}
286309

287310
//Respect ALL properties and values
288311
queryForOtherProperties.append(" SELECT ?p ?o { <").append(objectUri).append("> ?p ?o .\n");
@@ -442,13 +465,48 @@ private <T> T handleObject(Model inputModel, String objectUri, Class<T> targetCl
442465
if (Collection.class.isAssignableFrom(currentType)) {
443466
sparqlParameterName += "s"; //plural form for the concatenated values
444467
}
445-
if (querySolution.contains(sparqlParameterName)) {
468+
querySolution.varNames().forEachRemaining(System.out::println);
469+
System.out.println(querySolution.contains(sparqlParameterName));
470+
if (querySolution.contains(sparqlParameterName)) { //TODO why is this false for keys?! Answer: Because you can't do a GROUP_CONCAT on blank nodes
446471
String currentSparqlBinding = querySolution.get(sparqlParameterName).toString();
472+
447473
boolean objectIsBlankNode = querySolution.get(sparqlParameterName).isResource() && querySolution.get(sparqlParameterName).asNode().isBlank();
448-
System.out.println(currentSparqlBinding + " is blank node? " + objectIsBlankNode);
474+
String blankNodeId = "";
475+
//If the object is a blank node, we will struggle to make follow-up queries starting at the blank node as subject
476+
//For that case, we add some artificial identifiers here
477+
if(objectIsBlankNode) {
478+
blankNodeId = querySolution.get(sparqlParameterName).asNode().getBlankNodeId().toString();
479+
inputModel.add(
480+
ResourceFactory.createStatement(
481+
querySolution.get(sparqlParameterName).asResource(), //subject: the blank node
482+
ResourceFactory.createProperty(blankNodeIdPropertyUri.toString()), //some artificial property
483+
ResourceFactory.createStringLiteral(blankNodeId))); //object: the ID of the blank node
484+
}
449485

450486
if (currentType.isEnum()) {
451-
entry.getValue().invoke(returnObject, handleEnum(currentType, currentSparqlBinding));
487+
//Two possibilities:
488+
//1: The URI of the enum value is given directly e.g. ?s ?p <someUri>
489+
//2: The URI of the enum value is encapsulated in a blank node, e.g.
490+
// ?s ?p [ a demo:myEnum, demo:enumValue ]
491+
if(objectIsBlankNode)
492+
{
493+
Query innerEnumQuery = QueryFactory.create("SELECT ?type { ?s <" + blankNodeIdPropertyUri + "> + \"" + blankNodeId + "\" ; a ?type } ");
494+
QueryExecution innerEnumQueryExecution = QueryExecutionFactory.create(innerEnumQuery, inputModel);
495+
ResultSet innerEnumQueryExecutionResultSet = innerEnumQueryExecution.execSelect();
496+
while(innerEnumQueryExecutionResultSet.hasNext())
497+
{
498+
try {
499+
entry.getValue().invoke(returnObject, handleEnum(currentType, innerEnumQueryExecutionResultSet.next().get("type").toString()));
500+
break; //Stop after the first successful execution
501+
}
502+
catch (IOException ignored) //There might be errors, if multiple types are present, see example above
503+
{}
504+
}
505+
innerEnumQueryExecution.close();
506+
}
507+
else {
508+
entry.getValue().invoke(returnObject, handleEnum(currentType, currentSparqlBinding));
509+
}
452510
continue;
453511
}
454512

@@ -527,7 +585,13 @@ private <T> T handleObject(Model inputModel, String objectUri, Class<T> targetCl
527585

528586
} else {
529587
//Not a primitive object, but a complex sub-object. Recursively call this function to handle it
530-
entry.getValue().invoke(returnObject, handleObject(inputModel, currentSparqlBinding, entry.getValue().getParameterTypes()[0]));
588+
if (objectIsBlankNode) {
589+
System.out.println("Handling sub-object as blank node");
590+
entry.getValue().invoke(returnObject, handleObject(inputModel, blankNodeId, entry.getValue().getParameterTypes()[0]));
591+
} else {
592+
593+
entry.getValue().invoke(returnObject, handleObject(inputModel, currentSparqlBinding, entry.getValue().getParameterTypes()[0]));
594+
}
531595
}
532596
}
533597
}
@@ -690,7 +754,7 @@ private <T> T handleEnum(Class<T> enumClass, String url) throws IOException {
690754
url = url.substring(url.lastIndexOf("/") + 1);
691755
}
692756
for (T constant : constants) {
693-
if (url.equals(constant.toString())) {
757+
if (url.equalsIgnoreCase(constant.toString())) {
694758
return constant;
695759
}
696760
}

dataformat-jsonld/src/test/java/io/adminshell/aas/v3/model/dataformat/jsonld/ParserTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ public void parseAllSchemaExamplesTest() throws IOException, DeserializationExce
6262
}
6363
}
6464
Assert.assertEquals(0, errorCtr);
65+
66+
67+
AssetAdministrationShell aas = serializer.deserialize(SerializerUtil.readResourceToString("AAS_Reference_shortExample.ttl"), AssetAdministrationShell.class);
68+
System.out.println(serializer.serialize(aas));
6569
}
6670

6771
}

0 commit comments

Comments
 (0)