diff --git a/modules/shacl/src/main/scala/es/weso/shacl/MessageMap.scala b/modules/shacl/src/main/scala/es/weso/shacl/MessageMap.scala
new file mode 100644
index 00000000..29899734
--- /dev/null
+++ b/modules/shacl/src/main/scala/es/weso/shacl/MessageMap.scala
@@ -0,0 +1,67 @@
+package es.weso.shacl
+import cats._
+import cats.implicits._
+import es.weso.rdf.nodes.{Lang, LangLiteral, RDFNode, StringLiteral}
+
+case class MessageMap(mmap: Map[Option[Lang], String]) {
+
+ def getRDFNodes: List[RDFNode] = mmap.toList.map {
+ case (maybeLang, str) =>
+ maybeLang match {
+ case None => StringLiteral(str)
+ case Some(lang) => LangLiteral(str, lang)
+ }
+ }
+
+ def addMessage(node: RDFNode): Either[String, MessageMap] = node match {
+ case StringLiteral(str) => mmap.get(None) match {
+ case None => Right(MessageMap(mmap.updated(None, str)))
+ case Some(other) => Left(s"Trying to create two messages without language tag: $other and $str")
+ }
+ case LangLiteral(str,lang) => mmap.get(Some(lang)) match {
+ case None => Right(MessageMap(mmap.updated(Some(lang), str)))
+ case Some(other) => Left(s"Trying to create two messages with same language tag ($lang): $other and $str")
+ }
+ case _ => Left(s"Node $node must be a string or a language tagged string to be a message")
+ }
+
+ override def toString(): String = Show[MessageMap].show(this)
+
+}
+
+object MessageMap {
+
+ def fromString(msg: String): MessageMap = {
+ MessageMap(Map(None -> msg))
+ }
+
+ def fromRDFNodes(nodes: List[RDFNode]): Either[String, MessageMap] = {
+ val zero: Either[String,MessageMap] = Right(MessageMap(Map()))
+ def cmb(rest: Either[String,MessageMap], x: RDFNode): Either[String,MessageMap] = for {
+ mmap <- rest
+ r <- mmap.addMessage(x)
+ } yield r
+ nodes.foldLeft(zero)(cmb)
+ }
+
+ implicit def monoidMessageMap: Monoid[MessageMap] = new Monoid[MessageMap] {
+ override def empty: MessageMap = MessageMap(Map())
+
+ override def combine(m1: MessageMap, m2: MessageMap): MessageMap =
+ MessageMap(m1.mmap |+| m2.mmap)
+ }
+
+ implicit def showMessageMap: Show[MessageMap] = new Show[MessageMap] {
+ override def show(m: MessageMap): String = {
+ m.mmap.toList.map { case (maybeLang,msg) =>
+ maybeLang match {
+ case None => msg
+ case Some(lang) => s"$msg@$lang"
+ }
+ }.mkString(",")
+ }
+ }
+
+ def empty: MessageMap = Monoid[MessageMap].empty
+
+}
\ No newline at end of file
diff --git a/modules/shacl/src/main/scala/es/weso/shacl/SHACLPrefixes.scala b/modules/shacl/src/main/scala/es/weso/shacl/SHACLPrefixes.scala
index f80ed0d0..e6b34ac8 100644
--- a/modules/shacl/src/main/scala/es/weso/shacl/SHACLPrefixes.scala
+++ b/modules/shacl/src/main/scala/es/weso/shacl/SHACLPrefixes.scala
@@ -66,6 +66,8 @@ object SHACLPrefixes {
lazy val sh_result: IRI = sh + "result"
lazy val sh_resultPath: IRI = sh + "resultPath"
lazy val sh_resultSeverity: IRI = sh + "resultSeverity"
+ lazy val sh_resultMessage: IRI = sh + "resultMessage"
+ lazy val sh_severity: IRI = sh + "severity"
lazy val sh_sourceConstraintComponent: IRI = sh + "sourceConstraintComponent"
lazy val sh_sourceShape: IRI = sh + "sourceShape"
lazy val sh_value: IRI = sh + "value"
diff --git a/modules/shacl/src/main/scala/es/weso/shacl/Shape.scala b/modules/shacl/src/main/scala/es/weso/shacl/Shape.scala
index fa3b8f05..81416f12 100644
--- a/modules/shacl/src/main/scala/es/weso/shacl/Shape.scala
+++ b/modules/shacl/src/main/scala/es/weso/shacl/Shape.scala
@@ -2,6 +2,7 @@ package es.weso.shacl
import es.weso.rdf.nodes._
import es.weso.rdf.path.SHACLPath
+import es.weso.shacl.report.Severity
sealed abstract class Shape {
@@ -11,7 +12,8 @@ sealed abstract class Shape {
def propertyShapes: Seq[ShapeRef]
def closed: Boolean
def deactivated: Boolean
- def message: Map[Option[Lang],String]
+ def message: MessageMap
+ def severity: Option[Severity]
def ignoredProperties: List[IRI]
def hasId(iri: IRI): Boolean = {
@@ -56,7 +58,8 @@ case class NodeShape(
closed: Boolean,
ignoredProperties: List[IRI],
deactivated: Boolean,
- message: Map[Option[Lang],String]
+ message: MessageMap,
+ severity: Option[Severity]
) extends Shape {
def isPropertyConstraint = false
@@ -72,7 +75,8 @@ case class PropertyShape(
closed: Boolean,
ignoredProperties: List[IRI],
deactivated: Boolean,
- message: Map[Option[Lang],String]
+ message: MessageMap,
+ severity: Option[Severity]
) extends Shape {
def isPropertyConstraint = true
@@ -91,7 +95,8 @@ object Shape {
closed = false,
ignoredProperties = List(),
deactivated = false,
- message = Map()
+ message = MessageMap.empty,
+ severity = None
)
def emptyPropertyShape(
@@ -105,6 +110,7 @@ object Shape {
closed = false,
ignoredProperties = List(),
deactivated = false,
- message = Map()
+ message = MessageMap.empty,
+ severity = None
)
}
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 3b324b4a..0dbfd2c4 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
@@ -8,8 +8,9 @@ import es.weso.rdf.parser.RDFParser
import es.weso.rdf.path._
import es.weso.shacl.SHACLPrefixes._
import es.weso.shacl._
+import es.weso.shacl.report._
-import scala.util.{ Failure, Success, Try }
+import scala.util.{Failure, Success, Try}
object RDF2Shacl extends RDFParser with LazyLogging {
@@ -95,6 +96,7 @@ object RDF2Shacl extends RDFParser with LazyLogging {
closed <- booleanFromPredicateOptional(sh_closed)(n, rdf)
deactivated <- booleanFromPredicateOptional(sh_deactivated)(n,rdf)
message <- parseMessage(n, rdf)
+ severity <- parseSeverity(n,rdf)
ignoredNodes <- {
println(s"Parsing deactivated: $deactivated for node: $n") ;
rdfListForPredicateOptional(sh_ignoredProperties)(n, rdf)
@@ -110,38 +112,31 @@ object RDF2Shacl extends RDFParser with LazyLogging {
closed = closed.getOrElse(false),
ignoredProperties = ignoredIRIs,
deactivated = deactivated.getOrElse(false),
- message = message
+ message = message,
+ severity = severity
)
val sref = ShapeRef(n)
parsedShapes += (sref -> shape)
sref
}
- private def parseMessage: RDFParser[Map[Option[Lang],String]] = (n,rdf) => for {
+ private def parseSeverity: RDFParser[Option[Severity]] = (n,rdf) => for {
+ maybeIri <- iriFromPredicateOptional(sh_severity)(n,rdf)
+ } yield maybeIri match {
+ case Some(`sh_Violation`) => Some(ViolationSeverity)
+ case Some(`sh_Warning`) => Some(WarningSeverity)
+ case Some(`sh_Info`) => Some(InfoSeverity)
+ case Some(iri) => Some(GenericSeverity(iri))
+ case None => None
+ }
+
+ private def parseMessage: RDFParser[MessageMap] = (n,rdf) => for {
nodes <- objectsFromPredicate(sh_message)(n,rdf)
map <- cnvMessages(nodes)(n,rdf)
} yield map
- private def cnvMessages(ns: Set[RDFNode]): RDFParser[Map[Option[Lang], String]] = (n,rdf) => {
- val zero: Either[String,Map[Option[Lang], String]] = Right(Map())
- def cmb(next: Either[String,Map[Option[Lang], String]], x: RDFNode): Either[String,Map[Option[Lang], String]] = x match {
- case StringLiteral(str) => for {
- m <- next
- updated <- m.get(None) match {
- case None => Right(m.updated(None, str))
- case Some(other) => Left(s"Two values for predicate sh:message: $str and $other for node $n")
- }
- } yield updated
- case LangLiteral(str,lang) => for {
- m <- next
- updated <- m.get(Some(lang)) match {
- case None => Right(m.updated(Some(lang), str))
- case Some(other) => Left(s"Two values for predicate sh:message: $str and $other for node $n for lang $lang")
- }
- } yield updated
- case _ => Left(s"Node $x must be a string or a language tagged string")
- }
- ns.foldLeft(zero)(cmb)
+ private def cnvMessages(ns: Set[RDFNode]): RDFParser[MessageMap] = (n,rdf) => {
+ MessageMap.fromRDFNodes(ns.toList)
}
@@ -157,6 +152,7 @@ object RDF2Shacl extends RDFParser with LazyLogging {
ignoredNodes <- rdfListForPredicateOptional(sh_ignoredProperties)(n, rdf)
deactivated <- booleanFromPredicateOptional(sh_deactivated)(n, rdf)
message <- parseMessage(n, rdf)
+ severity <- parseSeverity(n,rdf)
ignoredIRIs <- {
println(s"Parsing deactivated: $deactivated for node: $n")
nodes2iris(ignoredNodes)
@@ -171,7 +167,8 @@ object RDF2Shacl extends RDFParser with LazyLogging {
closed = closed.getOrElse(false),
ignoredProperties = ignoredIRIs,
deactivated = deactivated.getOrElse(false),
- message = message
+ message = message,
+ severity = severity
)
val sref = ShapeRef(n)
diff --git a/modules/shacl/src/main/scala/es/weso/shacl/converter/Shacl2RDF.scala b/modules/shacl/src/main/scala/es/weso/shacl/converter/Shacl2RDF.scala
index b8c4ef14..186f5848 100644
--- a/modules/shacl/src/main/scala/es/weso/shacl/converter/Shacl2RDF.scala
+++ b/modules/shacl/src/main/scala/es/weso/shacl/converter/Shacl2RDF.scala
@@ -9,6 +9,7 @@ import es.weso.rdf.PREFIXES._
import es.weso.rdf.{RDFBuilder, nodes}
import es.weso.rdf.saver.RDFSaver
import es.weso.shacl._
+import es.weso.shacl.report.Severity
class Shacl2RDF extends RDFSaver with LazyLogging {
@@ -88,6 +89,7 @@ class Shacl2RDF extends RDFSaver with LazyLogging {
_ <- deactivated(shapeNode, ps.deactivated)
_ <- ignoredProperties(shapeNode, ps.ignoredProperties)
_ <- message(shapeNode, ps.message)
+ _ <- severity(shapeNode, ps.severity)
pathNode <- makePath(ps.path)
_ <- addTriple(shapeNode, sh_path, pathNode)
_ <- saveList(ps.components.toList, component(shapeNode))
@@ -103,15 +105,19 @@ class Shacl2RDF extends RDFSaver with LazyLogging {
_ <- ignoredProperties(shapeNode, n.ignoredProperties)
_ <- saveList(n.components, component(shapeNode))
_ <- message(shapeNode, n.message)
+ _ <- severity(shapeNode, n.severity)
} yield shapeNode
- private def message(n: RDFNode, message: Map[Option[Lang],String]): RDFSaver[Unit] =
- sequence(message.toList.map {
- case (maybeLang, msg) => maybeLang match {
- case None => addTriple(n,sh_message,StringLiteral(msg))
- case Some(lang) => addTriple(n,sh_message, LangLiteral(msg,lang))
- }
- }).map(_ => ())
+ private def message(n: RDFNode, message: MessageMap): RDFSaver[Unit] =
+ sequence(message.getRDFNodes.map(addTriple(n,sh_message,_))
+ ).map(_ => ())
+
+ private def severity(n: RDFNode, severity: Option[Severity]): RDFSaver[Unit] =
+ severity match {
+ case None => ok(())
+ case Some(s) => addTriple(n,sh_severity,s.toIRI)
+ }
+
private def component(id: RDFNode)(c: Component): RDFSaver[Unit] = c match {
case ClassComponent(v) => addTriple(id, sh_class, v)
diff --git a/modules/shacl/src/main/scala/es/weso/shacl/report/Severity.scala b/modules/shacl/src/main/scala/es/weso/shacl/report/Severity.scala
index 4c6e2c98..1189f356 100644
--- a/modules/shacl/src/main/scala/es/weso/shacl/report/Severity.scala
+++ b/modules/shacl/src/main/scala/es/weso/shacl/report/Severity.scala
@@ -15,6 +15,9 @@ case object WarningSeverity extends Severity {
case object InfoSeverity extends Severity {
override def toIRI: IRI = sh_Info
}
+case class GenericSeverity(iri: IRI) extends Severity {
+ override def toIRI: IRI = iri
+}
object Severity {
val defaultSeverity = ViolationSeverity
diff --git a/modules/shacl/src/main/scala/es/weso/shacl/report/ValidationReport2RDF.scala b/modules/shacl/src/main/scala/es/weso/shacl/report/ValidationReport2RDF.scala
index bf711274..1e81db4c 100644
--- a/modules/shacl/src/main/scala/es/weso/shacl/report/ValidationReport2RDF.scala
+++ b/modules/shacl/src/main/scala/es/weso/shacl/report/ValidationReport2RDF.scala
@@ -35,6 +35,7 @@ class ValidationReport2RDF extends RDFSaver with LazyLogging {
_ <- addTriple(node, sh_sourceConstraintComponent, vr.sourceConstraintComponent)
_ <- addTriple(node, sh_sourceShape, vr.sourceShape.id)
_ <- addTripleObjects(node, sh_value, vr.values.toList)
+ _ <- addTripleObjects(node, sh_resultMessage, vr.messageMap.getRDFNodes)
_ <- saveList(vr.message.toList, message(node))
_ <- vr.focusPath match {
case None => ok(())
diff --git a/modules/shacl/src/main/scala/es/weso/shacl/report/ValidationResult.scala b/modules/shacl/src/main/scala/es/weso/shacl/report/ValidationResult.scala
index 80c44a87..f521074a 100644
--- a/modules/shacl/src/main/scala/es/weso/shacl/report/ValidationResult.scala
+++ b/modules/shacl/src/main/scala/es/weso/shacl/report/ValidationResult.scala
@@ -6,6 +6,9 @@ import es.weso.rdf.path._
import es.weso.shacl._
import es.weso.shacl.validator.Attempt
+
+// TODO: Refactor this code creating Classes for each error?
+
abstract class AbstractResult
class ValidationResult(val focusNode: RDFNode,
@@ -15,35 +18,38 @@ class ValidationResult(val focusNode: RDFNode,
val sourceShape: ShapeRef,
val values: Seq[RDFNode],
val message: Seq[LiteralValue],
+ val messageMap: MessageMap,
val details: Seq[AbstractResult]
) {
override def toString = s"Violation error on $focusNode: ${message.mkString(",")}"
}
+
object ValidationResult {
- def basic(suffix: String, focusNode: RDFNode, attempt: Attempt, msg: String) =
+ def basic(suffix: String, focusNode: RDFNode, attempt: Attempt, msg: String,
+ messageMap: MessageMap = MessageMap.empty
+ ) =
new ValidationResult(
sourceConstraintComponent = sh + suffix,
focusNode = focusNode,
- resultSeverity = Severity.defaultSeverity,
+ resultSeverity = attempt.severity,
sourceShape = attempt.shapeRef,
values = Seq(),
focusPath = attempt.path,
- message = Seq(LiteralValue(
- StringLiteral(msg)
- )),
+ message = Seq(LiteralValue(StringLiteral(msg))),
+ messageMap = messageMap,
details = Seq()
)
def notFoundShapeRef(node: RDFNode, attempt: Attempt, msg: String) =
- basic("NotFoundShapeRef", node, attempt, msg)
+ basic("NotFoundShapeRef", node, attempt, msg, MessageMap.fromString(msg))
def expectedPropertyShape(node: RDFNode, attempt: Attempt, msg: String) =
- basic("ExpectedPropertyShape", node, attempt, msg)
+ basic("ExpectedPropertyShape", node, attempt, msg, MessageMap.fromString(msg))
def shapesFailed(node: RDFNode, shape: Shape, ps: Set[Shape], attempt: Attempt, msg: String) =
- basic("ShapesFailed", node, attempt, msg)
+ basic("ShapesFailed", node, attempt, msg, MessageMap.fromString(msg))
def regexError(node: RDFNode, attempt: Attempt, msg: String) =
basic("RegEx error", node, attempt, msg)
diff --git a/modules/shacl/src/main/scala/es/weso/shacl/validator/Attempt.scala b/modules/shacl/src/main/scala/es/weso/shacl/validator/Attempt.scala
index b242a9b4..90540c53 100644
--- a/modules/shacl/src/main/scala/es/weso/shacl/validator/Attempt.scala
+++ b/modules/shacl/src/main/scala/es/weso/shacl/validator/Attempt.scala
@@ -2,15 +2,19 @@ package es.weso.shacl.validator
import es.weso.rdf.nodes._
import es.weso.rdf.path.SHACLPath
-import es.weso.shacl.ShapeRef
+import es.weso.shacl.{MessageMap, ShapeRef}
+import es.weso.shacl.report.Severity
/**
* Represents current validation attempt
* It contains the node and a shape
* It may contain a predicate, path or nothing
*/
-case class Attempt(nodeShape: NodeShapePair, path: Option[SHACLPath]) {
- def node: RDFNode = nodeShape.node
- def shapeId: RDFNode = nodeShape.shape.id
- def shapeRef: ShapeRef = nodeShape.shape
+case class Attempt(node: RDFNode,
+ shapeRef: ShapeRef,
+ messageMap: MessageMap,
+ severity: Severity,
+ path: Option[SHACLPath]
+ ) {
+ def shapeId: RDFNode = shapeRef.id
}
diff --git a/modules/shacl/src/main/scala/es/weso/shacl/validator/AttemptInfo.scala b/modules/shacl/src/main/scala/es/weso/shacl/validator/AttemptInfo.scala
new file mode 100644
index 00000000..59f6b757
--- /dev/null
+++ b/modules/shacl/src/main/scala/es/weso/shacl/validator/AttemptInfo.scala
@@ -0,0 +1,21 @@
+package es.weso.shacl.validator
+
+import cats._
+import es.weso.rdf.nodes._
+import es.weso.shacl.report.Severity
+import es.weso.shacl.{MessageMap, ShapeRef}
+
+case class AttemptInfo(node: RDFNode,
+ shape: ShapeRef,
+ messageMap: MessageMap,
+ severity: Severity) {
+
+ override def toString = AttemptInfo.nodeShapeShow.show(this)
+
+}
+
+object AttemptInfo {
+ implicit val nodeShapeShow = new Show[AttemptInfo] {
+ def show(ns: AttemptInfo) = s"[${ns.node},${ns.shape.showId}]"
+ }
+}
diff --git a/modules/shacl/src/main/scala/es/weso/shacl/validator/Evidence.scala b/modules/shacl/src/main/scala/es/weso/shacl/validator/Evidence.scala
index 238763a2..3eb61876 100644
--- a/modules/shacl/src/main/scala/es/weso/shacl/validator/Evidence.scala
+++ b/modules/shacl/src/main/scala/es/weso/shacl/validator/Evidence.scala
@@ -1,6 +1,8 @@
package es.weso.shacl.validator
import cats._
+import es.weso.rdf.nodes.RDFNode
+import es.weso.shacl.ShapeRef
case class Evidences(ls: List[Evidence])
@@ -8,13 +10,16 @@ abstract class Evidence {
override def toString = Evidence.evidenceShow.show(this)
}
-case class NodeShapeEvidence(pair: NodeShapePair, msg: String) extends Evidence
+case class NodeShapeEvidence(node: RDFNode,
+ shape: ShapeRef,
+ msg: String
+ ) extends Evidence
case class MsgEvidence(msg: String) extends Evidence
object Evidence {
implicit val evidenceShow = new Show[Evidence] {
def show(e: Evidence) = e match {
- case NodeShapeEvidence(pair, msg) => s"${pair}: $msg"
+ case NodeShapeEvidence(node, shape, msg) => s"$node@${shape.id}: $msg"
case MsgEvidence(msg) => msg
}
}
diff --git a/modules/shacl/src/main/scala/es/weso/shacl/validator/NodeShapePair.scala b/modules/shacl/src/main/scala/es/weso/shacl/validator/NodeShapePair.scala
deleted file mode 100644
index f5d30fea..00000000
--- a/modules/shacl/src/main/scala/es/weso/shacl/validator/NodeShapePair.scala
+++ /dev/null
@@ -1,17 +0,0 @@
-package es.weso.shacl.validator
-
-import cats._
-import es.weso.rdf.nodes._
-import es.weso.shacl.ShapeRef
-
-case class NodeShapePair(node: RDFNode, shape: ShapeRef) {
-
- override def toString = NodeShapePair.nodeShapeShow.show(this)
-
-}
-
-object NodeShapePair {
- implicit val nodeShapeShow = new Show[NodeShapePair] {
- def show(ns: NodeShapePair) = s"[${ns.node},${ns.shape.showId}]"
- }
-}
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 267ff95f..1d661a6e 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
@@ -11,7 +11,7 @@ import es.weso.utils.{MyLogging, RegEx}
import es.weso.shacl.showShacl._
import SHACLChecker.{logger, _}
import es.weso.rdf.operations.Comparisons
-import es.weso.shacl.report.ValidationResult
+import es.weso.shacl.report.{Severity, ValidationResult}
import es.weso.shacl.report.ValidationResult._
import es.weso.rdf.operations.Comparisons._
@@ -112,10 +112,13 @@ case class Validator(schema: Schema) extends MyLogging {
case ps: PropertyShape => nodePropertyShape(node,ps)
}
+ private def getSeverity(s: Shape): Severity =
+ s.severity.getOrElse(Severity.defaultSeverity)
+
def nodeNodeShape(node: RDFNode, ns: NodeShape): CheckTyping = {
logger.debug(s"Node $node - NodeShape ${ns.showId}")
logger.debug(s"Node shape is deactivated? (${ns.deactivated})")
- val attempt = Attempt(NodeShapePair(node, ShapeRef(ns.id)), None)
+ val attempt = Attempt(node, ShapeRef(ns.id), ns.message, getSeverity(ns), None)
for {
t0 <- getTyping
t <- runLocal(checkNodeShape(ns)(attempt)(node), _.addType(node, ns))
@@ -133,7 +136,7 @@ case class Validator(schema: Schema) extends MyLogging {
def nodePropertyShape(node: RDFNode, ps: PropertyShape): CheckTyping = {
logger.debug(s"Node $node - PropertyShape ${ps.showId}")
val path = ps.path
- val attempt = Attempt(NodeShapePair(node, ShapeRef(ps.id)), Some(path))
+ val attempt = Attempt(node, ShapeRef(ps.id), ps.message, getSeverity(ps), Some(path))
if (ps.deactivated) for {
t <- addEvidence(attempt, s"Property shape ${ps.showId} is deactivated")
} yield (t,true)
@@ -204,8 +207,9 @@ case class Validator(schema: Schema) extends MyLogging {
rdf <- getRDF
os = rdf.objectsWithPath(node, path).toList
_ <- debug(s"checkPropertyShapePath: os=$os\nnode: $node, path=${path.show}")
+ shape <- getShapeRef(sref,attempt,node)
r <- checkAllWithTyping(os,(o: RDFNode) => {
- val newAttempt = Attempt(NodeShapePair(o, sref), Some(path))
+ val newAttempt = Attempt(o, sref, shape.message, getSeverity(shape), Some(path))
checkPropertyShape(newAttempt)(o)(ps)
})
} yield r
@@ -276,9 +280,10 @@ case class Validator(schema: Schema) extends MyLogging {
ps <- getPropertyShapeRef(psref, attempt, node)
rdf <- getRDF
os = rdf.objectsWithPath(node, path).toList
+ shape <- getShapeRef(psref,attempt,node)
_ <- debug(s"propertyShape2PropertyChecker: $os for $path")
check: CheckTyping = checkValues(os, o => {
- val newAttempt = Attempt(NodeShapePair(o, psref), Some(path))
+ val newAttempt = Attempt(o, psref, shape.message, getSeverity(shape), Some(path))
checkPropertyShape(newAttempt)(o)(ps)
}
)
@@ -588,7 +593,7 @@ case class Validator(schema: Schema) extends MyLogging {
private def filterConformShapes(values: Seq[RDFNode], shapes: Seq[ShapeRef], attempt: Attempt): Check[Seq[RDFNode]] = {
logger.debug(s"FilterConformShapes(values=$values, shapes=$shapes)")
def checkValuesShapes: Check[List[(RDFNode, Boolean)]] = {
- values.toList.map(value => conformsNodeShapes(value, shapes, attempt)).sequence
+ sequence(values.toList.map(value => conformsNodeShapes(value, shapes, attempt)))
}
for {
cs <- checkValuesShapes
@@ -613,22 +618,9 @@ case class Validator(schema: Schema) extends MyLogging {
}
private def or(sRefs: Seq[ShapeRef]): NodeChecker = attempt => node => {
- /* val checks: List[CheckTyping] = sRefs.toList.map(s => {
- nodeShapeRef(node, s, attempt)
- }) */
val last: CheckTyping = fail(s"None of the components of or pass")
- for {
- // t0 <- getTyping
- r <- checkSomeFlag(sRefs.toStream,
- (sref: ShapeRef) => nodeShapeRef(node, sref, attempt),
- last
- )
-/* t2 <- {
- logger.debug(s"@@@ Inside or. result of checkSome:\n${showResult(t1)}")
- addEvidence(attempt, s"$node passes or(${sRefs.map(_.showId).mkString(",")})")
- } */
- // t3 <- combineTypings(Seq(t1, t2))
- } yield r
+ def fn(sref: ShapeRef): CheckTyping = nodeShapeRef(node, sref, attempt)
+ checkSomeFlag(sRefs.toStream,fn,last)
}
private def not(sref: ShapeRef): NodeChecker = attempt => node => {
@@ -771,12 +763,11 @@ case class Validator(schema: Schema) extends MyLogging {
case _ => err(expectedPropertyShape(node, attempt, s"Expected shape $shape to be a property shape"))
}
private def addEvidence(attempt: Attempt, msg: String): Check[ShapeTyping] = {
- val nodeShape = attempt.nodeShape
for {
t <- getTyping
- shape <- getShapeRef(nodeShape.shape, attempt, attempt.node)
- _ <- addLog(List(NodeShapeEvidence(nodeShape, msg)))
- } yield t.addEvidence(nodeShape.node, shape, msg)
+ shape <- getShapeRef(attempt.shapeRef, attempt, attempt.node)
+ _ <- addLog(List(NodeShapeEvidence(attempt.node, attempt.shapeRef, msg)))
+ } yield t.addEvidence(attempt.node, shape, msg)
}
private def addNotEvidence(attempt: Attempt,
@@ -784,13 +775,13 @@ case class Validator(schema: Schema) extends MyLogging {
msg: String
): Check[ShapeTyping] = {
val node = attempt.node
- // val sref = attempt.shapeRef
+ val sref = attempt.shapeRef
for {
t <- getTyping
- sref <- getShapeRef(attempt.nodeShape.shape, attempt, node)
- _ <- addLog(List(NodeShapeEvidence(attempt.nodeShape, msg)))
+ shape <- getShapeRef(sref, attempt, node)
+ _ <- addLog(List(NodeShapeEvidence(attempt.node, sref, msg)))
} yield {
- t.addNotEvidence(node, sref, e)
+ t.addNotEvidence(node, shape, e)
}
}
diff --git a/modules/shacl/src/test/resources/shacl/report/message.ttl b/modules/shacl/src/test/resources/shacl/report/message.ttl
new file mode 100644
index 00000000..508aa135
--- /dev/null
+++ b/modules/shacl/src/test/resources/shacl/report/message.ttl
@@ -0,0 +1,21 @@
+prefix xsd:
+prefix :
+prefix sh:
+
+:MyShape
+ a sh:NodeShape ;
+ sh:targetNode :MyInstance ;
+ sh:property :p1 ;
+ sh:property :p2 .
+
+:p1 sh:path :myProperty ;
+ sh:minCount 1 ;
+ sh:datatype xsd:string ;
+ sh:severity sh:Warning .
+:p2 sh:path :myProperty ;
+ sh:maxLength 10 ;
+ sh:message "Too many characters"@en ;
+ sh:message "Zu viele Zeichen"@de .
+
+:MyInstance
+ :myProperty "http://toomanycharacters"^^xsd:anyURI .
\ No newline at end of file
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 67cac6c1..a76806f1 100644
--- a/modules/shacl/src/test/scala/es/weso/shacl/AbstractSyntaxTest.scala
+++ b/modules/shacl/src/test/scala/es/weso/shacl/AbstractSyntaxTest.scala
@@ -17,7 +17,7 @@ class AbstractSyntaxTest extends FunSpec with Matchers {
false,
List(),
false,
- Map()
+ MessageMap.empty
)
shape.id should be(id)
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 d8801f62..538968b7 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,8 +7,8 @@ import cats.implicits._
import es.weso.rdf.RDFReader
import es.weso.rdf.jena.RDFAsJenaModel
import es.weso.rdf.nodes._
-import es.weso.shacl.{Shape, ShapeRef}
-import es.weso.shacl.report.ValidationResult
+import es.weso.shacl.{MessageMap, Shape, ShapeRef}
+import es.weso.shacl.report.{Severity, ValidationResult}
// import es.weso.shacl.validator.ShapeTyping._
// import es.weso.typing.Typing
@@ -22,7 +22,11 @@ class SHACLCheckerTest extends FunSpec with Matchers with TryValues with OptionV
def mkErr(str: String): ValidationResult =
- ValidationResult.basic("",StringLiteral(str),Attempt(NodeShapePair(StringLiteral(str),ShapeRef(StringLiteral(str))),None),str)
+ ValidationResult.basic("",StringLiteral(str),Attempt(StringLiteral(str),
+ ShapeRef(StringLiteral(str)),
+ MessageMap.empty,
+ Severity.defaultSeverity,None
+ ),str)
type T = (RDFNode, Shape, Boolean)