.
+
+:Person a sh:NodeShape ;
+ sh:property :hasName .
+
+:Person sh:targetNode :alice .
+
+:alice :name "Alice" .
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 4cd9900d..1e0a7221 100644
--- a/modules/shacl/src/test/scala/es/weso/shacl/AbstractSyntaxTest.scala
+++ b/modules/shacl/src/test/scala/es/weso/shacl/AbstractSyntaxTest.scala
@@ -13,12 +13,17 @@ class AbstractSyntaxTest extends FunSpec with Matchers {
id = id,
components = List(),
targets = List(),
- propertyShapes = List(ShapeRef(x)),
+ propertyShapes = List(RefNode(x)),
false,
List(),
false,
MessageMap.empty,
- None
+ None,
+ name = MessageMap.empty,
+ description = MessageMap.empty,
+ order = None,
+ group = None,
+ sourceIRI = None
)
shape.id should be(id)
diff --git a/modules/shacl/src/test/scala/es/weso/shacl/RDF2ShaclTest.scala b/modules/shacl/src/test/scala/es/weso/shacl/RDF2ShaclTest.scala
index 6c54b8e2..bdc27fb3 100644
--- a/modules/shacl/src/test/scala/es/weso/shacl/RDF2ShaclTest.scala
+++ b/modules/shacl/src/test/scala/es/weso/shacl/RDF2ShaclTest.scala
@@ -108,7 +108,7 @@ class RDF2ShaclTest extends FunSpec with Matchers with TryValues with EitherValu
attempt match {
case Left(e) => fail(s"Failed $e")
case Right(shape) => {
- shape.propertyShapes should contain only (ShapeRef(prop))
+ shape.propertyShapes should contain only (RefNode(prop))
}
}
diff --git a/modules/shacl/src/test/scala/es/weso/shacl/SiblingsTest.scala b/modules/shacl/src/test/scala/es/weso/shacl/SiblingsTest.scala
index 3d78c4ea..fa1ef857 100644
--- a/modules/shacl/src/test/scala/es/weso/shacl/SiblingsTest.scala
+++ b/modules/shacl/src/test/scala/es/weso/shacl/SiblingsTest.scala
@@ -56,15 +56,15 @@ class SiblingsTest extends FunSpec
describe("Parent") {
it("should be able to find parent of a shape") {
- val eitherParents: Either[String, List[ShapeRef]] = for {
+ val eitherParents: Either[String, List[RefNode]] = for {
rdf <- RDFAsJenaModel.fromChars(str, "TURTLE")
schema <- RDF2Shacl.getShacl(rdf)
- } yield schema.parents(ShapeRef(psFemale))
+ } yield schema.parents(RefNode(psFemale))
eitherParents match {
case Right(ps) => {
info(s"Parents found: $ps")
- ps should contain only (ShapeRef(marriage))
+ ps should contain only (RefNode(marriage))
}
case Left(e) => fail(e)
}
@@ -73,10 +73,10 @@ class SiblingsTest extends FunSpec
describe("SiblingQualifiedValueShapes") {
it("should be able to find siblings of a shape") {
- val eitherShapes: Either[String, List[ShapeRef]] = for {
+ val eitherShapes: Either[String, List[RefNode]] = for {
rdf <- RDFAsJenaModel.fromChars(str, "TURTLE")
schema <- RDF2Shacl.getShacl(rdf)
- } yield (schema.siblingQualifiedShapes(ShapeRef(psFemale)))
+ } yield (schema.siblingQualifiedShapes(RefNode(psFemale)))
eitherShapes match {
case Right(ss) => {
diff --git a/modules/shacl/src/test/scala/es/weso/shacl/ValidatorTest.scala b/modules/shacl/src/test/scala/es/weso/shacl/ValidatorTest.scala
index a1ab47e9..788385ca 100644
--- a/modules/shacl/src/test/scala/es/weso/shacl/ValidatorTest.scala
+++ b/modules/shacl/src/test/scala/es/weso/shacl/ValidatorTest.scala
@@ -74,7 +74,7 @@ class ValidatorTest extends FunSpec with Matchers with TryValues with OptionValu
val good2 = ex + "good2"
val bad1 = ex + "bad1"
val ps = Shape.emptyPropertyShape(PS, PredicatePath(p)).copy(components = List(MinCount(1)))
- val psRefs = Seq(ShapeRef(PS))
+ val psRefs = Seq(RefNode(PS))
val s = Shape.empty(S).copy(
targets = Seq(TargetNode(x)),
propertyShapes = psRefs)
diff --git a/modules/shacl/src/test/scala/es/weso/shacl/manifest/RDF2Manifest.scala b/modules/shacl/src/test/scala/es/weso/shacl/manifest/RDF2Manifest.scala
index d6709fd8..4a8dc638 100644
--- a/modules/shacl/src/test/scala/es/weso/shacl/manifest/RDF2Manifest.scala
+++ b/modules/shacl/src/test/scala/es/weso/shacl/manifest/RDF2Manifest.scala
@@ -255,7 +255,7 @@ object RDF2Manifest extends LazyLogging {
format: String,
base: Option[String],
derefIncludes: Boolean
- ): Either[String, (Manifest,RDFReader)] = {
+ ): Either[String, (Manifest,RDFBuilder)] = {
for {
cs <- getContents(fileName)
rdf <- RDFAsJenaModel.fromChars(cs, format, None).map(_.normalizeBNodes)
diff --git a/modules/shacl/src/test/scala/es/weso/shacl/report/ReportGeneratorCompatTest.scala b/modules/shacl/src/test/scala/es/weso/shacl/report/ReportGeneratorCompatTest.scala
index 8466a7c8..8edbd8e2 100644
--- a/modules/shacl/src/test/scala/es/weso/shacl/report/ReportGeneratorCompatTest.scala
+++ b/modules/shacl/src/test/scala/es/weso/shacl/report/ReportGeneratorCompatTest.scala
@@ -4,7 +4,7 @@ import java.io._
import java.nio.file.{Path, Paths}
import com.typesafe.config._
-import es.weso.rdf.RDFReader
+import es.weso.rdf.{RDFBuilder, RDFReader}
import es.weso.rdf.jena.RDFAsJenaModel
import es.weso.rdf.nodes.{IRI, RDFNode}
import es.weso.rdf.parser.RDFParser
@@ -64,7 +64,7 @@ class ReportGeneratorCompatTest extends FunSpec with Matchers with RDFParser {
}
}
}
- def processManifest(m: Manifest, name: String, parentFolder: Path, rdfManifest: RDFReader): Unit = {
+ def processManifest(m: Manifest, name: String, parentFolder: Path, rdfManifest: RDFBuilder): Unit = {
// println(s"processManifest with ${name} and parent folder $parentFolder")
for ((includeNode, manifest) <- m.includes) {
describeManifest(includeNode, parentFolder)
@@ -73,7 +73,7 @@ class ReportGeneratorCompatTest extends FunSpec with Matchers with RDFParser {
processEntry(e,name,parentFolder, rdfManifest)
}
- def processEntry(e: manifest.Entry, name: String, parentFolder: Path, rdfManifest: RDFReader): Unit = {
+ def processEntry(e: manifest.Entry, name: String, parentFolder: Path, rdfManifest: RDFBuilder): Unit = {
println(s"Should check entry ${e.node.getLexicalForm} with $parentFolder")
getSchemaRdf(e.action, name, parentFolder,rdfManifest) match {
case Left(f) => {
@@ -89,7 +89,11 @@ class ReportGeneratorCompatTest extends FunSpec with Matchers with RDFParser {
}
}
- def getSchemaRdf(a: ManifestAction, fileName: String, parentFolder: Path, manifestRdf: RDFReader): Either[String, (Schema,RDFReader)] = for {
+ def getSchemaRdf(a: ManifestAction,
+ fileName: String,
+ parentFolder: Path,
+ manifestRdf: RDFBuilder
+ ): Either[String, (Schema,RDFReader)] = for {
pair <- getSchema(a,fileName,parentFolder,manifestRdf)
(schema,schemaRdf) = pair
dataRdf <- getData(a,fileName,parentFolder,manifestRdf,schemaRdf)
@@ -110,7 +114,11 @@ class ReportGeneratorCompatTest extends FunSpec with Matchers with RDFParser {
}
}
- def getSchema(a: ManifestAction, fileName: String, parentFolder: Path, manifestRdf: RDFReader): Either[String, (Schema, RDFReader)] = {
+ def getSchema(a: ManifestAction,
+ fileName: String,
+ parentFolder: Path,
+ manifestRdf: RDFBuilder
+ ): Either[String, (Schema, RDFReader)] = {
val parentIri = absoluteIri // absoluteIri.resolve(IRI(parentFolder))
a.schema match {
case None => {
diff --git a/modules/shacl/src/test/scala/es/weso/shacl/validator/SHACLCheckerTest.scala b/modules/shacl/src/test/scala/es/weso/shacl/validator/SHACLCheckerTest.scala
index 538968b7..20f03106 100644
--- a/modules/shacl/src/test/scala/es/weso/shacl/validator/SHACLCheckerTest.scala
+++ b/modules/shacl/src/test/scala/es/weso/shacl/validator/SHACLCheckerTest.scala
@@ -7,7 +7,7 @@ import cats.implicits._
import es.weso.rdf.RDFReader
import es.weso.rdf.jena.RDFAsJenaModel
import es.weso.rdf.nodes._
-import es.weso.shacl.{MessageMap, Shape, ShapeRef}
+import es.weso.shacl.{MessageMap, Shape, RefNode}
import es.weso.shacl.report.{Severity, ValidationResult}
// import es.weso.shacl.validator.ShapeTyping._
// import es.weso.typing.Typing
@@ -23,7 +23,7 @@ class SHACLCheckerTest extends FunSpec with Matchers with TryValues with OptionV
def mkErr(str: String): ValidationResult =
ValidationResult.basic("",StringLiteral(str),Attempt(StringLiteral(str),
- ShapeRef(StringLiteral(str)),
+ RefNode(StringLiteral(str)),
MessageMap.empty,
Severity.defaultSeverity,None
),str)
diff --git a/modules/shacl/src/test/scala/es/weso/shacl/validator/ShaclCoreTest.scala b/modules/shacl/src/test/scala/es/weso/shacl/validator/ShaclCoreTest.scala
index cf073885..6265a8ce 100644
--- a/modules/shacl/src/test/scala/es/weso/shacl/validator/ShaclCoreTest.scala
+++ b/modules/shacl/src/test/scala/es/weso/shacl/validator/ShaclCoreTest.scala
@@ -41,7 +41,7 @@ class ShaclCoreTest extends FunSpec with Matchers with TryValues with OptionValu
}
}
}
- def processManifest(m: Manifest, name: String, parentFolder: Path, rdfManifest: RDFReader): Unit = {
+ def processManifest(m: Manifest, name: String, parentFolder: Path, rdfManifest: RDFBuilder): Unit = {
// println(s"processManifest with ${name} and parent folder $parentFolder")
for ((includeNode, manifest) <- m.includes) {
describeManifest(includeNode, parentFolder)
@@ -50,7 +50,7 @@ class ShaclCoreTest extends FunSpec with Matchers with TryValues with OptionValu
processEntry(e,name,parentFolder, rdfManifest)
}
- def processEntry(e: manifest.Entry, name: String, parentFolder: Path, rdfManifest: RDFReader): Unit = {
+ def processEntry(e: manifest.Entry, name: String, parentFolder: Path, rdfManifest: RDFBuilder): Unit = {
it(s"Should check entry ${e.node.getLexicalForm} with $parentFolder") {
getSchemaRdf(e.action, name, parentFolder,rdfManifest) match {
case Left(f) => {
@@ -63,7 +63,7 @@ class ShaclCoreTest extends FunSpec with Matchers with TryValues with OptionValu
}
}
- def getSchemaRdf(a: ManifestAction, fileName: String, parentFolder: Path, manifestRdf: RDFReader): Either[String, (Schema,RDFReader)] = for {
+ def getSchemaRdf(a: ManifestAction, fileName: String, parentFolder: Path, manifestRdf: RDFBuilder): Either[String, (Schema,RDFReader)] = for {
pair <- getSchema(a,fileName,parentFolder,manifestRdf)
(schema,schemaRdf) = pair
dataRdf <- getData(a,fileName,parentFolder,manifestRdf,schemaRdf)
@@ -84,7 +84,7 @@ class ShaclCoreTest extends FunSpec with Matchers with TryValues with OptionValu
}
}
- def getSchema(a: ManifestAction, fileName: String, parentFolder: Path, manifestRdf: RDFReader): Either[String, (Schema, RDFReader)] = {
+ def getSchema(a: ManifestAction, fileName: String, parentFolder: Path, manifestRdf: RDFBuilder): Either[String, (Schema, RDFReader)] = {
info(s"Manifest action $a, fileName $fileName, parent: $parentFolder }")
val parentIri = absoluteIri // absoluteIri.resolve(IRI(parentFolder))
a.schema match {
diff --git a/modules/shex/src/test/scala/es/weso/shex/report/ReportGeneratorCompatTest.scala b/modules/shex/src/test/scala/es/weso/shex/report/ReportGeneratorCompatTest.scala
index 302e25b6..1c458d80 100644
--- a/modules/shex/src/test/scala/es/weso/shex/report/ReportGeneratorCompatTest.scala
+++ b/modules/shex/src/test/scala/es/weso/shex/report/ReportGeneratorCompatTest.scala
@@ -90,7 +90,7 @@ class ReportGeneratorCompatTest extends FunSpec with Matchers with RDFParser {
SingleTestReport(
passed = false,
name = name,
- uriTest = node.toIRI.str,
+ uriTest = node.getLexicalForm,
testType = sht_ValidationTest.str,
moreInfo = s"Error ${e}")
}
diff --git a/modules/srdf/src/main/scala/es/weso/rdf/Prefixes.scala b/modules/srdf/src/main/scala/es/weso/rdf/Prefixes.scala
index 74e8b508..1dee68b3 100644
--- a/modules/srdf/src/main/scala/es/weso/rdf/Prefixes.scala
+++ b/modules/srdf/src/main/scala/es/weso/rdf/Prefixes.scala
@@ -39,6 +39,8 @@ object PREFIXES {
lazy val rdf_first = rdf.add("first")
lazy val rdf_rest = rdf.add("rest")
lazy val rdf_langString = rdf.add("langString")
+
+ lazy val rdfs_label = rdfs.add("subClassOf")
lazy val rdfs_subClassOf = rdfs.add("subClassOf")
lazy val sh_alternativePath: IRI = sh + "alternativePath"
diff --git a/modules/srdf/src/main/scala/es/weso/rdf/RDFBuilder.scala b/modules/srdf/src/main/scala/es/weso/rdf/RDFBuilder.scala
index 74ebd237..d1d4bea8 100644
--- a/modules/srdf/src/main/scala/es/weso/rdf/RDFBuilder.scala
+++ b/modules/srdf/src/main/scala/es/weso/rdf/RDFBuilder.scala
@@ -30,5 +30,9 @@ trait RDFBuilder extends RDFReader {
def empty: Rdf
+ def merge(other: RDFReader): Either[String, Rdf]
+
+ def extendImports(): Either[String, Rdf]
+
}
diff --git a/modules/srdf/src/main/scala/es/weso/rdf/RDFReader.scala b/modules/srdf/src/main/scala/es/weso/rdf/RDFReader.scala
index a93c05e3..0cf178b1 100644
--- a/modules/srdf/src/main/scala/es/weso/rdf/RDFReader.scala
+++ b/modules/srdf/src/main/scala/es/weso/rdf/RDFReader.scala
@@ -60,7 +60,7 @@ trait RDFReader {
*/
// TODO: Extend this to return all iriObjects: Seq[RDFNode]
def iriObjects(): Set[IRI] = {
- rdfTriples.map(_.obj).filter(_.isIRI).map(_.toIRI)
+ rdfTriples.map(_.obj).collect { case i: IRI => i }
}
/**
@@ -195,7 +195,20 @@ trait RDFReader {
def getNumberOfStatements(): Either[String,Int]
+ /**
+ *
+ * @param other RDF reader
+ * @return true if this RDF graph is isomorphic with other
+ */
def isIsomorphicWith(other: RDFReader): Either[String,Boolean]
+ /**
+ * @return Source IRI of this RDF graph if exists
+ */
+ def sourceIRI: Option[IRI]
+
+ def asRDFBuilder: Either[String, RDFBuilder]
+
+ def rdfReaderName: String
}
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 760ac6c2..c0020d51 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
@@ -18,13 +18,12 @@ abstract class RDFNode {
case _ => false
}
- def isNonLiteral = this.isIRI || this.isBNode
+ def isNonLiteral =
+ this.isIRI || this.isBNode
- // Change this code to use Option
- def toIRI = this match {
- case i: IRI => i
- case _ =>
- throw RDFNodeException("Cannot convert RDFNode " + this + " to IRI")
+ def toIRI: Either[String,IRI] = this match {
+ case i: IRI => Right(i)
+ case _ => Left(s"Cannot convert node $this to IRI")
}
def getLexicalForm: String
diff --git a/modules/srdf/src/main/scala/es/weso/rdf/parser/RDFParser.scala b/modules/srdf/src/main/scala/es/weso/rdf/parser/RDFParser.scala
index 0c90eb06..4ce3a28e 100644
--- a/modules/srdf/src/main/scala/es/weso/rdf/parser/RDFParser.scala
+++ b/modules/srdf/src/main/scala/es/weso/rdf/parser/RDFParser.scala
@@ -64,7 +64,7 @@ trait RDFParser {
/**
* Returns the String associated with a predicate `p`
* @param p predicate
- * @return An RDFParser that returns the String associate with that predicate
+ * @return An RDFParser that returns the String associated with that predicate
*
*/
def stringFromPredicate(p: IRI): RDFParser[String] = { (n, rdf) =>
@@ -77,6 +77,23 @@ trait RDFParser {
} yield str
}
+ /**
+ * Returns the Decimal literal associated with a predicate `p`
+ * @param p predicate
+ * @return An RDFParser that returns the decimal literal associated with that predicate
+ *
+ */
+ def decimalLiteralFromPredicate(p: IRI): RDFParser[DecimalLiteral] = { (n, rdf) =>
+ for {
+ obj <- objectFromPredicate(p)(n, rdf)
+ node <- obj match {
+ case d: DecimalLiteral => parseOk(d)
+ case _ => parseFail("Value of predicate " + p + " must be a decimal literal but it is: " + obj)
+ }
+ } yield node
+ }
+
+
/**
*
*/
@@ -86,6 +103,9 @@ trait RDFParser {
def objectFromPredicateOptional(p: IRI): RDFParser[Option[RDFNode]] =
optional(objectFromPredicate(p))
+ def decimalLiteralFromPredicateOptional(p: IRI): RDFParser[Option[DecimalLiteral]] =
+ optional(decimalLiteralFromPredicate(p))
+
/**
* Returns a parser that obtains the type associated with the current node
*
@@ -233,7 +253,10 @@ trait RDFParser {
def hasSomeRDFType(ts: Set[IRI]): RDFParser[Boolean] = { (n, rdf) =>
for {
declaredTypes <- objectsFromPredicate(rdf_type)(n, rdf)
- } yield (declaredTypes.map(_.toIRI).diff(ts)).size > 0
+ } yield {
+ val iriTypes = declaredTypes.collect { case i: IRI => i}
+ iriTypes.diff(ts).size > 0
+ }
}
/**
@@ -589,7 +612,7 @@ trait RDFParser {
else parseFail(s"Expected rdf_nil but got $n")
def nodes2iris(ns: List[RDFNode]): Either[String, List[IRI]] = {
- sequenceEither(ns.map(node2IRI(_)))
+ sequenceEither(ns.map(_.toIRI))
}
// Todo: Use "sequence" when I find why it gives a type error...
@@ -606,11 +629,6 @@ trait RDFParser {
xs.foldLeft(zero)(next)
}
- def node2IRI(node: RDFNode): Either[String, IRI] = node match {
- case (i: IRI) => Right(i)
- case _ => Left(s"$node is not an IRI\n")
- }
-
/* def fromEitherString[A](e: Either[String,A]): Try[A] =
e.fold(str => parseFail(str),v => Success(v)) */
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
index fdca437d..e10e57e9 100644
--- a/modules/srdf4j/src/main/scala/es/weso/rdf/rdf4j/RDFAsRDF4jModel.scala
+++ b/modules/srdf4j/src/main/scala/es/weso/rdf/rdf4j/RDFAsRDF4jModel.scala
@@ -7,7 +7,7 @@ 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 es.weso.rdf.nodes.{IRI, _}
import org.eclipse.rdf4j.model.util.{ModelBuilder, Models}
import org.eclipse.rdf4j.rio.RDFFormat._
import org.eclipse.rdf4j.rio.{RDFFormat, Rio}
@@ -17,8 +17,10 @@ import scala.util._
import scala.collection.JavaConverters._
import RDF4jMapper._
import cats.implicits._
+import es.weso.utils.EitherUtils
-case class RDFAsRDF4jModel(model: Model)
+case class RDFAsRDF4jModel(model: Model,
+ sourceIRI: Option[IRI] = None)
extends RDFReader
with RDFBuilder
with RDFReasoner {
@@ -233,6 +235,52 @@ case class RDFAsRDF4jModel(model: Model)
case _ => Left(s"Cannot compare RDFAsJenaModel with reader of different type: ${other.getClass.toString}")
}
+ override def merge(other: RDFReader): Either[String,Rdf] = other match {
+ // TODO: optimize merge using RDF4j merge...
+ // case rdf4j: RDFAsRDF4jModel =>
+ case _ => {
+ val zero: Either[String,Rdf] = Right(this)
+ def cmb(next: Either[String,Rdf], x: RDFTriple): Either[String,Rdf] = for {
+ rdf1 <- next
+ rdf2 <- rdf1.addTriple(x)
+ } yield rdf2
+ other.rdfTriples.foldLeft(zero)(cmb)
+ }
+ }
+
+ override def extendImports():Either[String, Rdf] = for {
+ imports <- getImports
+ newRdf <- extendImports(this,imports,List(IRI("")))
+ } yield newRdf
+
+ private lazy val owlImports = IRI("http://www.w3.org/2002/07/owl#imports")
+
+ private def getImports: Either[String, List[IRI]] =
+ EitherUtils.sequence(
+ triplesWithPredicate(owlImports).toList.map(_.obj).map(_.toIRI)
+ )
+
+ private def extendImports(rdf: Rdf,
+ imports: List[IRI],
+ visited: List[IRI]
+ ): Either[String,Rdf] = {
+ imports match {
+ case Nil => Right(rdf)
+ case iri :: rest =>
+ if (visited contains iri)
+ extendImports(rdf,rest,visited)
+ else for {
+ newRdf <- RDFAsRDF4jModel.fromIRI(iri)
+ merged <- merge(newRdf)
+ restRdf <- extendImports(merged,rest,iri :: visited)
+ } yield restRdf
+ }
+ }
+
+ override def asRDFBuilder: Either[String, RDFBuilder] =
+ Right(this)
+
+ override def rdfReaderName: String = s"RDF4j"
}
@@ -256,5 +304,8 @@ object RDFAsRDF4jModel {
formats.map(_.getName)
}
+ def fromIRI(iri: IRI): Either[String,RDFAsRDF4jModel] = {
+ Left(s"Not implemented get RDF4j from IRI: $iri")
+ }
}
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 33555520..7eb3c316 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
@@ -24,7 +24,10 @@ import com.typesafe.scalalogging.LazyLogging
import es.weso.rdf.jena.JenaMapper.jenaNode2RDFNode
// TODO: Refactor to change String type by Url
-case class Endpoint(endpoint: String) extends RDFReader with RDFReasoner with LazyLogging {
+case class Endpoint(endpoint: String)
+ extends RDFReader
+ with RDFReasoner
+ with LazyLogging {
type Rdf = Endpoint
def availableParseFormats: List[String] = List()
@@ -95,12 +98,12 @@ case class Endpoint(endpoint: String) extends RDFReader with RDFReasoner with La
model2triples(model)
}
- def triplesWithSubject(node: RDFNode): Set[RDFTriple] = {
- logger.debug(s"Triples with subject ${node.show}")
- if (node.isIRI) {
- val model = QueryExecutionFactory.sparqlService(endpoint, queryTriplesWithSubject(node.toIRI)).execConstruct()
+ def triplesWithSubject(node: RDFNode): Set[RDFTriple] = node match {
+ case subj: IRI => {
+ val model = QueryExecutionFactory.sparqlService(endpoint, queryTriplesWithSubject(subj)).execConstruct()
model2triples(model)
- } else throw new Exception("triplesWithSubject: node " + node + " must be a IRI")
+ }
+ case _ => throw new Exception("triplesWithSubject: node " + node + " must be a IRI")
}
def triplesWithPredicate(p: IRI): Set[RDFTriple] = {
@@ -108,20 +111,20 @@ case class Endpoint(endpoint: String) extends RDFReader with RDFReasoner with La
model2triples(model)
}
- def triplesWithObject(node: RDFNode): Set[RDFTriple] = {
- log.debug(s"Triples with object ${node}")
- if (node.isIRI) {
- val model = QueryExecutionFactory.sparqlService(endpoint, queryTriplesWithObject(node.toIRI)).execConstruct()
+ def triplesWithObject(node: RDFNode): Set[RDFTriple] = node match {
+ case obj: IRI => {
+ val model = QueryExecutionFactory.sparqlService(endpoint, queryTriplesWithObject(obj)).execConstruct()
model2triples(model)
- } else throw new Exception("triplesWithObject: node " + node + " must be a IRI")
+ }
+ case _ => throw new Exception("triplesWithObject: node " + node + " must be a IRI")
}
- def triplesWithPredicateObject(p: IRI, o: RDFNode): Set[RDFTriple] = {
- log.debug(s"Triples with predicate ${p} and object $o")
- if (o.isIRI) {
- val model = QueryExecutionFactory.sparqlService(endpoint, queryTriplesWithPredicateObject(p, o.toIRI)).execConstruct()
+ def triplesWithPredicateObject(p: IRI, o: RDFNode): Set[RDFTriple] = o match {
+ case iri: IRI => {
+ val model = QueryExecutionFactory.sparqlService(endpoint, queryTriplesWithPredicateObject(p, iri)).execConstruct()
model2triples(model)
- } else throw new Exception("triplesWithPredicateObject: o " + o + " must be a IRI")
+ }
+ case _ => throw new Exception("triplesWithPredicateObject: o " + o + " must be a IRI")
}
def model2triples(model: Model): Set[RDFTriple] = {
@@ -230,6 +233,13 @@ case class Endpoint(endpoint: String) extends RDFReader with RDFReasoner with La
override def isIsomorphicWith(other: RDFReader): Either[String,Boolean] =
Left(s"Unimplemented isIsomorphicWith between endpoints")
+ override def sourceIRI = None
+
+ override def asRDFBuilder: Either[String,RDFBuilder] =
+ Left(s"Unimplemented isIsomorphicWith between endpoints")
+
+ override def rdfReaderName: String = s"Endpoint($endpoint)"
+
}
object Endpoint {
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 8f7c0e84..e877b52d 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
@@ -27,7 +27,7 @@ import org.apache.jena.sparql.path.Path
import cats.implicits._
import es.weso.rdf.dot.RDF2Dot
-case class RDFAsJenaModel(model: Model)
+case class RDFAsJenaModel(model: Model, sourceIRI: Option[IRI] = None)
extends RDFReader
with RDFBuilder
with RDFReasoner {
@@ -331,6 +331,56 @@ case class RDFAsJenaModel(model: Model)
NormalizaBNodes.normalizeBNodes(this,this.empty)
}
+ /**
+ * Apply owl:imports closure to an RDF source
+ * @return new RDFReader
+ */
+ override def extendImports():Either[String, Rdf] = for {
+ imports <- getImports
+ newRdf <- extendImports(this,imports,List(IRI("")))
+ } yield newRdf
+
+ private lazy val owlImports = IRI("http://www.w3.org/2002/07/owl#imports")
+
+ private def getImports: Either[String, List[IRI]] =
+ EitherUtils.sequence(
+ triplesWithPredicate(owlImports).toList.map(_.obj.toIRI)
+ )
+
+ private def extendImports(rdf: Rdf,
+ imports: List[IRI],
+ visited: List[IRI]
+ ): Either[String,Rdf] = {
+ imports match {
+ case Nil => Right(rdf)
+ case iri :: rest =>
+ if (visited contains iri)
+ extendImports(rdf,rest,visited)
+ else for {
+ newRdf <- RDFAsJenaModel.fromIRI(iri)
+ merged <- merge(newRdf)
+ restRdf <- extendImports(merged,rest,iri :: visited)
+ } yield restRdf
+ }
+ }
+
+ override def merge(other: RDFReader): Either[String,Rdf] = other match {
+ case jenaRdf: RDFAsJenaModel =>
+ Right(RDFAsJenaModel(this.model.add(jenaRdf.model)))
+ case _ => {
+ val zero: Either[String,Rdf] = Right(this)
+ def cmb(next: Either[String,Rdf], x: RDFTriple): Either[String,Rdf] = for {
+ rdf1 <- next
+ rdf2 <- rdf1.addTriple(x)
+ } yield rdf2
+ other.rdfTriples.foldLeft(zero)(cmb)
+ }
+ }
+
+ override def asRDFBuilder: Either[String, RDFBuilder] =
+ Right(this)
+
+ override def rdfReaderName: String = s"ApacheJena"
}
@@ -344,6 +394,15 @@ object RDFAsJenaModel {
RDFAsJenaModel(ModelFactory.createDefaultModel)
}
+ def fromIRI(iri: IRI): Either[String,RDFAsJenaModel] = {
+ Try {
+ // We delegate RDF management to Jena (content-negotiation and so...)
+ RDFDataMgr.loadModel(iri.str)
+ }.toEither.
+ leftMap(e => s"Exception reading RDF from ${iri.show}: ${e.getMessage}").
+ map(model => RDFAsJenaModel(model))
+ }
+
def fromURI(uri: String,
format: String = "TURTLE",
base: Option[String] = None
@@ -352,7 +411,7 @@ object RDFAsJenaModel {
Try {
val m = ModelFactory.createDefaultModel()
RDFDataMgr.read(m, uri, baseURI, shortnameToLang(format))
- RDFAsJenaModel(JenaUtils.relativizeModel(m))
+ RDFAsJenaModel(JenaUtils.relativizeModel(m), Some(IRI(uri)))
}.fold(e => Left(s"Exception accessing uri $uri: ${e.getMessage}"),
(Right(_))
)
@@ -364,7 +423,7 @@ object RDFAsJenaModel {
val m = ModelFactory.createDefaultModel()
val is: InputStream = new FileInputStream(file)
RDFDataMgr.read(m, is, baseURI, shortnameToLang(format))
- RDFAsJenaModel(JenaUtils.relativizeModel(m))
+ RDFAsJenaModel(JenaUtils.relativizeModel(m), Some(IRI(file.toURI)))
}.fold(e => Left(s"Exception parsing RDF from file ${file.getName}: ${e.getMessage}"),
Right(_))
}
@@ -384,5 +443,4 @@ object RDFAsJenaModel {
RDFLanguages.getRegisteredLanguages().asScala.map(_.getName).toList.distinct
}
-
}
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 0c089083..10502b78 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
@@ -44,17 +44,17 @@ case class RDFFromWeb() extends RDFReader {
throw new Exception("Cannot obtain triples from RDFFromWeb ")
}
- override def triplesWithSubject(node: RDFNode): Set[RDFTriple] = {
- if (node.isIRI) {
- val subj = node.toIRI
+ override def triplesWithSubject(node: RDFNode): Set[RDFTriple] =
+ node match {
+ case subj: IRI => {
val derefModel = ModelFactory.createDefaultModel
RDFDataMgr.read(derefModel, subj.str)
val model = QueryExecutionFactory.create(queryTriplesWithSubject(subj), derefModel).execConstruct()
val triples = model2triples(model)
log.debug("triples with subject " + subj + " =\n" + triples)
triples
- } else
- throw new Exception("triplesWithSubject: node " + node + " must be a IRI")
+ }
+ case _ => throw new Exception("triplesWithSubject: node " + node + " must be a IRI")
}
override def triplesWithPredicate(p: IRI): Set[RDFTriple] = {
@@ -64,26 +64,27 @@ case class RDFFromWeb() extends RDFReader {
model2triples(model)
}
- override def triplesWithObject(node: RDFNode): Set[RDFTriple] = {
- if (node.isIRI) {
- val obj = node.toIRI
+ override def triplesWithObject(node: RDFNode): Set[RDFTriple] =
+ node match {
+ case obj: IRI => {
val derefModel = ModelFactory.createDefaultModel
RDFDataMgr.read(derefModel, obj.str)
val model = QueryExecutionFactory.create(queryTriplesWithObject(obj), derefModel).execConstruct()
model2triples(model)
- } else
+ }
+ case _ =>
throw new Exception("triplesWithObject: node " + node + " must be a IRI")
}
- override def triplesWithPredicateObject(p: IRI, node: RDFNode): Set[RDFTriple] = {
- if (node.isIRI) {
- val obj = node.toIRI
+ override def triplesWithPredicateObject(p: IRI, node: RDFNode): Set[RDFTriple] =
+ node match {
+ case obj: IRI => {
val derefModel = ModelFactory.createDefaultModel
RDFDataMgr.read(derefModel, obj.str)
val model = QueryExecutionFactory.create(queryTriplesWithPredicateObject(p, obj), derefModel).execConstruct()
model2triples(model)
- } else
- throw new Exception("triplesWithObject: node " + node + " must be a IRI")
+ }
+ case _ => throw new Exception("triplesWithObject: node " + node + " must be a IRI")
}
override def getSHACLInstances(c: RDFNode): Seq[RDFNode] = {
@@ -154,4 +155,11 @@ case class RDFFromWeb() extends RDFReader {
override def isIsomorphicWith(other: RDFReader) = Left(s"Unimplemented isomorphic test in RDFFromWeb")
+ override def sourceIRI = None
+
+ override def asRDFBuilder: Either[String, RDFBuilder] =
+ Left(s"Cannot convert RDFFromWeb to RDFBuilder")
+
+ override def rdfReaderName: String = s"RDFFromWeb"
+
}
\ No newline at end of file
diff --git a/modules/srdfJena/src/test/resources/application.properties b/modules/srdfJena/src/test/resources/application.properties
index a0087c3a..524e0c93 100644
--- a/modules/srdfJena/src/test/resources/application.properties
+++ b/modules/srdfJena/src/test/resources/application.properties
@@ -1,9 +1,3 @@
# Folders for local shexTest tests
-shaclFolder=src/test/resources/shacl
-shaclCore=src/test/resources/shacl/core
-shaclLocal=src/test/resources/shaclLocal
-schemasFolder=src/test/resources/shexTest/schemas
-shexLocalFolder=src/test/resources/shexLocal
-negativeSyntaxFolder=src/test/resources/shexTest/negativeSyntax
-validationFolder=src/test/resources/shexTest/validation/
+rdfFolder=modules/srdfJena/src/test/resources/rdf/
diff --git a/modules/srdfJena/src/test/resources/rdf/imported.ttl b/modules/srdfJena/src/test/resources/rdf/imported.ttl
new file mode 100644
index 00000000..d3434e33
--- /dev/null
+++ b/modules/srdfJena/src/test/resources/rdf/imported.ttl
@@ -0,0 +1,6 @@
+prefix :
+prefix owl:
+
+:x :p _:1 .
+_:1 :name "Bob" ;
+ :source <> .
\ No newline at end of file
diff --git a/modules/srdfJena/src/test/resources/rdf/m1.ttl b/modules/srdfJena/src/test/resources/rdf/m1.ttl
new file mode 100644
index 00000000..ec7e5c7e
--- /dev/null
+++ b/modules/srdfJena/src/test/resources/rdf/m1.ttl
@@ -0,0 +1,5 @@
+prefix :
+prefix owl:
+
+:x :p _:1 .
+_:1 :name "Robert" .
diff --git a/modules/srdfJena/src/test/resources/rdf/m2.ttl b/modules/srdfJena/src/test/resources/rdf/m2.ttl
new file mode 100644
index 00000000..abe7d981
--- /dev/null
+++ b/modules/srdfJena/src/test/resources/rdf/m2.ttl
@@ -0,0 +1,5 @@
+prefix :
+prefix owl:
+
+:x :p _:1 .
+_:1 :name "Bob" .
diff --git a/modules/srdfJena/src/test/resources/rdf/merged.ttl b/modules/srdfJena/src/test/resources/rdf/merged.ttl
new file mode 100644
index 00000000..e377a05f
--- /dev/null
+++ b/modules/srdfJena/src/test/resources/rdf/merged.ttl
@@ -0,0 +1,5 @@
+prefix :
+prefix owl:
+
+:x :p [:name "Robert"],
+ [:name "Bob" ].
diff --git a/modules/srdfJena/src/test/resources/rdf/testImport.ttl b/modules/srdfJena/src/test/resources/rdf/testImport.ttl
new file mode 100644
index 00000000..0c2debad
--- /dev/null
+++ b/modules/srdfJena/src/test/resources/rdf/testImport.ttl
@@ -0,0 +1,8 @@
+prefix :
+prefix owl:
+
+<> owl:imports .
+
+:x :p _:1 .
+_:1 :name "Robert" ;
+ :source <> .
\ No newline at end of file
diff --git a/modules/srdfJena/src/test/resources/rdf/testImportWithLoop.ttl b/modules/srdfJena/src/test/resources/rdf/testImportWithLoop.ttl
new file mode 100644
index 00000000..06e15a22
--- /dev/null
+++ b/modules/srdfJena/src/test/resources/rdf/testImportWithLoop.ttl
@@ -0,0 +1,8 @@
+prefix :
+prefix owl:
+
+<> owl:imports <> .
+
+:x :p _:1 .
+_:1 :name "Robert" ;
+ :source <> .
\ No newline at end of file
diff --git a/modules/srdfJena/src/test/scala/es/weso/rdf/jena/ImportsTest.scala b/modules/srdfJena/src/test/scala/es/weso/rdf/jena/ImportsTest.scala
new file mode 100644
index 00000000..fcb85d9b
--- /dev/null
+++ b/modules/srdfJena/src/test/scala/es/weso/rdf/jena/ImportsTest.scala
@@ -0,0 +1,88 @@
+package es.weso.rdf.jena
+
+import java.nio.file.Paths
+
+import com.typesafe.config.{Config, ConfigFactory}
+import es.weso.rdf.nodes._
+import org.scalatest.{FunSpec, Matchers}
+
+class ImportsTest
+ extends FunSpec
+ with JenaBased
+ with Matchers {
+
+ val conf: Config = ConfigFactory.load()
+ val rdfFolderStr = conf.getString("rdfFolder")
+ val rdfFolder = IRI(Paths.get(rdfFolderStr).normalize.toUri.toString)
+
+ describe("Merge test") {
+ it(s"Should read merged file") {
+ val r = for {
+ rdf1 <- RDFAsJenaModel.fromIRI(rdfFolder + "/merged.ttl")
+ } yield rdf1
+
+ r.fold(e => fail(s"Error: $e"), rdf => {
+ info(s"RDF read: ${rdf.iris}")
+ rdf.iris.size should be(1)
+ })
+ }
+
+ it(s"Should merge files") {
+ val r = for {
+ rdf1 <- RDFAsJenaModel.fromIRI(rdfFolder + "/m1.ttl")
+ rdf2 <- RDFAsJenaModel.fromIRI(rdfFolder + "/m2.ttl")
+ merged <- rdf1.merge(rdf2)
+ mergedFromFile <- RDFAsJenaModel.fromIRI(rdfFolder + "/merged.ttl")
+ b <- merged.isIsomorphicWith(mergedFromFile)
+ } yield (merged,mergedFromFile,b)
+
+ r.fold(e => fail(s"Error: $e"), values => {
+ val (_, _,b) = values
+ b should be(true)
+ })
+ }
+ }
+
+ describe("Imports test") {
+ it(s"Should extend with imports") {
+ val r = for {
+ rdf1 <- RDFAsJenaModel.fromIRI(rdfFolder + "/testImport.ttl")
+ extended <- rdf1.extendImports()
+ } yield (rdf1,extended)
+ r.fold(e => fail(s"Error: $e"), values => {
+ val (_,extended) = values
+ val x = IRI("http://example.org/x")
+ val p = IRI("http://example.org/p")
+ extended.triplesWithSubjectPredicate(x,p).size should be(2)
+// info(s"Extended: ${extended.serialize("Turtle")}")
+ })
+ }
+
+ it(s"Should handle loops") {
+ val r = for {
+ rdf1 <- RDFAsJenaModel.fromIRI(rdfFolder + "/testImportWithLoop.ttl")
+ extended <- rdf1.extendImports()
+ } yield (rdf1,extended)
+ r.fold(e => fail(s"Error: $e"), values => {
+ val (rdf1,extended) = values
+ rdf1.rdfTriples.size should be(extended.rdfTriples.size)
+ })
+ }
+
+ ignore(s"Should handle external IRIs") {
+ val r = for {
+ rdf1 <- RDFAsJenaModel.fromChars(
+ """|prefix :
+ |<> owl:imports <...some IRI?...>
+ |""".stripMargin,"Turtle",None)
+ extended <- rdf1.extendImports()
+ } yield (rdf1,extended)
+ r.fold(e => fail(s"Error: $e"), values => {
+ val (rdf1,extended) = values
+ rdf1.rdfTriples.size should be(extended.rdfTriples.size)
+ })
+ }
+
+ }
+}
+
diff --git a/modules/srdfJena/src/test/scala/es/weso/rdf/jena/RDFAsJenaModelTest.scala b/modules/srdfJena/src/test/scala/es/weso/rdf/jena/RDFAsJenaModelTest.scala
index ad7e9b00..26900d70 100644
--- a/modules/srdfJena/src/test/scala/es/weso/rdf/jena/RDFAsJenaModelTest.scala
+++ b/modules/srdfJena/src/test/scala/es/weso/rdf/jena/RDFAsJenaModelTest.scala
@@ -30,10 +30,7 @@ class RDFAsJenaModelTest
with Matchers {
describe("Checking base") {
- val conf: Config = ConfigFactory.load()
- val shaclFolder = conf.getString("shaclCore")
- val shaclFolderURI = Paths.get(shaclFolder).normalize.toUri.toString
- println(s"ShaclFolder file...${shaclFolderURI}")
+ // println(s"ShaclFolder file...${shaclFolderURI}")
it("should be able to parse RDF with relative URIs and base") {
val emptyModel = ModelFactory.createDefaultModel
diff --git a/notes/0.1.03.md b/notes/0.1.03.md
new file mode 100644
index 00000000..0d279a36
--- /dev/null
+++ b/notes/0.1.03.md
@@ -0,0 +1,16 @@
+# New features
+
+- Added support for sh:severity, sh:message and sh:deactivated in SHACL
+
+TODOs
+-----
+
+- ShEx: Complete semantic actions implementation [Issue 116](https://github.com/labra/shaclex/issues/116)
+
+- ShEx: test-suite with shape maps and update report [Issue 115](https://github.com/labra/shaclex/issues/115)
+
+- 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 f42b2b4f..6928edf8 100644
--- a/version.sbt
+++ b/version.sbt
@@ -1 +1 @@
-version in ThisBuild := "0.1.02"
+version in ThisBuild := "0.1.03"