diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..5400d08f
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,44 @@
+# Guide for contributors
+
+This project follows a standard [fork and pull][fork-and-pull] model for accepting contributions via
+GitHub pull requests:
+
+0. [Pick (or report) an issue](#pick-or-report-an-issue)
+1. [Write code](#write-code)
+2. [Write tests](#write-tests)
+3. [Submit a pull request](#submit-a-pull-request)
+
+## Pick or report an issue
+
+We always welcome bug reports and feature requests—please don't feel like you need to have time to
+contribute a fix or implementation for your issue to be appreciated.
+
+## Write code
+
+We prefer functional programming for the code.
+
+* Code and comments should be formatted to a width no greater than 100 columns.
+* Files should not contain trailing spaces.
+* Imports should be sorted alphabetically.
+
+When in doubt, please run `sbt scalastyle` and let us know if you have any questions.
+
+## Write tests
+
+Shaclex uses [ScalaTest][scalatest] for testing.
+
+## Submit a pull request
+
+* Pull requests should be submitted from a separate branch (e.g. using
+ `git checkout -b "username/fix-123"`).
+* In general we discourage force pushing to an active pull-request branch that other people are
+ commenting on or contributing to, and suggest using `git merge master` during development.
+ Once development is complete, use `git rebase master` and force push to [clean up the history][squash].
+* The first line of a commit message should be no more than 72 characters long (to accommodate
+ formatting in various environments).
+* Commit messages should general use the present tense, normal sentence capitalization, and no final
+ punctuation.
+* If a pull request decreases code coverage more than by 2%, please file an issue to make sure that
+ tests get added.
+
+This guide for contributors is inspired by [circe's guide](https://github.com/circe/circe/blob/master/CONTRIBUTING.md).
diff --git a/build.sbt b/build.sbt
index d683e723..37e8dc18 100644
--- a/build.sbt
+++ b/build.sbt
@@ -37,6 +37,7 @@ lazy val http4sVersion = "0.18.0-M8"
lazy val scalatagsVersion = "0.6.7"
lazy val scallopVersion = "3.1.1"
lazy val jenaVersion = "3.6.0"
+lazy val rdf4jVersion = "2.2.4"
lazy val jgraphtVersion = "1.1.0"
lazy val diffsonVersion = "2.2.5"
lazy val xercesVersion = "2.11.0"
@@ -59,6 +60,9 @@ lazy val jgraphtCore = "org.jgrapht" % "jgrapht-core"
lazy val antlr4 = "org.antlr" % "antlr4" % antlrVersion
lazy val xercesImpl = "xerces" % "xercesImpl" % xercesVersion
lazy val jenaArq = "org.apache.jena" % "jena-arq" % jenaVersion
+lazy val rdf4j = "org.eclipse.rdf4j" % "rdf4j" % rdf4jVersion
+lazy val rdf4jModel = "org.eclipse.rdf4j" % "rdf4j-model" % rdf4jVersion
+lazy val rdf4j_rioTurtle = "org.eclipse.rdf4j" % "rdf4j-rio-turtle" % rdf4jVersion
lazy val scalaLogging = "com.typesafe.scala-logging" %% "scala-logging" % loggingVersion
lazy val scallop = "org.rogach" %% "scallop" % scallopVersion
lazy val scalactic = "org.scalactic" %% "scalactic" % scalacticVersion
@@ -94,8 +98,8 @@ lazy val shaclex = project
// buildInfoPackage := "es.weso.shaclex.buildinfo"
// )
.settings(commonSettings, packagingSettings, publishSettings, ghPagesSettings, wixSettings)
- .aggregate(schema, shacl, shex, manifest, srdfJena, srdf, utils, converter, rbe, typing, validating, server, shapeMaps, depGraphs)
- .dependsOn(schema, shacl, shex, manifest, srdfJena, srdf, utils, converter, rbe, typing, validating, server, shapeMaps, depGraphs)
+ .aggregate(schema, shacl, shex, manifest, srdfJena, srdf4j, srdf, utils, converter, rbe, typing, validating, server, shapeMaps, depGraphs)
+ .dependsOn(schema, shacl, shex, manifest, srdfJena, srdf4j, srdf, utils, converter, rbe, typing, validating, server, shapeMaps, depGraphs)
.settings(
unidocProjectFilter in (ScalaUnidoc, unidoc) := inAnyProject -- inProjects(noDocProjects: _*),
libraryDependencies ++= Seq(
@@ -132,7 +136,12 @@ lazy val shacl = project
.in(file("modules/shacl"))
.disablePlugins(RevolverPlugin)
.settings(commonSettings, publishSettings)
- .dependsOn(srdfJena, manifest, utils, typing, validating)
+ .dependsOn(srdf,
+ manifest,
+ utils,
+ typing,
+ validating,
+ srdfJena % Test)
.settings(
logBuffered in Test := false,
parallelExecution in Test := false,
@@ -164,7 +173,18 @@ lazy val shex = project
testOptions in Test := Seq(Tests.Filter(testFilter)),
testOptions in CompatTest := Seq(Tests.Filter(compatFilter)),
)
- .dependsOn(srdfJena, srdf, typing, utils % "test -> test; compile -> compile", validating, shapeMaps, rbe, manifest, depGraphs)
+ .dependsOn(
+ srdf,
+ typing,
+ utils % "test -> test; compile -> compile",
+ validating,
+ shapeMaps,
+ rbe,
+ manifest,
+ depGraphs,
+ srdfJena % Test,
+ srdf4j % Test
+ )
.settings(
libraryDependencies ++= Seq(
typesafeConfig % Test,
@@ -181,10 +201,13 @@ lazy val shapeMaps = project
.enablePlugins(Antlr4Plugin)
.disablePlugins(RevolverPlugin)
.settings(commonSettings, publishSettings, antlrSettings("es.weso.shapeMaps.parser"))
- .dependsOn(srdfJena)
+ .dependsOn(
+ srdf,
+ utils,
+ srdfJena % Test)
.settings(
libraryDependencies ++= Seq(
- sext,
+ sext % Test,
scalaLogging,
catsCore,
catsKernel,
@@ -221,7 +244,7 @@ lazy val manifest = project
.in(file("modules/manifest"))
.disablePlugins(RevolverPlugin)
.settings(commonSettings, publishSettings)
- .dependsOn(srdfJena, utils)
+ .dependsOn(srdf, utils, srdfJena)
.settings(
libraryDependencies ++= Seq(
typesafeConfig % Test,
@@ -278,6 +301,23 @@ lazy val srdfJena = project
)
)
+lazy val srdf4j = project
+ .in(file("modules/srdf4j"))
+ .disablePlugins(RevolverPlugin)
+ .dependsOn(srdf, utils)
+ .settings(commonSettings, publishSettings)
+ .settings(
+ libraryDependencies ++= Seq(
+ logbackClassic,
+ scalaLogging,
+ typesafeConfig % Test,
+ rdf4j, rdf4jModel, rdf4j_rioTurtle,
+ catsCore,
+ catsKernel,
+ catsMacros
+ )
+ )
+
lazy val typing = project
.in(file("modules/typing"))
.disablePlugins(RevolverPlugin)
@@ -312,7 +352,7 @@ lazy val utils = project
lazy val validating = project
.in(file("modules/validating"))
.disablePlugins(RevolverPlugin)
- .dependsOn(srdfJena, utils % "test -> test; compile -> compile")
+ .dependsOn(srdf, srdfJena % Test, utils % "test -> test; compile -> compile")
.settings(commonSettings, publishSettings)
.settings(
libraryDependencies ++= Seq(
diff --git a/modules/converter/README.md b/modules/converter/README.md
new file mode 100644
index 00000000..4412542d
--- /dev/null
+++ b/modules/converter/README.md
@@ -0,0 +1,3 @@
+# Converter module
+
+Converts between ShEx and SHACL
\ No newline at end of file
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 43f4509b..2ff21fd1 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
@@ -67,6 +67,6 @@ object Shacl2ShEx extends Converter {
shex.IRILabel(iri)
def mkBNodeLabel(n: Int): shex.ShapeLabel =
- shex.BNodeLabel(BNodeId(n.toString))
+ shex.BNodeLabel(BNode(n.toString))
}
\ No newline at end of file
diff --git a/modules/converter/src/main/scala/es/weso/shex/converter/shex2shacl.scala b/modules/converter/src/main/scala/es/weso/shex/converter/shex2shacl.scala
index f3d52d7f..c2bf8694 100644
--- a/modules/converter/src/main/scala/es/weso/shex/converter/shex2shacl.scala
+++ b/modules/converter/src/main/scala/es/weso/shex/converter/shex2shacl.scala
@@ -18,7 +18,7 @@ object ShEx2Shacl extends Converter {
def newBNode: RDFNode = {
bNodeCounter += 1
- BNodeId("shape" + (bNodeCounter))
+ BNode("shape" + (bNodeCounter))
}
var shapesMap: Map[shacl.ShapeRef, shacl.Shape] = Map()
diff --git a/modules/depGraphs/README.md b/modules/depGraphs/README.md
new file mode 100644
index 00000000..661cefa6
--- /dev/null
+++ b/modules/depGraphs/README.md
@@ -0,0 +1,3 @@
+# Dependency Graphs module
+
+Checks if there are cycles in dependency graphs
\ No newline at end of file
diff --git a/modules/manifest/src/main/scala/es/weso/manifest/RDF2Manifest.scala b/modules/manifest/src/main/scala/es/weso/manifest/RDF2Manifest.scala
index 0a6665da..24676fdb 100644
--- a/modules/manifest/src/main/scala/es/weso/manifest/RDF2Manifest.scala
+++ b/modules/manifest/src/main/scala/es/weso/manifest/RDF2Manifest.scala
@@ -117,7 +117,7 @@ case class RDF2Manifest(base: Option[IRI],
case iri: IRI =>
if (noType(iri, rdf)) Right(IRIResult(iri))
else compoundResult(iri, rdf)
- case bNode: BNodeId => compoundResult(bNode, rdf)
+ case bNode: BNode => compoundResult(bNode, rdf)
case _ => parseFail("Unexpected type of result " + n)
}
}
diff --git a/modules/rbe/README.md b/modules/rbe/README.md
new file mode 100644
index 00000000..264f54f4
--- /dev/null
+++ b/modules/rbe/README.md
@@ -0,0 +1,3 @@
+# RBE (Regular Bag Expressions) implementation
+
+This module implements Regular Bag Expressions
\ No newline at end of file
diff --git a/modules/schema/README.md b/modules/schema/README.md
new file mode 100644
index 00000000..aeb7e2e7
--- /dev/null
+++ b/modules/schema/README.md
@@ -0,0 +1,4 @@
+# Schema
+
+This module defines a generic Schema interface which can be used to validate elements.
+We provide two implementations of this interface, for ShEx and for SHACL.
\ No newline at end of file
diff --git a/modules/schema/src/main/scala/es/weso/schema/ShaclexSchema.scala b/modules/schema/src/main/scala/es/weso/schema/ShaclexSchema.scala
index e829b6e4..04d17750 100644
--- a/modules/schema/src/main/scala/es/weso/schema/ShaclexSchema.scala
+++ b/modules/schema/src/main/scala/es/weso/schema/ShaclexSchema.scala
@@ -59,7 +59,7 @@ case class ShaclexSchema(schema: ShaclSchema) extends Schema {
private def cnvShape(s: Shape): ShapeMapLabel = {
s.id match {
case iri: IRI => IRILabel(iri)
- case bnode: BNodeId => BNodeLabel(bnode)
+ case bnode: BNode => BNodeLabel(bnode)
case _ => throw new Exception(s"cnvShape: unexpected ${s.id}")
}
}
diff --git a/modules/server/README.md b/modules/server/README.md
new file mode 100644
index 00000000..eaccc6d0
--- /dev/null
+++ b/modules/server/README.md
@@ -0,0 +1,11 @@
+# Server module
+
+This module implements a simple server based on the [http4s](http://http4s.org/) library.
+
+The server contains a simple REST API defined [here](https://github.com/labra/shaclex/blob/master/modules/server/src/main/scala/es/weso/server/APIService.scala) and a web service that calls the REST API.
+The web service has been implemented using [Twirl templates](https://www.playframework.com/documentation/2.6.x/ScalaTemplates)
+which are defined [in this folder](https://github.com/labra/shaclex/tree/master/modules/server/src/main/twirl/es/weso).
+Some parts of the web service are implemennted in plain Javascript [here](https://github.com/labra/shaclex/tree/master/modules/server/src/main/resources/staticviews/js).
+In the future, it would be better to replace that Javascript code by ScalaJs.
+
+The server is deployed at [shaclex](http://shaclex.validatingrdf.com).
\ No newline at end of file
diff --git a/modules/shacl/README.md b/modules/shacl/README.md
new file mode 100644
index 00000000..e3be6d9e
--- /dev/null
+++ b/modules/shacl/README.md
@@ -0,0 +1,3 @@
+# SHACL implementation
+
+This module implements [SHACL](https://www.w3.org/TR/shacl/) on top of the [SRDF] interface so it can be used by [Apache Jena](https://jena.apache.org/) or [RDF4j](http://rdf4j.org/).
\ No newline at end of file
diff --git a/modules/shacl/src/main/scala/es/weso/shacl/Shacl.scala b/modules/shacl/src/main/scala/es/weso/shacl/Shacl.scala
index 27a4994f..5455f356 100644
--- a/modules/shacl/src/main/scala/es/weso/shacl/Shacl.scala
+++ b/modules/shacl/src/main/scala/es/weso/shacl/Shacl.scala
@@ -128,7 +128,7 @@ sealed abstract class Shape {
def showId: String =
id match {
case iri: IRI => iri.str
- case bnode: BNodeId => bnode.toString
+ case bnode: BNode => bnode.toString
}
def targetNodes: Seq[RDFNode] =
diff --git a/modules/shacl/src/main/scala/es/weso/shacl/converter/RDF2Shacl.scala b/modules/shacl/src/main/scala/es/weso/shacl/converter/RDF2Shacl.scala
index 81d9346f..36b6044c 100644
--- a/modules/shacl/src/main/scala/es/weso/shacl/converter/RDF2Shacl.scala
+++ b/modules/shacl/src/main/scala/es/weso/shacl/converter/RDF2Shacl.scala
@@ -238,7 +238,7 @@ object RDF2Shacl extends RDFParser with LazyLogging {
def parsePath: RDFParser[SHACLPath] = (n, rdf) => {
n match {
case iri: IRI => Right(PredicatePath(iri))
- case bnode: BNodeId => someOf(
+ case bnode: BNode => someOf(
inversePath,
oneOrMorePath,
zeroOrMorePath,
diff --git a/modules/shacl/src/main/scala/es/weso/shacl/validator/Validator.scala b/modules/shacl/src/main/scala/es/weso/shacl/validator/Validator.scala
index 21953ffd..9bee1e97 100644
--- a/modules/shacl/src/main/scala/es/weso/shacl/validator/Validator.scala
+++ b/modules/shacl/src/main/scala/es/weso/shacl/validator/Validator.scala
@@ -783,7 +783,7 @@ case class Validator(schema: Schema) extends LazyLogging {
private def equalsNode(n1: RDFNode, n2: RDFNode): Boolean = (n1, n2) match {
case (l1: Literal, l2: Literal) => l1 == l2
case (i1: IRI, i2: IRI) => i1 == i2
- case (b1: BNodeId, b2: BNodeId) => b1 == b2
+ case (b1: BNode, b2: BNode) => b1 == b2
case (_, _) => false
}
@@ -796,7 +796,7 @@ case class Validator(schema: Schema) extends LazyLogging {
case (DatatypeLiteral(n1, d1), DatatypeLiteral(n2, d2)) => d1 == d2 && n1 < n2
case (LangLiteral(n1, l1), LangLiteral(n2, l2)) => n1 < n2
case (i1: IRI, i2: IRI) => i1.str < i2.str
- case (b1: BNodeId, b2: BNodeId) => b1.id < b2.id
+ case (b1: BNode, b2: BNode) => b1.id < b2.id
case (_, _) => false
}
private def lessThanOrEqualNode(n1: RDFNode, n2: RDFNode): Boolean = (n1, n2) match {
@@ -807,7 +807,7 @@ case class Validator(schema: Schema) extends LazyLogging {
case (DatatypeLiteral(n1, d1), DatatypeLiteral(n2, d2)) => d1 == d2 && n1 <= n2
case (LangLiteral(n1, l1), LangLiteral(n2, l2)) => n1 <= n2
case (i1: IRI, i2: IRI) => i1.str <= i2.str
- case (b1: BNodeId, b2: BNodeId) => b1.id <= b2.id
+ case (b1: BNode, b2: BNode) => b1.id <= b2.id
case (_, _) => false
}
diff --git a/modules/shacl/src/test/scala/es/weso/shacl/AbstractSyntaxTest.scala b/modules/shacl/src/test/scala/es/weso/shacl/AbstractSyntaxTest.scala
index d5500d82..8a17e71f 100644
--- a/modules/shacl/src/test/scala/es/weso/shacl/AbstractSyntaxTest.scala
+++ b/modules/shacl/src/test/scala/es/weso/shacl/AbstractSyntaxTest.scala
@@ -8,7 +8,7 @@ class AbstractSyntaxTest extends FunSpec with Matchers {
describe("Abstract Syntax") {
it("should be able to create a shape") {
- val x = BNodeId("x")
+ val x = BNode("x")
val c: PropertyShape =
PropertyShape(
id = x,
diff --git a/modules/shapeMaps/README.md b/modules/shapeMaps/README.md
new file mode 100644
index 00000000..6358a217
--- /dev/null
+++ b/modules/shapeMaps/README.md
@@ -0,0 +1,3 @@
+# Shape maps implementation
+
+Implementation of [shape maps](http://shex.io/shape-map/) which can be used to trigger validation. Although shape maps were defined by the ShEx community group they can also be used in SHACL.
\ No newline at end of file
diff --git a/modules/shapeMaps/src/main/scala/es/weso/shapeMaps/Association.scala b/modules/shapeMaps/src/main/scala/es/weso/shapeMaps/Association.scala
index c15da3d3..f0a19e24 100644
--- a/modules/shapeMaps/src/main/scala/es/weso/shapeMaps/Association.scala
+++ b/modules/shapeMaps/src/main/scala/es/weso/shapeMaps/Association.scala
@@ -5,7 +5,7 @@ import io.circe.syntax._
import NodeSelector._
import ShapeMapLabel._
import es.weso.json.DecoderUtils._
-import es.weso.rdf.nodes.{ BNodeId, IRI, RDFNode }
+import es.weso.rdf.nodes.{ BNode, IRI, RDFNode }
case class Association(node: NodeSelector, shape: ShapeMapLabel, info: Info = Info()) {
@@ -44,7 +44,7 @@ object Association {
s => Left(DecodingFailure(s, Nil)),
node => node match {
case iri: IRI => Right(IRILabel(iri))
- case bnode: BNodeId => Right(BNodeLabel(bnode))
+ case bnode: BNode => Right(BNodeLabel(bnode))
case _ => Left(DecodingFailure(s"Cannot parse shapeMapLabel $node", Nil))
}))
}
diff --git a/modules/shapeMaps/src/main/scala/es/weso/shapeMaps/ShapeMapLabel.scala b/modules/shapeMaps/src/main/scala/es/weso/shapeMaps/ShapeMapLabel.scala
index 9b565a2b..9b4c483a 100644
--- a/modules/shapeMaps/src/main/scala/es/weso/shapeMaps/ShapeMapLabel.scala
+++ b/modules/shapeMaps/src/main/scala/es/weso/shapeMaps/ShapeMapLabel.scala
@@ -1,5 +1,5 @@
package es.weso.shapeMaps
-import es.weso.rdf.nodes.{ BNodeId, IRI }
+import es.weso.rdf.nodes.{ BNode, IRI }
import io.circe.{ Encoder, Json }
abstract class ShapeMapLabel {
@@ -14,7 +14,7 @@ abstract class ShapeMapLabel {
}
case class IRILabel(iri: IRI) extends ShapeMapLabel
-case class BNodeLabel(bnode: BNodeId) extends ShapeMapLabel
+case class BNodeLabel(bnode: BNode) extends ShapeMapLabel
case object Start extends ShapeMapLabel
object ShapeMapLabel {
diff --git a/modules/shex/README.md b/modules/shex/README.md
new file mode 100644
index 00000000..07cc9cff
--- /dev/null
+++ b/modules/shex/README.md
@@ -0,0 +1,5 @@
+# ShEx library
+
+ShEx 2.0 implementation.
+
+The implementation is defined in terms of the [SRDF](https://github.com/labra/shaclex/tree/master/modules/srdf) interface so it can work with both [Apache Jena](https://jena.apache.org/) and [RDF4j](http://rdf4j.org/).
\ No newline at end of file
diff --git a/modules/shex/src/main/scala/es/weso/shex/compact/SchemaMaker.scala b/modules/shex/src/main/scala/es/weso/shex/compact/SchemaMaker.scala
index 1b486033..14137a26 100644
--- a/modules/shex/src/main/scala/es/weso/shex/compact/SchemaMaker.scala
+++ b/modules/shex/src/main/scala/es/weso/shex/compact/SchemaMaker.scala
@@ -1170,8 +1170,8 @@ class SchemaMaker extends ShExDocBaseVisitor[Any] with LazyLogging {
}
}
- override def visitBlankNode(ctx: BlankNodeContext): Builder[BNodeId] = {
- ok(BNodeId(removeUnderscore(ctx.BLANK_NODE_LABEL().getText())))
+ override def visitBlankNode(ctx: BlankNodeContext): Builder[BNode] = {
+ ok(BNode(removeUnderscore(ctx.BLANK_NODE_LABEL().getText())))
}
def removeUnderscore(str: String): String =
diff --git a/modules/shex/src/main/scala/es/weso/shex/implicits/decoderShex.scala b/modules/shex/src/main/scala/es/weso/shex/implicits/decoderShex.scala
index c65b82a6..46d9b975 100644
--- a/modules/shex/src/main/scala/es/weso/shex/implicits/decoderShex.scala
+++ b/modules/shex/src/main/scala/es/weso/shex/implicits/decoderShex.scala
@@ -37,8 +37,8 @@ object decoderShEx {
/* implicit lazy val keyDecoderShapeLabel: KeyDecoder[ShapeLabel] =
KeyDecoder.instance { str => parseShapeLabel(str).toOption } */
- implicit lazy val decodeBNodeId: Decoder[BNodeId] =
- Decoder[String].map(BNodeId(_))
+ implicit lazy val decodeBNodeId: Decoder[BNode] =
+ Decoder[String].map(BNode(_))
implicit lazy val decodeIRI: Decoder[IRI] =
Decoder[String].emap(parseIRI(_))
@@ -412,7 +412,7 @@ object decoderShEx {
def parseShapeLabel(str: String): Either[String, ShapeLabel] = {
str match {
// Be careful with the order...
- case bNodeRegex(bNodeId) => Either.right(BNodeLabel(BNodeId(bNodeId)))
+ case bNodeRegex(bNodeId) => Either.right(BNodeLabel(BNode(bNodeId)))
case iriRegex(i) => parseIRI(i).map(iri => IRILabel(iri))
case _ => Either.left(s"$str doesn't match IRI or BNode")
}
diff --git a/modules/shex/src/main/scala/es/weso/shex/shex.scala b/modules/shex/src/main/scala/es/weso/shex/shex.scala
index fad83606..074f4ee0 100644
--- a/modules/shex/src/main/scala/es/weso/shex/shex.scala
+++ b/modules/shex/src/main/scala/es/weso/shex/shex.scala
@@ -410,7 +410,7 @@ abstract sealed trait ShapeLabel {
}
}
case class IRILabel(iri: IRI) extends ShapeLabel
-case class BNodeLabel(bnode: BNodeId) extends ShapeLabel
+case class BNodeLabel(bnode: BNode) extends ShapeLabel
object Schema {
diff --git a/modules/shex/src/main/scala/es/weso/shex/shexR/RDF2ShEx.scala b/modules/shex/src/main/scala/es/weso/shex/shexR/RDF2ShEx.scala
index bd6e72c7..d3b82019 100644
--- a/modules/shex/src/main/scala/es/weso/shex/shexR/RDF2ShEx.scala
+++ b/modules/shex/src/main/scala/es/weso/shex/shexR/RDF2ShEx.scala
@@ -68,7 +68,7 @@ trait RDF2ShEx extends RDFParser with LazyLogging {
private def mkId(n: RDFNode): Option[ShapeLabel] = n match {
case iri: IRI => Some(IRILabel(iri))
- case bnode: BNodeId => Some(BNodeLabel(bnode))
+ case bnode: BNode => Some(BNodeLabel(bnode))
case _ => None // TODO: Raise an exception?
}
diff --git a/modules/shex/src/main/scala/es/weso/shex/validator/ShapeTyping.scala b/modules/shex/src/main/scala/es/weso/shex/validator/ShapeTyping.scala
index 0fb9c79d..c1d85f49 100644
--- a/modules/shex/src/main/scala/es/weso/shex/validator/ShapeTyping.scala
+++ b/modules/shex/src/main/scala/es/weso/shex/validator/ShapeTyping.scala
@@ -50,7 +50,7 @@ case class ShapeTyping(t: Typing[RDFNode, ShapeType, ViolationError, String]) ex
case None => Left(s"Can't create Result shape map for a shape expression without label. ShapeExpr: ${s.shape}")
case Some(lbl) => lbl.toRDFNode match {
case i: IRI => Either.right(IRIMapLabel(i))
- case b: BNodeId => Either.right(BNodeLabel(b))
+ case b: BNode => Either.right(BNodeLabel(b))
case _ => Left(s"Can't create Result shape map for a shape expression with label: $lbl")
}
}
diff --git a/modules/shex/src/main/scala/es/weso/shex/validator/ShowValidator.scala b/modules/shex/src/main/scala/es/weso/shex/validator/ShowValidator.scala
index 4b4a6500..6c495641 100644
--- a/modules/shex/src/main/scala/es/weso/shex/validator/ShowValidator.scala
+++ b/modules/shex/src/main/scala/es/weso/shex/validator/ShowValidator.scala
@@ -17,7 +17,7 @@ class ShowValidator(schema: Schema) {
n match {
case i: IRI => i.show
case l: Literal => l.getLexicalForm
- case b: BNodeId => "_:" + b.getLexicalForm
+ case b: BNode => "_:" + b.getLexicalForm
}
}
}
diff --git a/modules/shex/src/main/scala/es/weso/shex/validator/Validator.scala b/modules/shex/src/main/scala/es/weso/shex/validator/Validator.scala
index e820a004..5d9c3e7c 100644
--- a/modules/shex/src/main/scala/es/weso/shex/validator/Validator.scala
+++ b/modules/shex/src/main/scala/es/weso/shex/validator/Validator.scala
@@ -96,7 +96,7 @@ case class Validator(schema: Schema) extends ShowValidator(schema) with LazyLogg
private[validator] def mkShapeLabel(n: RDFNode): Check[ShapeLabel] = {
n match {
case i: IRI => ok(IRILabel(i))
- case b: BNodeId => ok(BNodeLabel(b))
+ case b: BNode => ok(BNodeLabel(b))
case _ => {
errStr(s"mkShapeLabel: Node ${n.show} can't be a shape")
}
diff --git a/modules/shex/src/test/scala/es/weso/shex/shexCodecTest.scala b/modules/shex/src/test/scala/es/weso/shex/shexCodecTest.scala
index c8352d8c..ff6c60da 100644
--- a/modules/shex/src/test/scala/es/weso/shex/shexCodecTest.scala
+++ b/modules/shex/src/test/scala/es/weso/shex/shexCodecTest.scala
@@ -34,7 +34,7 @@ class shexCodecTest extends FunSpec with Matchers with EitherValues {
describe("Shape Label") {
codecValueTest[IRI](IRI("x"))
codecValueTest[ShapeLabel](IRILabel(IRI("http://example.org/")))
- codecValueTest[ShapeLabel](BNodeLabel(BNodeId("x")))
+ codecValueTest[ShapeLabel](BNodeLabel(BNode("x")))
}
describe("Max codec") {
diff --git a/modules/shex/src/test/scala/rdf4j/es/weso/shex/validator/ShapeMapValidatorTest.scala b/modules/shex/src/test/scala/rdf4j/es/weso/shex/validator/ShapeMapValidatorTest.scala
new file mode 100644
index 00000000..91a04f1c
--- /dev/null
+++ b/modules/shex/src/test/scala/rdf4j/es/weso/shex/validator/ShapeMapValidatorTest.scala
@@ -0,0 +1,257 @@
+package es.weso.shex.validator
+
+import es.weso.rdf.rdf4j._
+import es.weso.shapeMaps.ShapeMap
+import es.weso.shex._
+import org.scalatest._
+
+import scala.util._
+
+class ShapeMapValidator_RDF4jTest extends FunSpec with Matchers with EitherValues {
+
+ describe("Simple Shape") {
+ val shexStr =
+ """
+ |prefix :
+ |prefix xsd:
+ |
+ |:S { :p . }
+ |:CanVote xsd:integer MinInclusive 18
+ """.stripMargin
+ val rdfStr =
+ """|prefix :
+ |:a :p :b .
+ |:c :p 1 .""".stripMargin
+
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:S", ":a@:S")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:S,:b@:S", ":a@:S,:b@!:S")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:S,:b@:S,:c@:S", ":a@:S,:b@!:S,:c@:S")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:S,:a@:T", ":a@:S,:a@!:T")
+ shouldValidateWithShapeMap(rdfStr, shexStr, "23@:CanVote", "23@:CanVote")
+ }
+
+ describe("Recursive shape") {
+ val shexStr =
+ """
+ |prefix :
+ |:S { :p @:S }
+ """.stripMargin
+ val rdfStr =
+ """|prefix :
+ |:a :p :b .
+ |:b :p :a .
+ |:c :p :c .
+ |:d :p 1 .""".stripMargin
+
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:S", ":a@:S,:b@:S")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:S,:b@:S,:c@:S", ":a@:S,:b@:S,:c@:S")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:S,:b@:S,:c@:S,:d@:S", ":a@:S,:b@:S,:c@:S,:d@!:S")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":d@:S", ":d@!:S")
+ }
+
+ describe("Two recursive shapes") {
+ val shexStr =
+ """
+ |prefix :
+ |:S { :p @:T }
+ |:T { :q @:S }
+ """.stripMargin
+ val rdfStr =
+ """|prefix :
+ |:a :p :b .
+ |:b :q :a .
+ |:c :p :c .
+ |:d :p 1 .""".stripMargin
+
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:S", ":a@:S,:b@:T")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":b@:T", ":a@:S,:b@:T")
+ }
+
+ describe("Regular expressions") {
+ val shexStr =
+ """
+ |prefix :
+ |:A { :p /\\d{2}/ }
+ """.stripMargin
+ val rdfStr =
+ """|prefix :
+ |:a :p "23" .
+ |""".stripMargin
+
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:A", ":a@:A")
+ }
+ describe("Shape with EXTRA") {
+ val shexStr =
+ """
+ |prefix :
+ |prefix xsd:
+ |
+ |:S EXTRA :p { :p [ 1 ] }
+ """.stripMargin
+ val rdfStr =
+ """|prefix :
+ |:a :p 1, 2 .
+ |:b :p 1 .
+ |:bad :p 2 .
+ |""".stripMargin
+
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:S", ":a@:S")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:S,:b@:S", ":a@:S,:b@:S")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:S,:b@:S,:bad@:S", ":a@:S,:b@:S,:bad@!:S")
+ }
+
+ describe("Shape with EXTRA and CLOSED") {
+ val shexStr =
+ """
+ |prefix :
+ |prefix xsd:
+ |
+ |:S CLOSED EXTRA :p {
+ | :p [ 1 2 3];
+ | :p [ 3 4 5]
+ |}
+ """.stripMargin
+ val rdfStr =
+ """|prefix :
+ |:a :p 1, 3 .
+ |:b :p 2, 5, 7 .
+ |:bad1 :p 2 .
+ |""".stripMargin
+
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:S,:b@:S,:bad1@:S", ":a@:S,:b@:S,:bad1@!:S")
+ }
+ describe("Shape with inverse arcs") {
+ val shexStr =
+ """
+ |prefix :
+ |prefix xsd:
+ |
+ |:S { ^:p @:T* }
+ |:T { :q . }
+ """.stripMargin
+ val rdfStr =
+ """|prefix :
+ |:t1 :p :s1; :q "a" .
+ |""".stripMargin
+
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":s1@:S", ":s1@:S,:t1@:T")
+ }
+
+ describe("Language stem") {
+ val shexStr =
+ """
+ |prefix :
+ |:A { :p [@es] }
+ """.stripMargin
+ val rdfStr =
+ """|prefix :
+ |:a :p "Hola"@es .
+ |:x :p "Hi"@en .
+ |:y :p "Hi" .
+ |:z :p 23 .
+ |""".stripMargin
+
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:A", ":a@:A")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:A,:x@:A,:y@:A,:z@:A", ":a@:A,:x@!:A,:y@!:A,:z@!:A")
+ }
+
+ describe("IRI stem") {
+ val shexStr =
+ """
+ |prefix :
+ |:A { :p [ ~ ] }
+ """.stripMargin
+ val rdfStr =
+ """|prefix :
+ |:a :p .
+ |:b :p :x .
+ |:x :p .
+ |:y :p "Hi" .
+ |:z :p 23 .
+ |""".stripMargin
+
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:A", ":a@:A")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:A,:b@:A", ":a@:A,:b@:A")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:A,:b@:A,:x@:A,:y@:A,:z@:A", ":a@:A,:b@:A,:x@!:A,:y@!:A,:z@!:A")
+ }
+ describe("Closed list") {
+ val shexStr =
+ """
+ |prefix :
+ |PREFIX rdf:
+ |:A { :p @:List }
+ |:List CLOSED {
+ | rdf:first @:B ;
+ | rdf:rest [rdf:nil] OR @:List
+ |}
+ |:B { :q . }
+ """.stripMargin
+ val rdfStr =
+ """|prefix :
+ |PREFIX rdf:
+ |:a :p (:b1 :b2) .
+ |:b1 :q 1 .
+ |:b2 :q 2 .
+ |:ls rdf:first :b1; rdf:rest rdf:nil.
+ |:x :p (:b1 :c1) .
+ |:c1 :r 1 .
+ |""".stripMargin
+
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":b1@:B", ":b1@:B")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":ls@:List", ":ls@:List,:b1@:B")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:A,:ls@:List", ":a@:A,:ls@:List,:b1@:B,:b2@:B")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:A,:x@:A,:ls@:List", ":a@:A,:ls@:List,:b1@:B,:b2@:B,:x@!:A")
+ }
+ describe("Labeled triple constraints") {
+ val shexStr =
+ """
+ |prefix :
+ |:A { $ (:p .; :q .) }
+ |:B { :r . ; & }
+ |""".stripMargin
+ val rdfStr =
+ """|prefix :
+ |:a :p 1 ; :q 1 .
+ |:b :p 1 ; :q 1; :r 1 .
+ |""".stripMargin
+
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:A", ":a@:A")
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@:A,:b@:B", ":a@:A,:b@:B")
+ }
+ describe("Relative uris with base") {
+ val shexStr =
+ """
+ |prefix :
+ |base
+ | { :p . }
+ |""".stripMargin
+ val rdfStr =
+ """|prefix :
+ |:a :p 1 .
+ |""".stripMargin
+
+ shouldValidateWithShapeMap(rdfStr, shexStr, ":a@", ":a@")
+ }
+ def shouldValidateWithShapeMap(
+ rdfStr: String,
+ shexStr: String,
+ shapeMapStr: String,
+ expected: String): Unit = {
+ it(s"Should validate ${shexStr} with ${rdfStr} and ${shapeMapStr} and result $expected") {
+ val validate = for {
+ rdf <- RDFAsRDF4jModel.fromChars(rdfStr, "Turtle")
+ shex <- Schema.fromString(shexStr, "ShExC", None)
+ shapeMap <- ShapeMap.fromCompact(shapeMapStr, None, rdf.getPrefixMap, shex.prefixMap)
+ fixedShapeMap <- ShapeMap.fixShapeMap(shapeMap, rdf, rdf.getPrefixMap, shex.prefixMap)
+ result <- Validator.validate(shex, fixedShapeMap, rdf)
+ expectedShapeMap <- ShapeMap.parseResultMap(expected, None, rdf, shex.prefixMap)
+ compare <- result.compareWith(expectedShapeMap)
+ } yield compare
+ validate match {
+ case Left(msg) => fail(s"Error: $msg")
+ case Right(v) => v should be(true)
+ }
+ }
+ }
+
+}
diff --git a/modules/srdf/README.md b/modules/srdf/README.md
new file mode 100644
index 00000000..bd20f363
--- /dev/null
+++ b/modules/srdf/README.md
@@ -0,0 +1,28 @@
+# Simple RDF
+
+This module represents a generic and simple RDF interface.
+The ShEx/SHACL library uses only methods provided by this interface so it can be used by different libraries.
+
+It defines 3 traits:
+- [RDFReader](https://github.com/labra/shaclex/blob/master/modules/srdf/src/main/scala/es/weso/rdf/RDFReader.scala) declares methods to read data from an RDF model
+- [RDFBuilder](https://github.com/labra/shaclex/blob/master/modules/srdf/src/main/scala/es/weso/rdf/RDFBuilder.scala) contains methods to build RDF models. Generate an empty model, add or remove triples.
+- [RDFReasoner](https://github.com/labra/shaclex/blob/master/modules/srdf/src/main/scala/es/weso/rdf/RDFReasoner.scala) extends an RDFReader with the possibility of extending the model with the triples inferred by some inference engine.
+
+It also defines the main components of RDF which are:
+- [RDFTriple](https://github.com/labra/shaclex/blob/master/modules/srdf/src/main/scala/es/weso/rdf/triples/RDFTriple.scala) declares an RDF triple.
+- [RDFNode](https://github.com/labra/shaclex/blob/master/modules/srdf/src/main/scala/es/weso/rdf/nodes/RDFNode.scala) declares RDF nodes (subjects or objects)
+- [IRI](https://github.com/labra/shaclex/blob/master/modules/srdf/src/main/scala/es/weso/rdf/nodes/IRI.scala) declares resources which are IRIs
+- [BNode]((https://github.com/labra/shaclex/blob/master/modules/srdf/src/main/scala/es/weso/rdf/nodes/BNode.scala)) declares Blank nodes
+- [Literal](https://github.com/labra/shaclex/blob/master/modules/srdf/src/main/scala/es/weso/rdf/nodes/Literal.scala) declares RDF literals
+- [Lang](https://github.com/labra/shaclex/blob/master/modules/srdf/src/main/scala/es/weso/rdf/nodes/Lang.scala) handles Languages
+
+Some utilities to work with RDF are:
+
+- [Prefixes](https://github.com/labra/shaclex/blob/master/modules/srdf/src/main/scala/es/weso/rdf/PrefixMap.scala) declares a prefix map (a map associating aliases with IRIs)
+- [RDFParser](https://github.com/labra/shaclex/blob/master/modules/srdf/src/main/scala/es/weso/rdf/parser/RDFParser.scala) a reader monad to parse RDF with some common utilities
+- [SHACLPath](https://github.com/labra/shaclex/blob/master/modules/srdf/src/main/scala/es/weso/rdf/path/SHACLPath.scala) declares SHACL paths
+
+This interface has two implementations:
+
+- [SRDFJena](https://github.com/labra/shaclex/tree/master/modules/srdfJena) for [Apache Jena](https://jena.apache.org/)
+- [SRDF4j](https://github.com/labra/shaclex/tree/master/modules/srdf4j) for [RDF4j](http://rdf4j.org/)
diff --git a/modules/srdf/src/main/scala/es/weso/rdf/nodes/BNode.scala b/modules/srdf/src/main/scala/es/weso/rdf/nodes/BNode.scala
new file mode 100644
index 00000000..f6d3083a
--- /dev/null
+++ b/modules/srdf/src/main/scala/es/weso/rdf/nodes/BNode.scala
@@ -0,0 +1,19 @@
+package es.weso.rdf.nodes
+
+case class BNode(id: String) extends RDFNode {
+
+ // @deprecated
+ def newBNodeId: BNode = {
+ val n = id.drop(1).toInt + 1
+ BNode("b" + n)
+ }
+
+ override def toString: String = {
+ "_:" + id
+ }
+
+ override def getLexicalForm = id
+
+}
+
+
diff --git a/modules/srdf/src/main/scala/es/weso/rdf/nodes/Lang.scala b/modules/srdf/src/main/scala/es/weso/rdf/nodes/Lang.scala
new file mode 100644
index 00000000..1ad9ca9a
--- /dev/null
+++ b/modules/srdf/src/main/scala/es/weso/rdf/nodes/Lang.scala
@@ -0,0 +1,33 @@
+package es.weso.rdf.nodes
+
+case class Lang(lang: String) {
+
+ // This should be the right regular expression for lang.
+ // We don't use this expression because the specification does not also.
+ val langtag_ex: String = "(\\A[xX]([\\x2d]\\p{Alnum}{1,8})*\\z)" +
+ "|(((\\A\\p{Alpha}{2,8}(?=\\x2d|\\z)){1}" +
+ "(([\\x2d]\\p{Alpha}{3})(?=\\x2d|\\z)){0,3}" +
+ "([\\x2d]\\p{Alpha}{4}(?=\\x2d|\\z))?" +
+ "([\\x2d](\\p{Alpha}{2}|\\d{3})(?=\\x2d|\\z))?" +
+ "([\\x2d](\\d\\p{Alnum}{3}|\\p{Alnum}{5,8})(?=\\x2d|\\z))*)" +
+ "(([\\x2d]([a-wyzA-WYZ](?=\\x2d))([\\x2d](\\p{Alnum}{2,8})+)*))*" +
+ "([\\x2d][xX]([\\x2d]\\p{Alnum}{1,8})*)?)\\z"
+
+ // TODO. Specification defines other ways to match languages
+ def matchLanguage(other: Lang) =
+ this.lang.toLowerCase == other.lang.toLowerCase
+
+ override def toString = lang match {
+ case "" => ""
+ case ls => "@" + ls
+ }
+
+ // The following code has been inspired by:
+ // http://stackoverflow.com/questions/7681183/how-can-i-define-a-custom-equality-operation-that-will-be-used-by-immutable-set
+ override def equals(o: Any) = o match {
+ case that: Lang => that.lang.toLowerCase == this.lang.toLowerCase
+ case _ => false
+ }
+
+ override def hashCode = lang.toLowerCase.hashCode
+}
diff --git a/modules/srdf/src/main/scala/es/weso/rdf/nodes/Literal.scala b/modules/srdf/src/main/scala/es/weso/rdf/nodes/Literal.scala
index 2defb21e..ef7f79a2 100644
--- a/modules/srdf/src/main/scala/es/weso/rdf/nodes/Literal.scala
+++ b/modules/srdf/src/main/scala/es/weso/rdf/nodes/Literal.scala
@@ -111,34 +111,3 @@ case class LangLiteral(lexicalForm: String, lang: Lang) extends Literal {
override def getLexicalForm = lexicalForm
}
-case class Lang(lang: String) {
-
- // This should be the right regular expression for lang.
- // We don't use this expression because the specification does not also.
- val langtag_ex: String = "(\\A[xX]([\\x2d]\\p{Alnum}{1,8})*\\z)" +
- "|(((\\A\\p{Alpha}{2,8}(?=\\x2d|\\z)){1}" +
- "(([\\x2d]\\p{Alpha}{3})(?=\\x2d|\\z)){0,3}" +
- "([\\x2d]\\p{Alpha}{4}(?=\\x2d|\\z))?" +
- "([\\x2d](\\p{Alpha}{2}|\\d{3})(?=\\x2d|\\z))?" +
- "([\\x2d](\\d\\p{Alnum}{3}|\\p{Alnum}{5,8})(?=\\x2d|\\z))*)" +
- "(([\\x2d]([a-wyzA-WYZ](?=\\x2d))([\\x2d](\\p{Alnum}{2,8})+)*))*" +
- "([\\x2d][xX]([\\x2d]\\p{Alnum}{1,8})*)?)\\z"
-
- // TODO. Specification defines other ways to match languages
- def matchLanguage(other: Lang) =
- this.lang.toLowerCase == other.lang.toLowerCase
-
- override def toString = lang match {
- case "" => ""
- case ls => "@" + ls
- }
-
- // The following code has been inspired by:
- // http://stackoverflow.com/questions/7681183/how-can-i-define-a-custom-equality-operation-that-will-be-used-by-immutable-set
- override def equals(o: Any) = o match {
- case that: Lang => that.lang.toLowerCase == this.lang.toLowerCase
- case _ => false
- }
-
- override def hashCode = lang.toLowerCase.hashCode
-}
diff --git a/modules/srdf/src/main/scala/es/weso/rdf/nodes/RDFNode.scala b/modules/srdf/src/main/scala/es/weso/rdf/nodes/RDFNode.scala
index 12f1b7e0..aa2ce5a6 100644
--- a/modules/srdf/src/main/scala/es/weso/rdf/nodes/RDFNode.scala
+++ b/modules/srdf/src/main/scala/es/weso/rdf/nodes/RDFNode.scala
@@ -9,7 +9,7 @@ abstract class RDFNode {
}
def isBNode = this match {
- case _: BNodeId => true
+ case _: BNode => true
case _ => false
}
@@ -31,24 +31,6 @@ abstract class RDFNode {
}
-case class BNodeId(id: String) extends RDFNode {
-
- // @deprecated
- def newBNodeId: BNodeId = {
- val n = id.drop(1).toInt + 1
- BNodeId("b" + n)
- }
-
- override def toString: String = {
- "_:" + id
- }
-
- override def getLexicalForm = id
-
-}
-
-object InitialBNodeId extends BNodeId("b0")
-
object RDFNode {
val xsd = "http://www.w3.org/2001/XMLSchema#"
val rdfSyntax = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
@@ -81,7 +63,7 @@ object RDFNode {
val integerRegex = raw"(\d*)".r
s match {
case iriRegex(iri) => Right(IRI(iri))
- case bNodeRegex(bnodeId) => Right(BNodeId(bnodeId))
+ case bNodeRegex(bnodeId) => Right(BNode(bnodeId))
case literalRegex(str) => Right(StringLiteral(str))
case integerRegex(s) => {
try Right(IntegerLiteral(s.toInt))
diff --git a/modules/srdf/src/main/scala/es/weso/rdf/triples/RDFTriple.scala b/modules/srdf/src/main/scala/es/weso/rdf/triples/RDFTriple.scala
index 9492207a..8a3ef145 100644
--- a/modules/srdf/src/main/scala/es/weso/rdf/triples/RDFTriple.scala
+++ b/modules/srdf/src/main/scala/es/weso/rdf/triples/RDFTriple.scala
@@ -9,9 +9,9 @@ case class RDFTriple(subj: RDFNode, pred: IRI, obj: RDFNode) {
def hasObject(node: RDFNode): Boolean = obj == node
def hasPredicate(p: IRI): Boolean = pred == p
- def extractBNode(node: RDFNode): Set[BNodeId] = {
+ def extractBNode(node: RDFNode): Set[BNode] = {
node match {
- case b @ BNodeId(_) => Set(b)
+ case b @ BNode(_) => Set(b)
case _ => Set()
}
}
@@ -23,7 +23,7 @@ case class RDFTriple(subj: RDFNode, pred: IRI, obj: RDFNode) {
}
}
- def bNodes: Set[BNodeId] = {
+ def bNodes: Set[BNode] = {
extractBNode(subj) ++
extractBNode(obj)
}
@@ -61,8 +61,8 @@ object RDFTriple {
/**
* collects BNodes in a set of triples
*/
- def collectBNodes(triples: Set[RDFTriple]): Set[BNodeId] = {
- triples.foldLeft(Set[BNodeId]())((set, triple) =>
+ def collectBNodes(triples: Set[RDFTriple]): Set[BNode] = {
+ triples.foldLeft(Set[BNode]())((set, triple) =>
set ++ triple.bNodes)
}
diff --git a/modules/srdf/src/main/scala/es/weso/utils/PrefixMapUtils.scala b/modules/srdf/src/main/scala/es/weso/utils/PrefixMapUtils.scala
index 3f696322..2aa6ea87 100644
--- a/modules/srdf/src/main/scala/es/weso/utils/PrefixMapUtils.scala
+++ b/modules/srdf/src/main/scala/es/weso/utils/PrefixMapUtils.scala
@@ -22,7 +22,7 @@ object PrefixMapUtils {
def showRDFNode(n: RDFNode)(pm: PrefixMap): String = {
n match {
case i: IRI => showIRI(i)(pm)
- case b: BNodeId => b.toString
+ case b: BNode => b.toString
case s: StringLiteral => escapeLexicalForm(s.lexicalForm)
case s: DatatypeLiteral => escapeLexicalForm(s.lexicalForm) + "^^" + s.dataType.toString
case l: Literal => l.toString
diff --git a/modules/srdf4j/LICENSE b/modules/srdf4j/LICENSE
new file mode 100644
index 00000000..4e15fa53
--- /dev/null
+++ b/modules/srdf4j/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Jose Emilio Labra Gayo
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/modules/srdf4j/README.md b/modules/srdf4j/README.md
new file mode 100644
index 00000000..3cd1d3cf
--- /dev/null
+++ b/modules/srdf4j/README.md
@@ -0,0 +1,3 @@
+# SRDFj
+
+Implementation of the [Simple RDF](https://github.com/labra/shaclex/tree/master/modules/srdf) interface based on [SRDFj](http://rdf4j.org/)
\ No newline at end of file
diff --git a/modules/srdf4j/src/main/scala/es/weso/rdf/rdf4j/RDF4jMapper.scala b/modules/srdf4j/src/main/scala/es/weso/rdf/rdf4j/RDF4jMapper.scala
new file mode 100644
index 00000000..a2018f8f
--- /dev/null
+++ b/modules/srdf4j/src/main/scala/es/weso/rdf/rdf4j/RDF4jMapper.scala
@@ -0,0 +1,131 @@
+package es.weso.rdf.rdf4j
+
+import es.weso.rdf.nodes._
+import es.weso.rdf.triples._
+
+import scala.collection.JavaConverters._
+import org.eclipse.rdf4j.model.{BNode => BNode_RDF4j, IRI => IRI_RDF4j, Literal => Literal_RDF4j, _}
+import org.eclipse.rdf4j.model.impl.{SimpleValueFactory, BooleanLiteral => BooleanLiteral_RDF4j, DecimalLiteral => DecimalLiteral_RDF4j, IntegerLiteral => IntegerLiteral_RDF4j}
+import cats.implicits._
+import org.eclipse.rdf4j.model.vocabulary.XMLSchema
+
+import scala.util.{Failure, Success, Try}
+
+object RDF4jMapper {
+
+ lazy val valueFactory = SimpleValueFactory.getInstance;
+
+ private[rdf4j] def literal2Literal(lit: Literal_RDF4j): Literal = {
+ lit match {
+ case bl: BooleanLiteral_RDF4j => BooleanLiteral(bl.booleanValue())
+ case il: IntegerLiteral_RDF4j => IntegerLiteral(il.intValue())
+ case dl: DecimalLiteral_RDF4j => DecimalLiteral(dl.decimalValue())
+ case _ if (lit.getDatatype == XMLSchema.STRING) => StringLiteral(lit.stringValue())
+ case _ if (lit.getLanguage.isPresent) => LangLiteral(lit.stringValue, Lang(lit.getLanguage.get()))
+ case _ => DatatypeLiteral(lit.stringValue(), iri2iri(lit.getDatatype))
+ }
+ }
+
+ private[rdf4j] def iri2iri(iri: IRI_RDF4j): IRI = IRI(iri.toString)
+
+ private[rdf4j] def bnode2Bnode(b: BNode_RDF4j): BNode = BNode(b.getID)
+
+ private[rdf4j] def value2RDFNode(value: Value): RDFNode = {
+ value match {
+ case lit: Literal_RDF4j => literal2Literal(lit)
+ case bnode: BNode_RDF4j => bnode2Bnode(bnode)
+ case iri: IRI_RDF4j => iri2iri(iri)
+ }
+ }
+
+ private[rdf4j] def statement2RDFTriple(s: Statement): RDFTriple = {
+ RDFTriple(resource2RDFNode(s.getSubject), iri2iri(s.getPredicate), value2RDFNode(s.getObject))
+ }
+
+ private[rdf4j] def resource2RDFNode(r: Resource): RDFNode = {
+ r match {
+ case iri: IRI_RDF4j => iri2iri(iri)
+ case bnode: BNode_RDF4j => bnode2Bnode(bnode)
+ case lit: Literal_RDF4j => literal2Literal(lit)
+ }
+ }
+
+ private[rdf4j] def iri2Property(iri: IRI): IRI_RDF4j = {
+ valueFactory.createIRI(iri.str)
+ }
+
+ private[rdf4j] def rdfNode2Resource(r: RDFNode): Either[String, Resource] = {
+ r match {
+ case iri: IRI => Right(valueFactory.createIRI(iri.str))
+ case bnode: BNode => Right(valueFactory.createBNode(bnode.id))
+ case _ => Left(s"Cannot convert rdfNode: $r to Resource")
+ }
+ }
+
+ private[rdf4j] def rdfNode2Value(r: RDFNode): Value = r match {
+ case iri: IRI => iri2Property(iri)
+ case bnode: BNode => valueFactory.createBNode(bnode.id)
+ case StringLiteral(str) => valueFactory.createLiteral(str)
+ case BooleanLiteral(b) => valueFactory.createLiteral(b)
+ case IntegerLiteral(i) => valueFactory.createLiteral(i)
+ case DecimalLiteral(d) => valueFactory.createLiteral(d.bigDecimal)
+ case DoubleLiteral(d) => valueFactory.createLiteral(d)
+ case DatatypeLiteral(l,d) => valueFactory.createLiteral(l,iri2Property((d)))
+ case LangLiteral(l,Lang(lang)) => valueFactory.createLiteral(l,lang)
+ }
+
+ private[rdf4j] def newBNode(): BNode_RDF4j = valueFactory.createBNode()
+
+ private[rdf4j] def statements2RDFTriples(statements: Set[Statement]): Set[RDFTriple] = {
+ statements.map(statement2RDFTriple(_))
+ }
+
+ private[rdf4j] def triplesSubject(resource: Resource, model: Model): Set[Statement] = {
+ model.filter(resource, null, null).asScala.toSet
+ }
+
+ private[rdf4j] def triplesPredicate(iri: IRI_RDF4j, model: Model): Set[Statement] = {
+ model.filter(null, iri, null).asScala.toSet
+ }
+
+ private[rdf4j] def triplesObject(value: Value, model: Model): Set[Statement] = {
+ model.filter(null, null, value).asScala.toSet
+ }
+
+ private[rdf4j] def triplesPredicateObject(iri: IRI_RDF4j, obj: Value, model: Model): Set[Statement] = {
+ model.filter(null, iri, obj).asScala.toSet
+ }
+
+ private[rdf4j] def rdfTriples2Model(triples: Set[RDFTriple]): Either[String, Model] = for {
+ ss <- triples.map(rdfTriple2Statement(_)).toList.sequence
+ } yield {
+
+ ???
+ }
+
+ private[rdf4j] def rdfTriple2Statement(triple: RDFTriple): Either[String, Statement] = {
+ val pred = iri2Property(triple.pred)
+ val obj = rdfNode2Value(triple.obj)
+ for {
+ subj <- rdfNode2Resource(triple.subj)
+ } yield valueFactory.createStatement(subj, pred, obj)
+ }
+
+ // TODO: Check rules of datatype
+ private[rdf4j] def wellTypedDatatype(node: RDFNode, expectedDatatype: IRI): Either[String,Boolean] = node match {
+ case l: Literal => Try {
+ val datatypeIRI = valueFactory.createIRI(l.dataType.str)
+ val rdf4jLiteral = valueFactory.createLiteral(l.getLexicalForm, datatypeIRI)
+ val x = rdf4jLiteral.getLabel
+ rdf4jLiteral.getDatatype
+ } match {
+ case Success(iri) => {
+ Right(iri.stringValue == expectedDatatype.str)
+ }
+ case Failure(e) => Left(e.getMessage)
+ }
+ // case DatatypeLiteral(_,dt) => Right(dt == expectedDatatype)
+ case _ => Right(false)
+ }
+
+}
\ No newline at end of file
diff --git a/modules/srdf4j/src/main/scala/es/weso/rdf/rdf4j/RDFAsRDF4jModel.scala b/modules/srdf4j/src/main/scala/es/weso/rdf/rdf4j/RDFAsRDF4jModel.scala
new file mode 100644
index 00000000..939e4450
--- /dev/null
+++ b/modules/srdf4j/src/main/scala/es/weso/rdf/rdf4j/RDFAsRDF4jModel.scala
@@ -0,0 +1,315 @@
+package es.weso.rdf.rdf4j
+
+import java.io._
+import es.weso.rdf._
+import es.weso.rdf.path.SHACLPath
+import es.weso.rdf.triples.RDFTriple
+import io.circe.Json
+import org.eclipse.rdf4j.model.{
+ IRI => IRI_RDF4j,
+ BNode => _,
+ Literal => _,
+ _
+}
+import es.weso.rdf.nodes._
+import org.eclipse.rdf4j.model.util.ModelBuilder
+import org.eclipse.rdf4j.rio.RDFFormat._
+import org.eclipse.rdf4j.rio.{RDFFormat, Rio}
+import org.apache.commons.io.input.CharSequenceInputStream
+import scala.util._
+import scala.collection.JavaConverters._
+import RDF4jMapper._
+import cats._
+import cats.implicits._
+
+case class RDFAsRDF4jModel(model: Model)
+ extends RDFReader
+ with RDFBuilder
+ with RDFReasoner {
+
+ type Rdf = RDFAsRDF4jModel
+
+ override def fromString(cs: CharSequence,
+ format: String,
+ base: Option[String] = None): Either[String, Rdf] = {
+ val builder = new ModelBuilder()
+ val baseURI = base.getOrElse("")
+ for {
+ format <- getRDFFormat(format)
+ model <- Try {
+ val is : InputStream = new CharSequenceInputStream(cs,"UTF-8")
+ Rio.parse(is, baseURI, format)
+ }.fold(e => Left(s"Exception: ${e.getMessage}\nBase:$base, format: $format\n$cs"),
+ Right(_)
+ )
+ } yield RDFAsRDF4jModel(model)
+ }
+
+ private def getRDFFormat(name: String): Either[String,RDFFormat] = {
+ name.toUpperCase match {
+ case "TURTLE" => Right(TURTLE)
+ case "JSONLD" => Right(JSONLD)
+ case "RDFXML" => Right(RDFXML)
+ case x => Left(s"Unsupported syntax $x")
+ }
+ }
+
+ override def serialize(formatName: String): Either[String, String] = for {
+ format <- getRDFFormat(formatName)
+ str <- Try {
+ val out: StringWriter = new StringWriter()
+ Rio.write(model,out,format)
+ out.toString
+ }.fold(e => Left(s"Error serializing RDF to format $formatName: $e"),
+ Right(_)
+ )
+ } yield str
+
+ private def extend_rdfs: Rdf = {
+ this
+ // TODO: Check how to add inference in RDF4j
+ /* val infModel = ModelFactory.createRDFSModel(model)
+ RDFAsJenaModel(infModel) */
+ }
+
+ // TODO: this implementation only returns subjects
+ override def iris(): Set[IRI] = {
+ val resources: Set[Resource] = model.subjects().asScala.toSet
+ resources.filter(_.isInstanceOf[IRI_RDF4j]).map(_.asInstanceOf[IRI_RDF4j].toString).map(IRI(_))
+ }
+
+ override def subjects(): Set[RDFNode] = {
+ val resources: Set[Resource] = model.subjects().asScala.toSet
+ resources.map(r => resource2RDFNode(r))
+ }
+
+ override def rdfTriples(): Set[RDFTriple] = {
+ model.asScala.toSet.map(statement2RDFTriple(_))
+ }
+
+ override def triplesWithSubject(node: RDFNode): Set[RDFTriple] = {
+ val maybeResource = rdfNode2Resource(node).toOption
+ val empty: Set[RDFTriple] = Set()
+ maybeResource.fold(empty) {
+ case resource =>
+ val statements: Set[Statement] = triplesSubject(resource, model)
+ statements2RDFTriples(statements)
+ }
+ }
+
+ /**
+ * return the SHACL instances of a node `cls`
+ * A node `node` is a shacl instance of `cls` if `node rdf:type/rdfs:subClassOf* cls`
+ */
+ override def getSHACLInstances(c: RDFNode): Seq[RDFNode] = {
+ ???
+ /*
+ val cJena: JenaRDFNode = JenaMapper.rdfNode2JenaNode(c, model)
+ JenaUtils.getSHACLInstances(cJena, model).map(n => JenaMapper.jenaNode2RDFNode(n))
+ */
+ }
+
+ override def hasSHACLClass(n: RDFNode, c: RDFNode): Boolean = {
+ /*
+ val nJena: JenaRDFNode = JenaMapper.rdfNode2JenaNode(n, model)
+ val cJena: JenaRDFNode = JenaMapper.rdfNode2JenaNode(c, model)
+ JenaUtils.hasClass(nJena, cJena, model)
+ */
+ ???
+ }
+
+ override def nodesWithPath(path: SHACLPath): Set[(RDFNode, RDFNode)] = {
+ /*
+ val jenaPath: Path = JenaMapper.path2JenaPath(path, model)
+ val pairs = JenaUtils.getNodesFromPath(jenaPath, model).
+ map(p => (JenaMapper.jenaNode2RDFNode(p._1), JenaMapper.jenaNode2RDFNode(p._2)))
+ pairs.toSet */
+ ???
+ }
+
+ override def objectsWithPath(subj: RDFNode, path: SHACLPath): Set[RDFNode] = {
+ ???
+ /*
+ val jenaNode: JenaRDFNode = JenaMapper.rdfNode2JenaNode(subj, model)
+ val jenaPath: Path = JenaMapper.path2JenaPath(path, model)
+ val nodes = JenaUtils.objectsFromPath(jenaNode, jenaPath, model).map(n => JenaMapper.jenaNode2RDFNode(n))
+ nodes.toSet
+ */
+ }
+
+ override def subjectsWithPath(path: SHACLPath, obj: RDFNode): Set[RDFNode] = {
+ ???
+ /*
+ val jenaNode: JenaRDFNode = JenaMapper.rdfNode2JenaNode(obj, model)
+ val jenaPath: Path = JenaMapper.path2JenaPath(path, model)
+ val nodes = JenaUtils.subjectsFromPath(jenaNode, jenaPath, model).map(n => JenaMapper.jenaNode2RDFNode(n))
+ nodes.toSet
+ */
+ }
+
+ override def triplesWithPredicate(iri: IRI): Set[RDFTriple] = {
+ val pred = iri2Property(iri)
+ statements2RDFTriples(triplesPredicate(pred, model))
+ }
+
+ override def triplesWithObject(node: RDFNode): Set[RDFTriple] = {
+ val obj = rdfNode2Resource(node).toOption
+ val empty: Set[RDFTriple] = Set()
+ obj.fold(emptySet) { o => {
+ statements2RDFTriples(triplesObject(o, model))
+ }
+ }
+ }
+
+ private lazy val emptySet: Set[RDFTriple] = Set()
+
+ override def triplesWithPredicateObject(p: IRI, o: RDFNode): Set[RDFTriple] = {
+ val prop = iri2Property(p)
+ val maybeObj = rdfNode2Resource(o).toOption
+ maybeObj.fold(emptySet) { obj =>
+ statements2RDFTriples(triplesPredicateObject(prop,obj, model))
+ }
+ }
+
+ override def getPrefixMap: PrefixMap = {
+ PrefixMap {
+ val nsSet: Set[Namespace] = model.getNamespaces.asScala.toSet
+ nsSet.map(ns => (Prefix(ns.getPrefix), IRI(ns.getName))).toMap
+ }
+ }
+
+ override def addPrefixMap(pm: PrefixMap): Rdf = {
+ pm.pm.foreach {
+ case (Prefix(prefix),value) => model.setNamespace(prefix,value.str)
+ }
+ this
+ }
+
+ override def addTriples(triples: Set[RDFTriple]): Rdf = {
+ ???
+/*
+ val newModel = JenaMapper.RDFTriples2Model(triples, model)
+ model.add(newModel)
+ this
+*/
+ }
+
+ // TODO: This is not efficient
+ override def rmTriple(triple: RDFTriple): Rdf = {
+ ???
+/*
+ val empty = ModelFactory.createDefaultModel
+ val model2delete = JenaMapper.RDFTriples2Model(Set(triple), empty)
+ model.difference(model2delete)
+ this
+*/
+ }
+
+ override def createBNode: (RDFNode, Rdf) = {
+ (BNode(newBNode.getID), this)
+ }
+
+ override def addPrefix(alias: String, iri: String): Rdf = {
+ model.setNamespace(alias,iri)
+ this
+ }
+
+ /*def qName(str: String): IRI = {
+ IRI(model.expandPrefix(str))
+ }
+*/
+ override def empty: Rdf = {
+ RDFAsRDF4jModel.empty
+ }
+
+ override def checkDatatype(node: RDFNode, datatype: IRI): Either[String, Boolean] =
+ wellTypedDatatype(node,datatype)
+
+ /*private def resolveString(str: String): Either[String,IRI] = {
+ Try(IRIResolver.resolveString(str)).fold(
+ e => Left(e.getMessage),
+ iri => Right(IRI(iri))
+ )
+ }*/
+ private val NONE = "NONE"
+ private val RDFS = "RDFS"
+ private val OWL = "OWL"
+
+ override def applyInference(inference: String): Either[String, Rdf] = {
+ Right(this) // TODO (as it is doesn't apply inference)
+/*
+ inference.toUpperCase match {
+ case `NONE` => Right(this)
+ case `RDFS` => JenaUtils.inference(model, RDFS).map(RDFAsJenaModel(_))
+ case `OWL` => JenaUtils.inference(model, OWL).map(RDFAsJenaModel(_))
+ case other => Left(s"Unsupported inference $other")
+ }
+*/
+ }
+
+ def availableInferenceEngines: List[String] = List(NONE, RDFS, OWL)
+
+ override def querySelect(queryStr: String): Either[String, List[Map[String,RDFNode]]] =
+ Left(s"Not implemented querySelect for RDf4j yet")
+
+ override def queryAsJson(queryStr: String): Either[String, Json] =
+ Left(s"Not implemented queryAsJson for RDf4j")
+
+
+
+ override def getNumberOfStatements(): Either[String,Int] =
+ Right(model.size)
+
+}
+
+
+object RDFAsRDF4jModel {
+
+ def apply(): RDFAsRDF4jModel = {
+ RDFAsRDF4jModel.empty
+ }
+
+ lazy val empty: RDFAsRDF4jModel = {
+ val builder = new ModelBuilder()
+ RDFAsRDF4jModel(builder.build)
+ }
+
+ def fromURI(uri: String, format: String = "TURTLE", base: Option[String] = None): Either[String,RDFAsRDF4jModel] = {
+ ???
+/*
+ val baseURI = base.getOrElse(FileUtils.currentFolderURL)
+ Try {
+ val m = ModelFactory.createDefaultModel()
+ RDFDataMgr.read(m, uri, baseURI, shortnameToLang(format))
+ RDFAsJenaModel(JenaUtils.relativizeModel(m))
+ }.fold(e => Left(s"Exception accessing uri $uri: ${e.getMessage}"),
+ Right(_)
+ )
+*/
+ }
+
+ def fromFile(file: File, format: String, base: Option[String] = None): Either[String, RDFAsRDF4jModel] = {
+ ???
+/*
+ val baseURI = base.getOrElse("")
+ Try {
+ val m = ModelFactory.createDefaultModel()
+ val is: InputStream = new FileInputStream(file)
+ RDFDataMgr.read(m, is, baseURI, shortnameToLang(format))
+ RDFAsJenaModel(JenaUtils.relativizeModel(m))
+ }.fold(e => Left(s"Exception parsing RDF from file ${file.getName}: ${e.getMessage}"),
+ Right(_))
+*/
+ }
+
+ def fromChars(cs: CharSequence, format: String, base: Option[String] = None): Either[String,RDFAsRDF4jModel] = {
+ RDFAsRDF4jModel.empty.fromString(cs, format, base)
+ }
+
+ def availableFormats: List[String] = {
+ val formats = List(TURTLE,JSONLD,RDFXML)
+ formats.map(_.getName)
+ }
+
+
+}
diff --git a/modules/srdf4j/src/test/scala/es/weso/rdf/rdf4j/RDF4jParserTest.scala b/modules/srdf4j/src/test/scala/es/weso/rdf/rdf4j/RDF4jParserTest.scala
new file mode 100644
index 00000000..ad1a692f
--- /dev/null
+++ b/modules/srdf4j/src/test/scala/es/weso/rdf/rdf4j/RDF4jParserTest.scala
@@ -0,0 +1,78 @@
+package es.weso.rdf.rdf4j
+
+import es.weso.rdf.{Prefix, PrefixMap}
+import es.weso.rdf.nodes._
+import es.weso.rdf.parser._
+import org.scalatest._
+
+import scala.util._
+
+class RDF4jParserTest extends FunSpec with Matchers with EitherValues with OptionValues {
+
+ describe("RDF4jParser") {
+ it(s"Should parse simple Turtle string") {
+ val str =
+ """prefix :
+ |:x :p :y .
+ |:y a 1 .
+ """.stripMargin
+ val mayberdf = RDFAsRDF4jModel.fromChars(str,"Turtle",None)
+ mayberdf match {
+ case Left(str) => fail(s"Error parsing: $str")
+ case Right(rdf) => rdf.getNumberOfStatements().right.value should be(2)
+ }
+ }
+ }
+
+ describe(s"RDF4j API as SRDF") {
+ it(s"Should be able to parse prefix maps") {
+ val str =
+ """prefix :
+ |:x :p :y .
+ |:y a 1 .
+ """.stripMargin
+ val mayberdf = RDFAsRDF4jModel.fromChars(str,"Turtle",None)
+ mayberdf match {
+ case Left(str) => fail(s"Error parsing: $str")
+ case Right(rdf) => {
+ val pm = rdf.getPrefixMap()
+ pm.getIRI("").value should be(IRI("http://example.org/"))
+ }
+ }
+ }
+
+ it(s"Should be able to extend the prefix map") {
+ val str =
+ """prefix :
+ |:x :p :y .
+ |:y a 1 .
+ """.stripMargin
+ val mayberdf = RDFAsRDF4jModel.fromChars(str,"Turtle",None)
+ mayberdf match {
+ case Left(str) => fail(s"Error parsing: $str")
+ case Right(rdf) => {
+ rdf.addPrefixMap(PrefixMap(Map(Prefix("kiko") -> IRI("http://kiko.org"))))
+ rdf.getPrefixMap.getIRI("kiko").value should be(IRI("http://kiko.org"))
+ rdf.getPrefixMap.getIRI("pepe") should be(None)
+ }
+ }
+ }
+
+ it(s"Should be able to get subjects") {
+ val str =
+ """prefix :
+ |:x :p :y .
+ |:y a 1 .
+ """.stripMargin
+ val mayberdf = RDFAsRDF4jModel.fromChars(str,"Turtle",None)
+ mayberdf match {
+ case Left(str) => fail(s"Error parsing: $str")
+ case Right(rdf) => {
+ val ts = rdf.triplesWithSubject(IRI("http://example.org/x"))
+ ts.size should be(1)
+ }
+ }
+ }
+
+ }
+}
diff --git a/modules/srdfJena/README.md b/modules/srdfJena/README.md
new file mode 100644
index 00000000..37ef124d
--- /dev/null
+++ b/modules/srdfJena/README.md
@@ -0,0 +1,5 @@
+# srdfJena module
+
+Implements the [SRDF](https://github.com/labra/shaclex/tree/master/modules/srdf) interface.
+
+The main class is [RDFAsJenaModel](https://github.com/labra/shaclex/blob/master/modules/srdfJena/src/main/scala/es/weso/rdf/jena/RDFAsJenaModel.scala#L29) which can be used to declare RDFReader's and RDFBuilder's on top of Jena Models
\ No newline at end of file
diff --git a/modules/srdfJena/src/main/scala/es/weso/rdf/jena/Endpoint.scala b/modules/srdfJena/src/main/scala/es/weso/rdf/jena/Endpoint.scala
index afe4e9ff..961243a8 100644
--- a/modules/srdfJena/src/main/scala/es/weso/rdf/jena/Endpoint.scala
+++ b/modules/srdfJena/src/main/scala/es/weso/rdf/jena/Endpoint.scala
@@ -134,7 +134,7 @@ case class Endpoint(endpoint: String) extends RDFReader with RDFReasoner {
def jena2rdfnode(r: JenaRDFNode): RDFNode = {
if (r.isAnon) {
- BNodeId(r.asNode.getBlankNodeId.getLabelString)
+ BNode(r.asNode.getBlankNodeId.getLabelString)
} else if (r.isURIResource) {
IRI(r.asResource.getURI())
} else if (r.isLiteral) {
diff --git a/modules/srdfJena/src/main/scala/es/weso/rdf/jena/JenaMapper.scala b/modules/srdfJena/src/main/scala/es/weso/rdf/jena/JenaMapper.scala
index 41fc8f3b..51c41bb6 100644
--- a/modules/srdfJena/src/main/scala/es/weso/rdf/jena/JenaMapper.scala
+++ b/modules/srdfJena/src/main/scala/es/weso/rdf/jena/JenaMapper.scala
@@ -60,7 +60,7 @@ object JenaMapper {
def rdfNode2Resource(n: RDFNode, m: JenaModel): Option[Resource] = {
n match {
case i: IRI => Some(m.getResource(resolve(i)))
- case BNodeId(id) => {
+ case BNode(id) => {
// Creates the BNode if it doesn't exist
Some(m.createResource(new AnonId(id)))
}
@@ -94,7 +94,7 @@ object JenaMapper {
// println(s"jenaNode2RDFNode: URI = ${r.asResource().getURI()}")
IRI(r.asResource().getURI)
} else if (r.isAnon) {
- BNodeId(r.asResource().getId.getLabelString)
+ BNode(r.asResource().getId.getLabelString)
} else if (r.isLiteral) {
val lit = r.asLiteral()
if (lit.getLanguage() != "") {
@@ -134,7 +134,7 @@ object JenaMapper {
def createResource(m: JenaModel, node: RDFNode): Resource = {
node match {
- case BNodeId(id) => m.createResource(new AnonId(id.toString))
+ case BNode(id) => m.createResource(new AnonId(id.toString))
case i: IRI => m.createResource(resolve(i))
case _ => throw new Exception("Cannot create a resource from " + node)
}
@@ -148,7 +148,7 @@ object JenaMapper {
val xsdboolean = xsd + "boolean"
node match {
- case BNodeId(id) =>
+ case BNode(id) =>
m.createResource(new AnonId(id.toString))
case i: IRI =>
m.createResource(resolve(i))
@@ -237,14 +237,14 @@ object JenaMapper {
case l: es.weso.rdf.nodes.Literal => {
Try {
val jenaLiteral = emptyModel.createTypedLiteral(l.getLexicalForm, l.dataType.str)
- jenaLiteral.getValue() // if it is ill-typed it raises an exception
- (jenaLiteral.getDatatypeURI)
+ jenaLiteral.getValue // if it is ill-typed it raises an exception
+ jenaLiteral.getDatatypeURI
} match {
case Success(iri) => {
// println(s"JenaMapper.welltypedDatatype, $node. Comparing $expectedDatatype with $iri")
Right(iri == expectedDatatype.str)
}
- case Failure(e) => Left(e.getMessage())
+ case Failure(e) => Left(e.getMessage)
}
}
case _ => Right(false)
diff --git a/modules/srdfJena/src/main/scala/es/weso/rdf/jena/RDFAsJenaModel.scala b/modules/srdfJena/src/main/scala/es/weso/rdf/jena/RDFAsJenaModel.scala
index 30ea3d4a..b0da5280 100644
--- a/modules/srdfJena/src/main/scala/es/weso/rdf/jena/RDFAsJenaModel.scala
+++ b/modules/srdfJena/src/main/scala/es/weso/rdf/jena/RDFAsJenaModel.scala
@@ -37,7 +37,7 @@ case class RDFAsJenaModel(model: Model)
override def fromString(cs: CharSequence,
format: String,
- base: Option[String] = None): Either[String, RDFAsJenaModel] = {
+ base: Option[String] = None): Either[String, Rdf] = {
Try {
val m = ModelFactory.createDefaultModel
val str_reader = new StringReader(cs.toString)
@@ -49,14 +49,26 @@ case class RDFAsJenaModel(model: Model)
)
}
- override def serialize(format: String): Either[String, String] = {
- // TODO: Check if format exists...
- val out: StringWriter = new StringWriter()
- model.write(out, format)
- Right(out.toString)
+ private def getRDFFormat(formatName: String): Either[String,String] = {
+ val supportedFormats = RDFLanguages.getRegisteredLanguages()
+ formatName.toUpperCase match {
+ case format if supportedFormats.contains(format) => Right(format)
+ case unknown => Left(s"Unsupported format $unknown")
+ }
}
- def extend_rdfs: Rdf = {
+ override def serialize(formatName: String): Either[String, String] = for {
+ format <- getRDFFormat(formatName)
+ str <- Try {
+ val out: StringWriter = new StringWriter()
+ model.write(out, format)
+ out.toString
+ }.fold(e => Left(s"Error serializing RDF to format $formatName: $e"),
+ Right(_)
+ )
+ } yield str
+
+ private def extend_rdfs: Rdf = {
val infModel = ModelFactory.createRDFSModel(model)
RDFAsJenaModel(infModel)
}
@@ -122,7 +134,7 @@ case class RDFAsJenaModel(model: Model)
nodes.toSet
}
- def toRDFTriples(ls: Set[Statement]): Set[RDFTriple] = {
+ private def toRDFTriples(ls: Set[Statement]): Set[RDFTriple] = {
ls.map(st => statement2triple(st))
}
@@ -152,18 +164,18 @@ case class RDFAsJenaModel(model: Model)
}
}
- def model2triples(model: Model): Set[RDFTriple] = {
+ private def model2triples(model: Model): Set[RDFTriple] = {
model.listStatements().asScala.map(st => statement2triple(st)).toSet
}
- def statement2triple(st: Statement): RDFTriple = {
+ private def statement2triple(st: Statement): RDFTriple = {
RDFTriple(
JenaMapper.jenaNode2RDFNode(st.getSubject),
property2iri(st.getPredicate),
JenaMapper.jenaNode2RDFNode(st.getObject))
}
- def property2iri(p: Property): IRI = {
+ private def property2iri(p: Property): IRI = {
IRI(p.getURI)
}
@@ -174,7 +186,7 @@ case class RDFAsJenaModel(model: Model)
})
}
- override def addPrefixMap(pm: PrefixMap): RDFAsJenaModel = {
+ override def addPrefixMap(pm: PrefixMap): Rdf = {
val map: Map[String, String] = pm.pm.map {
case (Prefix(str), iri) => (str, iri.str)
}
@@ -185,14 +197,14 @@ case class RDFAsJenaModel(model: Model)
// TODO: Check that the last character is indeed :
// private def removeLastColon(str: String): String = str.init
- override def addTriples(triples: Set[RDFTriple]): RDFAsJenaModel = {
+ override def addTriples(triples: Set[RDFTriple]): Rdf = {
val newModel = JenaMapper.RDFTriples2Model(triples, model)
model.add(newModel)
this
}
// TODO: This is not efficient
- override def rmTriple(triple: RDFTriple): RDFAsJenaModel = {
+ override def rmTriple(triple: RDFTriple): Rdf = {
val empty = ModelFactory.createDefaultModel
val model2delete = JenaMapper.RDFTriples2Model(Set(triple), empty)
model.difference(model2delete)
@@ -201,19 +213,19 @@ case class RDFAsJenaModel(model: Model)
override def createBNode: (RDFNode, RDFAsJenaModel) = {
val resource = model.createResource
- (BNodeId(resource.getId.getLabelString), this)
+ (BNode(resource.getId.getLabelString), this)
}
- override def addPrefix(alias: String, iri: String): RDFAsJenaModel = {
+ override def addPrefix(alias: String, iri: String): Rdf = {
model.setNsPrefix(alias, iri)
this
}
- def qName(str: String): IRI = {
+ private def qName(str: String): IRI = {
IRI(model.expandPrefix(str))
}
- def empty: Rdf = {
+ override def empty: Rdf = {
RDFAsJenaModel.empty
}
@@ -226,9 +238,9 @@ case class RDFAsJenaModel(model: Model)
iri => Right(IRI(iri))
)
}*/
- val NONE = "NONE"
- val RDFS = "RDFS"
- val OWL = "OWL"
+ private val NONE = "NONE"
+ private val RDFS = "RDFS"
+ private val OWL = "OWL"
override def applyInference(inference: String): Either[String, Rdf] = {
inference.toUpperCase match {
diff --git a/modules/srdfJena/src/main/scala/es/weso/rdf/jena/RDFFromWeb.scala b/modules/srdfJena/src/main/scala/es/weso/rdf/jena/RDFFromWeb.scala
index 70e97af3..c084b82c 100644
--- a/modules/srdfJena/src/main/scala/es/weso/rdf/jena/RDFFromWeb.scala
+++ b/modules/srdfJena/src/main/scala/es/weso/rdf/jena/RDFFromWeb.scala
@@ -124,7 +124,7 @@ case class RDFFromWeb() extends RDFReader {
def jena2rdfnode(r: JenaRDFNode): RDFNode = {
if (r.isAnon) {
- BNodeId(r.asNode.getBlankNodeId.getLabelString)
+ BNode(r.asNode.getBlankNodeId.getLabelString)
} else if (r.isURIResource) {
IRI(r.asResource.getURI())
} else if (r.isLiteral) {
diff --git a/modules/srdfJena/src/test/scala/es/weso/rdf/jena/RDFJenaSpec.scala b/modules/srdfJena/src/test/scala/es/weso/rdf/jena/RDFJenaSpec.scala
index fbb02367..ccbe1a5d 100644
--- a/modules/srdfJena/src/test/scala/es/weso/rdf/jena/RDFJenaSpec.scala
+++ b/modules/srdfJena/src/test/scala/es/weso/rdf/jena/RDFJenaSpec.scala
@@ -37,7 +37,7 @@ class RDFJenaSpec
val pm: PrefixMap = PrefixMap(map)
rdf.addPrefixMap(pm)
rdf.addTriples(Set(
- RDFTriple(IRI("http://example.org#a"), IRI("http://foaf.org#knows"), BNodeId("b" + 1)), RDFTriple(BNodeId("b" + 1), IRI("http://foaf.org#knows"), BNodeId("b" + 2)), RDFTriple(BNodeId("b" + 2), IRI("http://foaf.org#name"), StringLiteral("pepe"))))
+ RDFTriple(IRI("http://example.org#a"), IRI("http://foaf.org#knows"), BNode("b" + 1)), RDFTriple(BNode("b" + 1), IRI("http://foaf.org#knows"), BNode("b" + 2)), RDFTriple(BNode("b" + 2), IRI("http://foaf.org#name"), StringLiteral("pepe"))))
val m2 = str2model("""|@prefix : .
|@prefix foaf: .
|:a foaf:knows _:x .
diff --git a/modules/srdfJena/src/test/scala/es/weso/rdf/parser/RDFParserTest.scala b/modules/srdfJena/src/test/scala/es/weso/rdf/parser/RDFParserTest.scala
index 9f87161e..c0bfbb92 100644
--- a/modules/srdfJena/src/test/scala/es/weso/rdf/parser/RDFParserTest.scala
+++ b/modules/srdfJena/src/test/scala/es/weso/rdf/parser/RDFParserTest.scala
@@ -42,8 +42,8 @@ class RDFParserTest extends FunSpec with Matchers with RDFParser with EitherValu
|:x :p :T .""".stripMargin
val try1 = for {
rdf <- RDFAsJenaModel.fromChars(cs, "TURTLE")
- val n: RDFNode = IRI("http://example.org/x")
- val p: IRI = IRI("http://example.org/q")
+ n: RDFNode = IRI("http://example.org/x")
+ p: IRI = IRI("http://example.org/q")
obj <- iriFromPredicate(p)(n, rdf)
} yield (obj)
try1 match {
diff --git a/modules/srdfJena/src/test/scala/es/weso/rdftriple/jenaMapper/JenaMapperTest.scala b/modules/srdfJena/src/test/scala/es/weso/rdftriple/jenaMapper/JenaMapperTest.scala
index 8648b8d6..4bb396f7 100644
--- a/modules/srdfJena/src/test/scala/es/weso/rdftriple/jenaMapper/JenaMapperTest.scala
+++ b/modules/srdfJena/src/test/scala/es/weso/rdftriple/jenaMapper/JenaMapperTest.scala
@@ -17,7 +17,7 @@ class JenaMapperTest
describe("Jena Mapper") {
it("Should compare one triple with 2 different bNodes") {
- val ts = Set(RDFTriple(BNodeId("b" + 0), IRI("http://example.org#p"), BNodeId("b" + 1)))
+ val ts = Set(RDFTriple(BNode("b" + 0), IRI("http://example.org#p"), BNode("b" + 1)))
val s = """[] [] ."""
val empty = ModelFactory.createDefaultModel
val model1 = RDFTriples2Model(ts, empty)
@@ -26,7 +26,7 @@ class JenaMapperTest
}
it("Should compare one triple with a shared bNode") {
- val ts = Set(RDFTriple(BNodeId("b" + 0), IRI("http://example.org#p"), BNodeId("b" + 0)))
+ val ts = Set(RDFTriple(BNode("b" + 0), IRI("http://example.org#p"), BNode("b" + 0)))
val s = """_:a _:a ."""
val empty = ModelFactory.createDefaultModel
val model1 = RDFTriples2Model(ts, empty)
@@ -35,7 +35,7 @@ class JenaMapperTest
}
it("Should compare one triple with a prefix decl") {
- val ts = Set(RDFTriple(BNodeId("b" + 0), IRI("http://example.org#p"), BNodeId("b" + 0)))
+ val ts = Set(RDFTriple(BNode("b" + 0), IRI("http://example.org#p"), BNode("b" + 0)))
val s = """|@prefix : .
|_:a :p _:a .""".stripMargin
val empty = ModelFactory.createDefaultModel
@@ -45,7 +45,7 @@ class JenaMapperTest
}
it("Should compare one triple with an integer literal") {
- val ts = Set(RDFTriple(BNodeId("b" + 0), IRI("http://example.org#p"), IntegerLiteral(1)))
+ val ts = Set(RDFTriple(BNode("b" + 0), IRI("http://example.org#p"), IntegerLiteral(1)))
val s = """|@prefix : .
|_:a :p 1 .""".stripMargin
val empty = ModelFactory.createDefaultModel
@@ -55,7 +55,7 @@ class JenaMapperTest
}
it("Should compare one triple with a decimal literal") {
- val ts = Set(RDFTriple(BNodeId("b" + 0), IRI("http://example.org#p"), DecimalLiteral(1.2)))
+ val ts = Set(RDFTriple(BNode("b" + 0), IRI("http://example.org#p"), DecimalLiteral(1.2)))
val s = """|@prefix : .
|_:a :p 1.2 .""".stripMargin
val empty = ModelFactory.createDefaultModel
@@ -65,7 +65,7 @@ class JenaMapperTest
}
it("Should compare one triple with a boolean literal") {
- val ts = Set(RDFTriple(BNodeId("b" + 0), IRI("http://example.org#p"), BooleanLiteral(true)))
+ val ts = Set(RDFTriple(BNode("b" + 0), IRI("http://example.org#p"), BooleanLiteral(true)))
val s = """|@prefix : .
|_:a :p true .""".stripMargin
val empty = ModelFactory.createDefaultModel
@@ -76,7 +76,7 @@ class JenaMapperTest
// The following test fails probably for Double comparison
ignore("Should compare one triple with a double literal") {
- val ts = Set(RDFTriple(BNodeId("b" + 0), IRI("http://example.org#p"), DoubleLiteral(1.2e3)))
+ val ts = Set(RDFTriple(BNode("b" + 0), IRI("http://example.org#p"), DoubleLiteral(1.2e3)))
val s = """|@prefix : .
|_:a :p 1.2e3 .""".stripMargin
val empty = ModelFactory.createDefaultModel
@@ -87,9 +87,9 @@ class JenaMapperTest
it("Should convert three triples") {
val ts = Set(
- RDFTriple(BNodeId("b" + 0), IRI("http://example.org#p"), BNodeId("b" + 0)),
- RDFTriple(BNodeId("b" + 0), IRI("http://example.org#p"), IntegerLiteral(4)),
- RDFTriple(BNodeId("b" + 0), IRI("http://example.org#p"), LangLiteral("pepe", Lang("es"))))
+ RDFTriple(BNode("b" + 0), IRI("http://example.org#p"), BNode("b" + 0)),
+ RDFTriple(BNode("b" + 0), IRI("http://example.org#p"), IntegerLiteral(4)),
+ RDFTriple(BNode("b" + 0), IRI("http://example.org#p"), LangLiteral("pepe", Lang("es"))))
val empty = ModelFactory.createDefaultModel
val m1 = RDFTriples2Model(ts, empty)
val m2 = str2model("""|@prefix : .
diff --git a/modules/typing/README.md b/modules/typing/README.md
new file mode 100644
index 00000000..0054fb0d
--- /dev/null
+++ b/modules/typing/README.md
@@ -0,0 +1,3 @@
+# Typing module
+
+A generic typing interface. A typing represents a maps from values to possible types that those values can have.
\ No newline at end of file
diff --git a/modules/validating/README.md b/modules/validating/README.md
new file mode 100644
index 00000000..4e930dd7
--- /dev/null
+++ b/modules/validating/README.md
@@ -0,0 +1,3 @@
+# Validating module
+
+Validating defines a generic validating monad.
\ No newline at end of file
diff --git a/notes/0.0.67.md b/notes/0.0.67.md
new file mode 100644
index 00000000..45735a88
--- /dev/null
+++ b/notes/0.0.67.md
@@ -0,0 +1,17 @@
+# New features
+
+- Added implementation of SRDF4j which allows the library to work also with RDf4j
+
+- Conversion between ShEx serialized compact syntax and Json
+
+
+TODOs
+-----
+
+- SHACL support using RDf4j (add SHACL paths to SRDF4j)
+
+- Generate SHACL validating report
+
+- Conversion from ShEx to SHACL
+
+- Conversion from SHACL to ShEx
diff --git a/version.sbt b/version.sbt
index 5b17c66d..3b627c86 100644
--- a/version.sbt
+++ b/version.sbt
@@ -1 +1 @@
-version in ThisBuild := "0.0.66"
\ No newline at end of file
+version in ThisBuild := "0.0.67"
\ No newline at end of file