Skip to content

Commit b8bee07

Browse files
authored
Merge pull request #19 from herminiogg/enhancement-#17
Enhancement #17
2 parents 1f71d48 + ba95b4b commit b8bee07

23 files changed

+207
-48
lines changed

src/main/resources/javaClassGeneric.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package $package;
22

33
import com.herminiogarcia.dmaog.common.IRIValue;
4+
import com.herminiogarcia.dmaog.common.MultilingualString;
45
import java.util.List;
56

67
public class $className {

src/main/scala/com/herminiogarcia/dmaog/codeGeneration/CodeGenerator.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,12 @@ class CodeGenerator(mappingRules: Option[String], mappingLanguage: String, pathT
105105
}
106106

107107
// Datatype with cardinality conversion
108-
val dataType = if(theObject.isLiteral)
109-
convertToJavaDataType(theObject.asLiteral().getDatatype) // TODO: loop the results to find the best type
108+
val dataType =
109+
if(theObject.isLiteral &&
110+
(theObject.asLiteral().getLanguage.nonEmpty || theObject.asLiteral().getDatatypeURI == "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString"))
111+
"MultilingualString"
112+
else if(theObject.isLiteral)
113+
convertToJavaDataType(theObject.asLiteral().getDatatype) // TODO: loop the results to find the best type
110114
else "IRIValue"
111115
val dataTypeWithCardinality = if(objectCardinality > 1) "List<" + dataType + ">" else dataType
112116
attributes.append(new DataTypedPredicate(predicate, dataTypeWithCardinality))

src/main/scala/com/herminiogarcia/dmaog/codeGeneration/MappingRulesAnalyser.scala

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.herminiogarcia.dmaog.codeGeneration
33
import com.herminiogarcia.dmaog.common.DataTypedPredicate
44
import com.herminiogarcia.dmaog.common.Util.convertToJavaDataType
55
import com.herminiogarcia.shexml.MappingLauncher
6-
import com.herminiogarcia.shexml.ast.{AST, Action, DataTypeLiteral, Declaration, DeclarationStatement, LiteralObject, LiteralObjectValue, LiteralSubject, ObjectElement, Prefix, ShExML, Shape, ShapeLink}
6+
import com.herminiogarcia.shexml.ast.{AST, Action, DataTypeLiteral, Declaration, DeclarationStatement, LangTag, LiteralObject, LiteralObjectValue, LiteralSubject, ObjectElement, Prefix, ShExML, Shape, ShapeLink}
77
import org.apache.jena.datatypes.{RDFDatatype, TypeMapper}
88
import org.apache.jena.datatypes.xsd.XSDDatatype
99

@@ -50,15 +50,18 @@ case class ShExMLStaticRulesAnalyser(mappingRules: String) extends MappingRulesA
5050
.filterNot(po => po.predicate.prefix == "rdf:" && po.predicate.extension == "type").map(po => {
5151
val predicate = getPrefixes().find(_._1 == po.predicate.prefix).get._2 + po.predicate.extension
5252
po.objectOrShapeLink match {
53-
case ObjectElement(prefix, _, _, _, dataType, _, _) => dataType.map {
53+
case ObjectElement(prefix, _, _, _, dataType, langTag, _) => dataType.map {
5454
case DataTypeLiteral(value) =>
5555
val dataTypeURI = "http://www.w3.org/2001/XMLSchema#" + value.split(":")(1)
5656
val xsdType = TypeMapper.getInstance().getSafeTypeByName(dataTypeURI)
5757
//not having cardinality information, safest way is to assume every attribute can have multiple values
5858
val javaType = "List<" + convertToJavaDataType(xsdType) + ">"
59-
new DataTypedPredicate(predicate, javaType)
60-
case _=> generateTypeAccordingToPrefix(prefix, predicate)
61-
}.getOrElse(generateTypeAccordingToPrefix(prefix, predicate))
59+
langTag match {
60+
case Some(_) => new DataTypedPredicate(predicate, "List<MultilingualString>")
61+
case None => new DataTypedPredicate(predicate, javaType)
62+
}
63+
case _=> generateTypeAccordingToPrefix(prefix, predicate, langTag)
64+
}.getOrElse(generateTypeAccordingToPrefix(prefix, predicate, langTag))
6265
case ShapeLink(_) => new DataTypedPredicate(predicate, "List<IRIValue>")
6366
case LiteralObject(_, _) => new DataTypedPredicate(predicate, "List<IRIValue>")
6467
case LiteralObjectValue(_) => new DataTypedPredicate(predicate, "List<String>")
@@ -68,8 +71,9 @@ case class ShExMLStaticRulesAnalyser(mappingRules: String) extends MappingRulesA
6871
}).toMap
6972
}
7073

71-
private def generateTypeAccordingToPrefix(prefix: String, predicate: String): DataTypedPredicate = {
72-
if(prefix.isEmpty) new DataTypedPredicate(predicate, "List<String>")
74+
private def generateTypeAccordingToPrefix(prefix: String, predicate: String, langTag: Option[LangTag]): DataTypedPredicate = {
75+
if(prefix.isEmpty && langTag.isDefined) new DataTypedPredicate(predicate, "List<MultilingualString>")
76+
else if(prefix.isEmpty) new DataTypedPredicate(predicate, "List<String>")
7377
else new DataTypedPredicate(predicate, "List<IRIValue>")
7478
}
7579

src/main/scala/com/herminiogarcia/dmaog/common/Model.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ package com.herminiogarcia.dmaog.common
22

33

44
class IRIValue(val iri: String, val namespace: String, val localName: String)
5+
class MultilingualString(val value: String, val langTag: String)
56
class DataTypedPredicate(val predicate: String, val dataType: String)

src/main/scala/com/herminiogarcia/dmaog/common/ModelWriter.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ case class DataSparqlEndpointWriter(override val pathToGenerate: String) extends
4141
val formattedTriples = triples.map(t => {
4242
convertURIToPrefixedValue(t.getSubject.getURI, prefixes) + " " +
4343
convertURIToPrefixedValue(t.getPredicate.getURI, prefixes) + " " + {
44-
if (t.getObject.isLiteral) "\"" + t.getLiteral.getString + "\"" +"^^<" + t.getLiteral.getDatatype.getURI + ">"
44+
if(t.getObject.isLiteral && t.getObject.asLiteral().getLanguage.nonEmpty)
45+
"\"" + t.getObject.asLiteral().getString + "\"" +"@" + t.getObject.asLiteral().getLanguage
46+
else if(t.getObject.isLiteral) "\"" + t.getLiteral.getString + "\"" +"^^<" + t.getLiteral.getDatatype.getURI + ">"
4547
else convertURIToPrefixedValue(t.getObject.asResource().getURI, prefixes) + " "
4648
} + " ."
4749
}).mkString("\n")

src/main/scala/com/herminiogarcia/dmaog/dataAccess/DataAccess.scala

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.herminiogarcia.dmaog.dataAccess
22

3-
import com.herminiogarcia.dmaog.common.{DataLocalFileWriter, IRIValue, ModelLoader, PrefixedNameConverter, ResourceLoader}
3+
import com.herminiogarcia.dmaog.common.{DataLocalFileWriter, IRIValue, ModelLoader, MultilingualString, PrefixedNameConverter, ResourceLoader}
44
import org.apache.jena.datatypes.xsd.XSDDatatype
55
import org.apache.jena.query.{Dataset, DatasetFactory, QueryExecutionFactory, QueryFactory, QuerySolution, ResultSet, ResultSetFactory}
66
import org.apache.jena.rdf.model.{Model, ModelFactory, ResourceFactory}
@@ -77,19 +77,22 @@ class DataAccess(fileNameForGeneratedContent: String,
7777

7878
private def getGroupedStatements(sparql: String,
7979
getSubjectFunction: QuerySolution => String,
80-
getPredicateFunction: QuerySolution => String): Map[String, List[(String, String)]] = {
80+
getPredicateFunction: QuerySolution => String): Map[String, List[(String, ObjectResult)]] = {
8181
val resultSet = QueryExecutorFactory.getQueryExecutor(sparql, sparqlEndpoint, () => getModel).execute()
82-
val groupedBySubjectResults = mutable.Map[String, List[(String, String)]]()
82+
val groupedBySubjectResults = mutable.Map[String, List[(String, ObjectResult)]]()
8383
while(resultSet.hasNext) {
8484
val result = resultSet.next()
8585
val subject = getSubjectFunction(result)
8686
val predicate = getPredicateFunction(result)
8787
val theObject = result.get("object")
8888
val predicateURI = predicate
8989
val objectValue = {
90-
if(theObject == null) ""
91-
else if(theObject.isLiteral) theObject.asLiteral().getString
92-
else theObject.asResource().getURI
90+
if(theObject == null) LiteralResult("")
91+
else if(theObject.isLiteral &&
92+
(theObject.asLiteral().getLanguage.nonEmpty || theObject.asLiteral().getDatatypeURI == "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString"))
93+
MultilingualStringResult(theObject.asLiteral().getString, theObject.asLiteral().getLanguage)
94+
else if(theObject.isLiteral) LiteralResult(theObject.asLiteral().getString)
95+
else IRIResult(theObject.asResource().getURI)
9396
}
9497
groupedBySubjectResults.get(subject) match {
9598
case Some(value) =>
@@ -199,7 +202,7 @@ class DataAccess(fileNameForGeneratedContent: String,
199202
val sparql = loadFromResources("insertData.sparql")
200203
.replaceFirst("\\$prefixes", "")
201204
.replaceFirst("\\$triples", triples)
202-
updateExecution(instance, sparql)
205+
updateExecution(sparql)
203206
}
204207

205208
def delete[T](instance: T): Unit = {
@@ -208,11 +211,10 @@ class DataAccess(fileNameForGeneratedContent: String,
208211
.replaceFirst("\\$prefixes", "")
209212
.replaceAll("\\$triples", triples)
210213
.replaceFirst("\\$value", "<" + getIdValue(instance) + ">")
211-
updateExecution(instance, sparql)
212-
214+
updateExecution(sparql)
213215
}
214216

215-
private def updateExecution[T](instance: T, sparql: String): Unit = {
217+
private def updateExecution(sparql: String): Unit = {
216218
val dataset = DatasetFactory.create(getModel)
217219
val updateQuery = UpdateFactory.create(sparql)
218220
createUpdateProcessor(updateQuery, dataset).execute()
@@ -252,6 +254,8 @@ class DataAccess(fileNameForGeneratedContent: String,
252254
val values2 = values.map(v => {
253255
val objectTriple = {
254256
if(v.isInstanceOf[IRIValue]) Option("<" + v.asInstanceOf[IRIValue].iri + ">")
257+
else if(v.isInstanceOf[MultilingualString])
258+
Option("\"" + v.asInstanceOf[MultilingualString].value + "\"" +"@" + v.asInstanceOf[MultilingualString].langTag)
255259
else if(v != null) Option("\"" + v.toString + "\"" +"^^<" + getXSDType(v).getURI + ">")
256260
else Option.empty
257261
}
@@ -321,7 +325,7 @@ class DataAccess(fileNameForGeneratedContent: String,
321325
}).map(_._2 + capitalizedFieldName)
322326
}
323327

324-
private def populateObjects[T](groupedBySubjectResults: Map[String, List[(String, String)]], theClass: Class[T]): List[T] = {
328+
private def populateObjects[T](groupedBySubjectResults: Map[String, List[(String, ObjectResult)]], theClass: Class[T]): List[T] = {
325329
val model = getModel
326330
val prefixedNameConverterFunc = convertPrefixedName(nsPrefixes)_
327331
val prefixedValueConverterFunc = convertPrefixedNameToValue(nsPrefixes)_
@@ -334,15 +338,18 @@ class DataAccess(fileNameForGeneratedContent: String,
334338
val attributeName = prefixedNameConverterFunc(attribute._1).capitalize
335339
val setterName = "set" + attributeName
336340
val getterName = "get" + attributeName
337-
val value = prefixedValueConverterFunc(attribute._2)
341+
val value = prefixedValueConverterFunc(attribute._2.value)
338342
methods.find(_.getName == setterName).foreach(m => {
339343
val getterMethod = methods.find(_.getName == getterName).headOption
340344
val isList = m.getParameterTypes.head == classOf[util.List[Object]]
341345
val setterParameterType =
342346
if(!isList) m.getParameterTypes.head
343347
else m.getGenericParameterTypes.head.asInstanceOf[ParameterizedType].getActualTypeArguments.headOption.map(_.asInstanceOf[Class[_]]).get
344348
if(setterParameterType == classOf[IRIValue]) {
345-
invokeSetterOrAddToList(m, getterMethod, instance, createIRIValue(attribute._2, model), isList)
349+
invokeSetterOrAddToList(m, getterMethod, instance, createIRIValue(attribute._2.value, model), isList)
350+
} else if(setterParameterType == classOf[MultilingualString]) {
351+
invokeSetterOrAddToList(m, getterMethod, instance,
352+
new MultilingualString(attribute._2.value, attribute._2.asInstanceOf[MultilingualStringResult].langtag), isList)
346353
} else {
347354
if(setterParameterType.getMethods.exists(_.getName == "valueOf") && setterParameterType != classOf[String]) {
348355
val numericConversion = setterParameterType.getMethod("valueOf", classOf[String])
@@ -407,7 +414,7 @@ class DataAccess(fileNameForGeneratedContent: String,
407414
new IRIValue(iri, namespace, localPart)
408415
}
409416

410-
private def generateRDF[T](groupedBySubjectResults: Map[String, List[(String, String)]], rdfFormat: String): String = {
417+
private def generateRDF[T](groupedBySubjectResults: Map[String, List[(String, ObjectResult)]], rdfFormat: String): String = {
411418
//new model and add prefixes
412419
val inputModel = getModel
413420
val modelToReturn = ModelFactory.createDefaultModel()
@@ -416,8 +423,11 @@ class DataAccess(fileNameForGeneratedContent: String,
416423
groupedBySubjectResults.foreach({ case (subject, predicateObjects) => predicateObjects.foreach({
417424
case (predicate, theObject) =>
418425
inputModel.getResource(subject).listProperties(ResourceFactory.createProperty(predicate)).asScala.filter(s => {
419-
if(s.getObject.isLiteral) s.getObject.asLiteral().getString == theObject
420-
else s.getObject.asResource().getURI == theObject
426+
if(s.getObject.isLiteral && s.getObject.asLiteral().getLanguage.nonEmpty)
427+
s.getObject.asLiteral().getString == theObject.value &&
428+
s.getObject.asLiteral().getLanguage == theObject.asInstanceOf[MultilingualStringResult].langtag
429+
else if(s.getObject.isLiteral) s.getObject.asLiteral().getString == theObject.value
430+
else s.getObject.asResource().getURI == theObject.value
421431
}).foreach(modelToReturn.add)
422432
}) })
423433
//serialize in the appropriate format
@@ -460,3 +470,11 @@ case class SparqlEndpointQueryExecutor(sparql: String, endpoint: String) extends
460470
resultSet
461471
}
462472
}
473+
474+
sealed trait ObjectResult {
475+
val value: String
476+
}
477+
478+
case class LiteralResult(value: String) extends ObjectResult
479+
case class IRIResult(value: String) extends ObjectResult
480+
case class MultilingualStringResult(value: String, langtag: String) extends ObjectResult

src/test/resources/data.ttl

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ dbr:Leonardo_DiCaprio
1515
schema:countryOfOrigin dbr:USA ;
1616
schema:director dbr:Christopher_Nolan ;
1717
schema:musicBy dbr:Hans_Zimmer ;
18-
schema:name "Inception" .
18+
schema:name "Inception" ;
19+
:nameWithLanguage "Inception" @en .
1920

2021
dbr:Christian_Bale a :Actor ;
2122
:appear_on :4 ;
@@ -37,7 +38,9 @@ dbr:Mackenzie_Foy a :Actor ;
3738
schema:countryOfOrigin dbr:USA ;
3839
schema:director dbr:Christopher_Nolan ;
3940
schema:musicBy dbr:Hans_Zimmer ;
40-
schema:name "Interstellar" .
41+
schema:name "Interstellar" ;
42+
:nameWithLanguage "Interstellar" @en .
43+
4144

4245
dbr:Matthew_McConaughey
4346
a :Actor ;
@@ -56,7 +59,8 @@ dbr:John_Lithgow a :Actor ;
5659
schema:countryOfOrigin dbr:USA ;
5760
schema:director dbr:Christopher_Nolan ;
5861
schema:musicBy dbr:Hans_Zimmer ;
59-
schema:name "Dunkirk" .
62+
schema:name "Dunkirk" ;
63+
:nameWithLanguage "Dunkirk" @en .
6064

6165
dbr:Ellen_Burstyn a :Actor ;
6266
:appear_on :2 ;
@@ -111,7 +115,8 @@ dbr:Timothée_Chalamet
111115
schema:countryOfOrigin dbr:USA ;
112116
schema:director dbr:Christopher_Nolan ;
113117
schema:musicBy dbr:David_Julyan ;
114-
schema:name "The Prestige" .
118+
schema:name "The Prestige" ;
119+
:nameWithLanguage "The Prestige" @en .
115120

116121
dbr:Michael_Caine a :Actor ;
117122
:appear_on :4 ;

src/test/scala/com/herminiogarcia/dmaog/ClassGenerator.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,16 @@ trait ClassGenerator {
99
val rules: String
1010

1111
def generateClasses(static: Boolean = false): Unit = {
12+
removeDirContent()
1213
new File("./tmp").mkdir() //create temp directory for tests
1314
new CodeGenerator(Option(rules), "shexml", "./tmp/", "com.example",
1415
None, None, None, None, static).generate()
1516
}
1617

18+
def removeDirContent(): Unit = {
19+
new File("./tmp").deleteOnExit()
20+
}
21+
1722
def loadClass(entityName: String): String = {
1823
val buffer = scala.io.Source.fromFile("./tmp/"+ entityName + ".java")
1924
val result = buffer.mkString

src/test/scala/com/herminiogarcia/dmaog/DataAccessSingletonTest.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package com.herminiogarcia.dmaog
22

3-
import org.scalatest.BeforeAndAfter
3+
import org.scalatest.{BeforeAndAfter, DoNotDiscover}
44
import org.scalatest.funsuite.AnyFunSuite
55

6+
@DoNotDiscover
67
class DataAccessSingletonTest extends AnyFunSuite with BeforeAndAfter with ClassGenerator {
78

89
val rules =

src/test/scala/com/herminiogarcia/dmaog/FilmAndActorCodeGenerationTest.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.herminiogarcia.dmaog
22

3+
import org.scalatest.DoNotDiscover
4+
5+
@DoNotDiscover
36
class FilmAndActorCodeGenerationTest extends FilmCodeGenerationTest {
47

58
override val rules =

0 commit comments

Comments
 (0)