diff --git a/.github/gradle.yml b/.github/workflows/gradle.yml similarity index 58% rename from .github/gradle.yml rename to .github/workflows/gradle.yml index 53d7608..07c7f09 100644 --- a/.github/gradle.yml +++ b/.github/workflows/gradle.yml @@ -8,7 +8,14 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + - name: Setup Java + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 - name: Build with Gradle run: ./gradlew build + - name: Run tests with Gradle + run: ./gradlew test \ No newline at end of file diff --git a/src/main/java/fi/csc/mscr/tranformation/rml/RMLGenerator.java b/src/main/java/fi/csc/mscr/tranformation/rml/RMLGenerator.java index caa323d..70ad80b 100644 --- a/src/main/java/fi/csc/mscr/tranformation/rml/RMLGenerator.java +++ b/src/main/java/fi/csc/mscr/tranformation/rml/RMLGenerator.java @@ -13,6 +13,7 @@ import org.topbraid.shacl.vocabulary.SH; import java.io.InputStream; +import java.util.Optional; //import fi.vm.yti.datamodel.api.v2.dto.MSCR; @@ -25,26 +26,97 @@ public class RMLGenerator { String nsFNML = "http://semweb.mmlab.be/ns/fnml#"; String nsGREL = "http://users.ugent.be/~bjdmeest/function/grel.ttl#"; + enum ReferenceFormulation { + JSONPath, + XPath, + CSV + } + public String testMe() { return "test"; } - private String getSourceIteratorForTargetShape(Model model, String targetShapeUri) throws Exception { + private Optional getSubjectMapTemplate(Model model, String mappingIri) { + String q = String.format(""" + PREFIX rdf: + PREFIX : + + select ?template + where { + ?mappingURI a :Mapping . + ?mappingURI :target/rdf:_1 ?target . + ?target :uri . + + ?mappingURI :processing ?proc . + ?proc ?p ?o . + ?o rdf:_2 ?oo. # TODO: is this general? + ?oo :value ?template + + } + + """, mappingIri); + + QueryExecution qe = QueryExecutionFactory.create(q, model); + ResultSet results = qe.execSelect(); + + if (results.hasNext()) { + QuerySolution res = results.next(); + return Optional.of(res.getLiteral("template").toString()); + } else { + return Optional.empty(); + } + + } + + private Optional getSourceSchema(Model model, String crosswalkIri) { + + String q = String.format(""" + PREFIX rdf: + PREFIX : + + select ?format where { + <%s> a :Crosswalk ; + :sourceSchema ?sourceIri . + + ?sourceIri :format ?format + } + """, crosswalkIri); + + QueryExecution qe = QueryExecutionFactory.create(q, model); + ResultSet results = qe.execSelect(); + + if (results.hasNext()) { + QuerySolution res = results.next(); + var ref = res.getLiteral("format"); + + switch (ref.toString()) { + case "JSONSCHEMA": return Optional.of(ReferenceFormulation.JSONPath); + case "XSD": return Optional.of(ReferenceFormulation.XPath); + case "CSV": return Optional.of(ReferenceFormulation.CSV); + + default: throw new Error("No matching reference formulation found for " + ref); + } + } else { + return Optional.empty(); + } + + } + + private Optional getSourceIteratorForTargetShape(Model model, String targetShapeUri) { String q = String.format(""" PREFIX rdf: PREFIX : select ?iterator where { - ?mappingURI a . + ?mappingURI a :Mapping . ?mappingURI :target/rdf:_1 ?target . ?target :uri <%s> . ?mappingURI :source/rdf:_1 ?source . ?source :uri ?sourceShapeId . - ?sourceShapeId ?iterator . - + ?sourceShapeId :instancePath ?iterator . } """, targetShapeUri); @@ -54,14 +126,16 @@ private String getSourceIteratorForTargetShape(Model model, String targetShapeUr if (results.hasNext()) { QuerySolution res = results.next(); - return res.getLiteral("iterator").toString(); + return Optional.of(res.getLiteral("iterator").toString()); } else { - throw new Exception("Source iterator could not be found for " + targetShapeUri); + return Optional.empty(); } } - public Resource addLogicalSource(String logicalSourceURI, Model m) throws Exception { + public Resource addLogicalSource(String logicalSourceURI, Model inputModel, String targetShapeUri, String crosswalkIri) throws Exception { + + Model outputModel = ModelFactory.createDefaultModel(); /* You can now get the iterator source for a specific shape by querying along the lines: @@ -74,25 +148,48 @@ public Resource addLogicalSource(String logicalSourceURI, Model m) throws Except */ String iterator = ""; - Resource logicalSource = m.createResource(logicalSourceURI); + ReferenceFormulation refFormulation = null; + Resource logicalSource = outputModel.createResource(logicalSourceURI); + try { - iterator = getSourceIteratorForTargetShape(m, "iterator:https://shacl-play.sparna.fr/shapes/Person"); + var ref = this.getSourceSchema(inputModel, crosswalkIri); + refFormulation = ref.orElseThrow(Exception::new); } catch(Exception e) { - System.out.println(e.toString()); + System.out.println("Format could not be found"); throw e; } + + logicalSource.addProperty(RDF.type, outputModel.createResource(nsRML + "BaseSource")); + Resource referenceFormulation = outputModel.createResource(nsQL + refFormulation); + logicalSource.addProperty(outputModel.createProperty(nsRML + "referenceFormulation"), referenceFormulation); + //logicalSource.addProperty(m.createProperty(nsRML + "source"), m.createLiteral("data/person.json")); - logicalSource.addProperty(m.createProperty(nsRML + "iterator"), iterator); - - /*logicalSource.addProperty(RDF.type, m.createResource(nsRML + "BaseSource")); - Resource referenceFormulation = m.createResource(nsQL + "JSONPath"); - logicalSource.addProperty(m.createProperty(nsRML + "referenceFormulation"), referenceFormulation); - logicalSource.addProperty(m.createProperty(nsRML + "source"), m.createLiteral("data/person.json")); + try { + iterator = this.getSourceIteratorForTargetShape(inputModel, targetShapeUri).orElseThrow(Exception::new); + } catch(Exception e) { + System.out.println("Iterator could not be found"); + throw e; + } - Resource sourceProperty = inputModel.getResource(sourcePropertyURI); - Literal iteratorPath = sourceProperty.getProperty(MSCR.instancePath).getLiteral(); - logicalSource.addProperty(m.createProperty(nsRML + "iterator"), iteratorPath);*/ + logicalSource.addProperty(outputModel.createProperty(nsRML + "iterator"), iterator); return logicalSource; } + + public Model addSubjectMap(Model inputModel, String mappingIri) throws Exception { + + Model outputModel = ModelFactory.createDefaultModel(); + + Resource triplesMap = outputModel.createResource("#Mapping"); + + Optional template = getSubjectMapTemplate(inputModel, mappingIri); + + Resource subjMap = outputModel.createResource(); + subjMap.addProperty(outputModel.createProperty(nsRR + "template"), outputModel.createLiteral(template.orElseThrow(Exception::new))); + subjMap.addProperty(outputModel.createProperty(nsRR + "class"), outputModel.createResource("http://schema.org/Person")); // TODO: get this from target + + triplesMap.addProperty(outputModel.createProperty(nsRR + "subjectMap"), subjMap); + + return outputModel; + } } diff --git a/src/test/java/fi/csc/mscr/tranformation/rml/RMLGeneratorTest.java b/src/test/java/fi/csc/mscr/tranformation/rml/RMLGeneratorTest.java index 1a069ca..e4da203 100644 --- a/src/test/java/fi/csc/mscr/tranformation/rml/RMLGeneratorTest.java +++ b/src/test/java/fi/csc/mscr/tranformation/rml/RMLGeneratorTest.java @@ -5,43 +5,62 @@ import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.StmtIterator; import org.apache.jena.riot.RDFDataMgr; import org.junit.jupiter.api.Test; +import java.util.Arrays; +import java.util.List; class RMLGeneratorTest { - Model loadPersonTestData() { - Model model = ModelFactory.createDefaultModel(); - - String[] inputFileNames = {"src/test/resources/data/person_json2shacl/crosswalk_content.ttl", - "src/test/resources/data/person_json2shacl/crosswalk_metadata.ttl", - "src/test/resources/data/person_json2shacl/source_metadata.ttl", - "src/test/resources/data/person_json2shacl/source_content.ttl", - "src/test/resources/data/person_json2shacl/target_metadata.ttl", - "src/test/resources/data/person_json2shacl/target_content.ttl" - }; - - for (String inputFileName : inputFileNames) { - model.add(RDFDataMgr.loadModel(inputFileName)); - } - - return model; - } - - @Test - void test() { - RMLGenerator r = new RMLGenerator(); - assertEquals("test", r.testMe()); - } - - @Test - void testAddLogicalSource() throws Exception { - Model model = loadPersonTestData(); - RMLGenerator r = new RMLGenerator(); - Resource logicalSource = r.addLogicalSource("http://example.com/1", model); - - assertEquals("http://example.com/1", logicalSource.getURI()); + String[] personJsonTestData = {"src/test/resources/data/person_json2shacl/crosswalk_content.ttl", + "src/test/resources/data/person_json2shacl/crosswalk_metadata.ttl", + "src/test/resources/data/person_json2shacl/source_metadata.ttl", + "src/test/resources/data/person_json2shacl/source_content.ttl", + "src/test/resources/data/person_json2shacl/target_metadata.ttl", + "src/test/resources/data/person_json2shacl/target_content.ttl" + }; + + Model loadPersonTestData(List inputFileNames) { + Model model = ModelFactory.createDefaultModel(); + + inputFileNames.forEach(inputFileName -> model.add(RDFDataMgr.loadModel(inputFileName))); + + return model; + } + + @Test + void test() { + RMLGenerator r = new RMLGenerator(); + assertEquals("test", r.testMe()); + } + + @Test + void testAddLogicalSourceForPersonJSON() throws Exception { + Model model = loadPersonTestData(Arrays.asList(this.personJsonTestData)); + RMLGenerator r = new RMLGenerator(); + Resource logicalSource = r.addLogicalSource("http://example.com/1", model, "iterator:https://shacl-play.sparna.fr/shapes/Person", "mscr:crosswalk:653b47f8-0bad-4c0e-86e9-f4ff13b5d8e3"); + + /*StmtIterator props = logicalSource.listProperties(); + props.forEach( + System.out::println + );*/ + + assertEquals("http://example.com/1", logicalSource.getURI()); + assertEquals("http://semweb.mmlab.be/ns/rml#BaseSource", logicalSource.getProperty(model.createProperty("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")).getObject().toString()); assertEquals("$", logicalSource.getProperty(model.createProperty("http://semweb.mmlab.be/ns/rml#iterator")).getObject().asLiteral().toString()); - } + assertEquals("http://semweb.mmlab.be/ns/ql#JSONPath", logicalSource.getProperty(model.createProperty("http://semweb.mmlab.be/ns/rml#referenceFormulation")).getObject().toString()); + } + + @Test + void testAddSubjectMap() throws Exception { + Model model = loadPersonTestData(Arrays.asList(this.personJsonTestData)); + RMLGenerator r = new RMLGenerator(); + + Model subjMap = r.addSubjectMap(model, "subject:https://shacl-play.sparna.fr/shapes/Person"); + + subjMap.write(System.out, "TTL"); + + } } diff --git a/src/test/resources/data/person_json2shacl/crosswalk_content.ttl b/src/test/resources/data/person_json2shacl/crosswalk_content.ttl index 6e577ff..e8fae1a 100644 --- a/src/test/resources/data/person_json2shacl/crosswalk_content.ttl +++ b/src/test/resources/data/person_json2shacl/crosswalk_content.ttl @@ -53,7 +53,7 @@ ; [ - "http://uri.suomi.fi/datamodel/ns/mscr#replace2Func" ; + "http://uri.suomi.fi/datamodel/ns/mscr#template" ; # TODO: adapt in MSCR [ a ; @@ -64,9 +64,9 @@ ] ; [ - "pattern" ; + ; # TODO: adapt in MSCR - "https://example.com/{value_0}{value_1}" + "https://example.com/{firstName}{lastName}" # TODO: adapt in MSCR ] ] ] ;