diff --git a/modules/converter/src/main/scala/es/weso/shacl/converter/Shacl2ShEx.scala b/modules/converter/src/main/scala/es/weso/shacl/converter/Shacl2ShEx.scala index 521b6f9b..747a6d3b 100644 --- a/modules/converter/src/main/scala/es/weso/shacl/converter/Shacl2ShEx.scala +++ b/modules/converter/src/main/scala/es/weso/shacl/converter/Shacl2ShEx.scala @@ -13,10 +13,14 @@ object Shacl2ShEx extends Converter { val prefixes = schema.pm val rs: List[Result[shex.ShapeExpr]] = schema.shapes.map(cnvShape(_)).toList val r: Result[List[shex.ShapeExpr]] = sequence(rs) + r.map( m => shex.Schema.empty.copy( prefixes = Some(prefixes), - shapes = Some(m) + shapes = { + println(s"@@@Conversion Map: $m") + Some(m) + } ) ) } @@ -32,17 +36,6 @@ object Shacl2ShEx extends Converter { xs.foldLeft(zero)(comb) } - /* def cnvShape(s: shacl.Shape): Result[shex.ShapeExpr] = { - val id : Id = cnvId(s.id) - val rs = s.propertyShapes.toList.map(cnvConstraint(id, _)).sequence - rs.map(ses => ses.size match { - case 1 => ses.head - case n if n > 1 => shex.ShapeAnd(id,ses) - case _ => ??? // TODO - }) - } */ - - def cnvShape(c: shacl.Shape): Result[shex.ShapeExpr] = c match { case nc: shacl.NodeShape => cnvNodeShape(nc) diff --git a/modules/schema/src/test/scala/es/weso/schema/SchemaConversionsTest.scala b/modules/schema/src/test/scala/es/weso/schema/SchemaConversionsTest.scala index 1f76d4ad..9be3c008 100644 --- a/modules/schema/src/test/scala/es/weso/schema/SchemaConversionsTest.scala +++ b/modules/schema/src/test/scala/es/weso/schema/SchemaConversionsTest.scala @@ -1,21 +1,22 @@ package es.weso.schema import org.scalatest._ -import cats._ import cats.implicits._ +import es.weso.json.JsonCompare.jsonDiff +import es.weso.rdf.jena.RDFAsJenaModel +import io.circe.parser._ class SchemaConversionsTest extends FunSpec with Matchers with EitherValues { - ignore("ShExC -> ShExJ") { - it("Converts simple ShExC to ShExJ") { - val strShExC = - """ + describe("ShExC -> ShExJ") { + val strShExC = + """ |prefix : |:S { :p IRI } """.stripMargin - val strShExJ = - """ + val strExpected = + """ | { | "type" : "Schema", | "@context" : "http://www.w3.org/ns/shex.jsonld", @@ -35,24 +36,126 @@ class SchemaConversionsTest extends FunSpec with Matchers with EitherValues { | ] |} """.stripMargin + shouldConvert(strShExC, "ShExC", "ShEx", "ShExJ", "ShEx", strExpected, jsonCompare) + } - val r = for { - schemaFromShExC <- ShExSchema.fromString(strShExC,"ShExC",None).leftMap(e => s"Error reading ShExC\n$e") - schemaFromShExJ <- ShExSchema.fromString(strShExJ,"ShExJ",None).leftMap(e => s"Error reading ShExJ\n$e") - schemaFromConversion <- schemaFromShExC.convert(Some("ShEx"), Some("ShExJ")).leftMap(e => s"Error converting\n$e") - } yield (schemaFromShExC,schemaFromConversion) - - r.fold(e => fail(s"Error: $e"), - v => { - val (s1,s2) = v - if (s1 == s2) { - info(s"Schema == schema from conversion\nFrom ShExC: $s1\nFrom ShExJ:$s2") - } else { - fail(s"Schema != schema from conversion\nFrom ShExC: $s1\nFrom ShExJ:$s2") - } - } - ) - } + describe(s"ShExC -> Turtle") { + val strShExC = + """ + |prefix : + |:S { :p IRI } + """.stripMargin + val strExpected = + """ + |@prefix : . + |@prefix sx: . + | + |:S a sx:Shape ; + | sx:closed false ; + | sx:expression [ + | a sx:TripleConstraint ; + | sx:predicate :p ; + | sx:valueExpr [ + | a sx:NodeConstraint ; + | sx:nodeKind sx:iri ] + | ] . + | + |[ a sx:Schema ; + | sx:shapes :S + |] . + """.stripMargin + shouldConvert(strShExC, "ShExC", "ShEx", "Turtle", "ShEx", strExpected, rdfCompare) } + + describe(s"SHACL (Turtle) -> SHACL (JSON-LD)") { + val strShacl = + """ + |prefix : + |prefix sh: + |:S a sh:NodeShape ; + | sh:nodeKind sh:IRI + """.stripMargin + + val strExpected = + """ + | . + | "false"^^ . + | . + """.stripMargin + shouldConvert(strShacl, "Turtle", "Shaclex", "N-Triples", "Shaclex", strExpected, rdfCompare) + } + + describe(s"SHACL (Turtle) -> ShEx (Turtle)") { + val strShacl = + """ + |prefix : + |prefix sh: + |:S a sh:NodeShape ; + | sh:nodeKind sh:IRI + """.stripMargin + + val strExpected = """ + | { "type" : "Schema", + | "@context" : "http://www.w3.org/ns/shex.jsonld", + | "shapes" : [ { + | "type" : "NodeConstraint", + | "id" : "http://example.org/S", + | "nodeKind" : "iri" + | } ] } + """.stripMargin + shouldConvert(strShacl, "Turtle", "Shaclex", "ShExJ", "ShEx", strExpected, jsonCompare) + } + + + def shouldConvert(str: String, format: String, engine: String, + targetFormat: String, targetEngine: String, + expected: String, + compare: (String,String) => Either[String, Boolean] + ): Unit = { + it(s"Should convert $str with format $format and engine $engine and obtain $expected") { + val r = for { + schema <- Schemas.fromString(str, format, engine, None). + leftMap(e => s"Error reading Schema ($format/$engine): $str\nError: $e") + strConverted <- schema.convert(Some(targetFormat), Some(targetEngine)). + leftMap(e => s"Error converting schema(${schema.name}) to ($targetFormat/$targetEngine\n$e") + result <- compare(strConverted, expected). + leftMap(e => s"Error in comparison: $e") + } yield (strConverted, expected, result) + + r.fold(e => fail(s"Error: $e"), v => { + val (s1, s2, r) = v + if (r) { + info(s"Conversion is ok") + } else { + fail(s"Different results\ns1=$s1\ns2$s2") + } + }) + } + } + + def jsonCompare(s1: String, s2: String): Either[String, Boolean] = for { + json1 <- parse(s1).leftMap(e => s"Error parsing $s1\n$e") + json2 <- parse(s2).leftMap(e => s"Error parsing $s2\n$e") + b <- + if (json1.equals(json2)) { Right(true)} + else Left(s"Json's different:\nJson1: $json1\nJson2: $json2. Diff: ${jsonDiff(json1, json2)}") + } yield b + + def rdfCompare(s1: String, s2: String): Either[String, Boolean] = for { + rdf1 <- RDFAsJenaModel.fromChars(s1,"TURTLE",None) + rdf2 <- RDFAsJenaModel.fromChars(s2,"TURTLE",None) + b <- rdf1.isIsomorphicWith(rdf2) + } yield b + + def shExCompare(s1: String, s2: String): Either[String, Boolean] = for { + schema1 <- Schemas.fromString(s1,"ShExC","ShEx",None). + leftMap(e => s"Error reading ShEx from string s1: $s1\n$e") + schema2 <- Schemas.fromString(s2,"ShExC","ShEx",None). + leftMap(e => s"Error reading ShEx from string s1: $s1\n$e") + json1 <- schema1.convert(Some("ShExJ"),Some("ShExC")) + json2 <- schema2.convert(Some("ShExJ"),Some("ShExC")) + b <- jsonCompare(json1,json2) + } yield b + } diff --git a/modules/shex/src/main/scala/es/weso/shex/Schema.scala b/modules/shex/src/main/scala/es/weso/shex/Schema.scala index 6c33c034..33c052a6 100644 --- a/modules/shex/src/main/scala/es/weso/shex/Schema.scala +++ b/modules/shex/src/main/scala/es/weso/shex/Schema.scala @@ -71,7 +71,7 @@ case class Schema(prefixes: Option[PrefixMap], object Schema { - def rdfDataFormats(rdfReader: RDFReader) = rdfReader.availableParseFormats // RDFAsJenaModel.availableFormats.map(_.toUpperCase) + def rdfDataFormats(rdfReader: RDFReader) = rdfReader.availableParseFormats.map(_.toUpperCase) // RDFAsJenaModel.availableFormats.map(_.toUpperCase) def empty: Schema = Schema(None, None, None, None, None, None, List()) @@ -105,6 +105,7 @@ object Schema { format: String, rdfBuilder: RDFBuilder): Either[String,String] = { val formatUpperCase = format.toUpperCase + println(s"Target format: $formatUpperCase, available: ${rdfDataFormats(rdfBuilder)} ") formatUpperCase match { case "SHEXC" => { import compact.CompactShow._ diff --git a/notes/0.1.0.md b/notes/0.1.0.md new file mode 100644 index 00000000..d6d311b4 --- /dev/null +++ b/notes/0.1.0.md @@ -0,0 +1,18 @@ +# New features + +- Passes all SHACL-Core tests + +TODOs +----- + +- ShEx: Complete UML generation for ShEx schemas + +- ShEx: Complete semantic actions implementation + +- ShEx: test-suite with shape maps and update report [Issue ]() + +- Shaclex: Conversion from ShEx to SHACL [Issue 114](https://github.com/labra/shaclex/issues/114) + +- Shaclex: Conversion from SHACL to ShEx [Issue 113](https://github.com/labra/shaclex/issues/113) + +- Shacl: Implement SHACL-Sparql [Issue 112](https://github.com/labra/shaclex/issues/112) diff --git a/version.sbt b/version.sbt index e1b8d9ce..e7654440 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "0.0.97" +version in ThisBuild := "0.1.0"