Class: SHACL::Algebra::Shape

Inherits:
Operator
  • Object
show all
Defined in:
lib/shacl/algebra/shape.rb

Direct Known Subclasses

NodeShape, PropertyShape

Constant Summary collapse

NAME =
:Shape
NODE_KIND_COMPARE =

The matrix of comparisons of different types of nodes

{
  RDF::URI => [
    RDF::Vocab::SHACL.IRI,
    RDF::Vocab::SHACL.BlankNodeOrIRI,
    RDF::Vocab::SHACL.IRIOrLiteral,
  ],
  RDF::Node => [
    RDF::Vocab::SHACL.BlankNode,
    RDF::Vocab::SHACL.BlankNodeOrIRI,
    RDF::Vocab::SHACL.BlankNodeOrLiteral,
  ],
  RDF::Literal => [
    RDF::Vocab::SHACL.Literal,
    RDF::Vocab::SHACL.IRIOrLiteral,
    RDF::Vocab::SHACL.BlankNodeOrLiteral,
  ]
}

Constants inherited from Operator

Operator::ALL_KEYS

Instance Attribute Summary

Attributes inherited from Operator

#graph, #options

Instance Method Summary collapse

Methods inherited from Operator

apply_op, #comment, #conforms, #deactivated?, from_expanded_value, from_json, #id, iri, #iri, #label, #not_satisfied, parse_path, #satisfy, to_rdf, #to_sxp_bin, #type

Instance Method Details

#builtin_class(types, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

Specifies that each value node is a SHACL instance of a given type.

Examples:

ex:ClassExampleShape
  a sh:NodeShape ;
  sh:targetNode ex:Bob, ex:Alice, ex:Carol ;
  sh:property [
    sh:path ex:address ;
    sh:class ex:PostalAddress ;
  ] .


52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/shacl/algebra/shape.rb', line 52

def builtin_class(types, node, path, value_nodes, **options)
  value_nodes.map do |n|
    has_type = n.resource? && begin
      objects = graph.query({subject: n, predicate: RDF.type}).objects
      types.all? {|t| objects.include?(t)}
    end
    satisfy(focus: node, path: path,
      value: n,
      message: "is#{' not' unless has_type} of class #{type.to_sxp}",
      resultSeverity: (options.fetch(:severity) unless has_type),
      component: RDF::Vocab::SHACL.ClassConstraintComponent,
      **options)
  end.flatten.compact
end

#builtin_datatype(datatype, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

Specifies a condition to be satisfied with regards to the datatype of each value node.

Examples:

ex:DatatypeExampleShape
  a sh:NodeShape ;
  sh:targetNode ex:Alice, ex:Bob, ex:Carol ;
  sh:property [
    sh:path ex:age ;
    sh:datatype xsd:integer ;
  ] .


83
84
85
86
87
88
89
90
91
92
93
# File 'lib/shacl/algebra/shape.rb', line 83

def builtin_datatype(datatype, node, path, value_nodes, **options)
  value_nodes.map do |n|
    has_datatype = n.literal? && n.datatype == datatype && n.valid?
    satisfy(focus: node, path: path,
      value: n,
      message: "is#{' not' unless has_datatype} a valid literal with datatype #{datatype.to_sxp}",
      resultSeverity: (options.fetch(:severity) unless has_datatype),
      component: RDF::Vocab::SHACL.DatatypeConstraintComponent,
      **options)
  end.flatten.compact
end

#builtin_disjoint(properties, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

Specifies the condition that the set of value nodes is disjoint with the set of objects of the triples that have the focus node as subject and the value of sh:disjoint as predicate.

Examples:

ex:DisjointExampleShape
  a sh:NodeShape ;
  sh:targetNode ex:USA, ex:Germany ;
  sh:property [
    sh:path ex:prefLabel ;
    sh:disjoint ex:altLabel ;
  ] .


111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/shacl/algebra/shape.rb', line 111

def builtin_disjoint(properties, node, path, value_nodes, **options)
  disjoint_nodes = properties.map do |prop|
    graph.query({subject: node, predicate: prop}).objects
  end.flatten.compact
  value_nodes.map do |n|
    has_value = disjoint_nodes.include?(n)
    satisfy(focus: node, path: path,
      value: n,
      message: "is#{' not' unless has_value} disjoint with #{disjoint_nodes.to_sxp}",
      resultSeverity: (options.fetch(:severity) if has_value),
      component: RDF::Vocab::SHACL.DisjointConstraintComponent,
      **options)
  end.flatten.compact
end

#builtin_equals(property, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

Specifies the condition that the set of all value nodes is equal to the set of objects of the triples that have the focus node as subject and the value of sh:equals as predicate.

Examples:

ex:EqualExampleShape
  a sh:NodeShape ;
  sh:targetNode ex:Bob ;
  sh:property [
    sh:path ex:firstName ;
    sh:equals ex:givenName ;
  ] .


142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/shacl/algebra/shape.rb', line 142

def builtin_equals(property, node, path, value_nodes, **options)
  equal_nodes = graph.query({subject: node, predicate: property}).objects
  (value_nodes.map do |n|
    has_value = equal_nodes.include?(n)
    satisfy(focus: node, path: path,
      value: n,
      message: "is#{' not' unless has_value} a value in #{equal_nodes.to_sxp}",
      resultSeverity: (options.fetch(:severity) unless has_value),
      component: RDF::Vocab::SHACL.EqualsConstraintComponent,
      **options)
  end +
  equal_nodes.map do |n|
    !value_nodes.include?(n) ?
      not_satisfied(focus: node, path: path,
        value: n,
        message: "should have a value in #{value_nodes.to_sxp}",
        resultSeverity: options.fetch(:severity),
        component: RDF::Vocab::SHACL.EqualsConstraintComponent,
        **options) :
      nil
  end).flatten.compact
end

#builtin_hasValue(term, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

Specifies the condition that at least one value node is equal to the given RDF term.

Examples:

ex:StanfordGraduate
  a sh:NodeShape ;
  sh:targetNode ex:Alice ;
  sh:property [
    sh:path ex:alumniOf ;
    sh:hasValue ex:Stanford ;
  ] .


181
182
183
184
185
186
187
188
# File 'lib/shacl/algebra/shape.rb', line 181

def builtin_hasValue(term, node, path, value_nodes, **options)
  has_value = value_nodes.include?(term)
  [satisfy(focus: node, path: path,
    message: "is#{' not' unless has_value} the value #{term.to_sxp}",
    resultSeverity: (options.fetch(:severity) unless has_value),
    component: RDF::Vocab::SHACL.HasValueConstraintComponent,
    **options)]
end

#builtin_in(list, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

Specifies the condition that each value node is a member of a provided SHACL list.

Examples:

ex:InExampleShape
  a sh:NodeShape ;
  sh:targetNode ex:RainbowPony ;
  sh:property [
    sh:path ex:color ;
    sh:in ( ex:Pink ex:Purple ) ;
  ] .


206
207
208
209
210
211
212
213
214
215
216
# File 'lib/shacl/algebra/shape.rb', line 206

def builtin_in(list, node, path, value_nodes, **options)
  value_nodes.map do |n|
    has_value = list.include?(n)
    satisfy(focus: node, path: path,
      value: n,
      message: "is#{' not' unless has_value} a value in #{list.to_sxp}",
      resultSeverity: (options.fetch(:severity) unless has_value),
      component: RDF::Vocab::SHACL.InConstraintComponent,
      **options)
  end.flatten.compact
end

#builtin_languageIn(datatypes, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

The condition specified by sh:languageIn is that the allowed language tags for each value node are limited by a given list of language tags.

Examples:

ex:NewZealandLanguagesShape
  a sh:NodeShape ;
  sh:targetNode ex:Mountain, ex:Berg ;
  sh:property [
    sh:path ex:prefLabel ;
    sh:languageIn ( "en" "mi" ) ;
  ] .


233
234
235
236
237
238
239
240
241
242
243
# File 'lib/shacl/algebra/shape.rb', line 233

def builtin_languageIn(datatypes, node, path, value_nodes, **options)
  value_nodes.map do |n|
    has_language = n.literal? && datatypes.any? {|l| n.language.to_s.start_with?(l)}
    satisfy(focus: node, path: path,
      value: n,
      message: "is#{' not' unless has_language} a literal with a language in #{datatypes.to_sxp}",
      resultSeverity: (options.fetch(:severity) unless has_language),
      component: RDF::Vocab::SHACL.LanguageInConstraintComponent,
      **options)
  end.flatten.compact
end

#builtin_maxExclusive(term, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

Compares value nodes to be < than the specified value.

Examples:

ex:NumericRangeExampleShape
  a sh:NodeShape ;
  sh:targetNode ex:Bob, ex:Alice, ex:Ted ;
  sh:property [
    sh:path ex:age ;
    sh:minInclusive 0 ;
    sh:maxInclusive 150 ;
  ] .


262
263
264
265
# File 'lib/shacl/algebra/shape.rb', line 262

def builtin_maxExclusive(term, node, path, value_nodes, **options)
  compare(:<, [term], node, path, value_nodes,
          RDF::Vocab::SHACL.MaxExclusiveConstraintComponent, **options)
end

#builtin_maxInclusive(term, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

Compares value nodes to be <= than the specified value.

Examples:

ex:NumericRangeExampleShape
  a sh:NodeShape ;
  sh:targetNode ex:Bob, ex:Alice, ex:Ted ;
  sh:property [
    sh:path ex:age ;
    sh:minInclusive 0 ;
    sh:maxInclusive 150 ;
  ] .


284
285
286
287
# File 'lib/shacl/algebra/shape.rb', line 284

def builtin_maxInclusive(term, node, path, value_nodes, **options)
  compare(:<=, [term], node, path, value_nodes,
          RDF::Vocab::SHACL.MaxInclusiveConstraintComponent, **options)
end

#builtin_maxLength(term, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

Specifies the maximum string length of each value node that satisfies the condition. This can be applied to any literals and IRIs, but not to blank nodes.



296
297
298
299
300
301
302
303
304
305
306
# File 'lib/shacl/algebra/shape.rb', line 296

def builtin_maxLength(term, node, path, value_nodes, **options)
  value_nodes.map do |n|
    compares = !n.node? && n.to_s.length <= term.to_i
    satisfy(focus: node, path: path,
      value: n,
      message: "is#{' not' unless compares} a literal at with length <= #{term.to_sxp}",
      resultSeverity: (options.fetch(:severity) unless compares),
      component: RDF::Vocab::SHACL.MaxLengthConstraintComponent,
      **options)
  end.flatten.compact
end

#builtin_minExclusive(term, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

Compares value nodes to be > than the specified value.

Examples:

ex:NumericRangeExampleShape
  a sh:NodeShape ;
  sh:targetNode ex:Bob, ex:Alice, ex:Ted ;
  sh:property [
    sh:path ex:age ;
    sh:minInclusive 0 ;
    sh:maxInclusive 150 ;
  ] .


325
326
327
328
# File 'lib/shacl/algebra/shape.rb', line 325

def builtin_minExclusive(term, node, path, value_nodes, **options)
  compare(:>, [term], node, path, value_nodes,
          RDF::Vocab::SHACL.MinExclusiveConstraintComponent, **options)
end

#builtin_minInclusive(term, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

Compares value nodes to be >= than the specified value.

Examples:

ex:NumericRangeExampleShape
  a sh:NodeShape ;
  sh:targetNode ex:Bob, ex:Alice, ex:Ted ;
  sh:property [
    sh:path ex:age ;
    sh:minInclusive 0 ;
    sh:maxInclusive 150 ;
  ] .


347
348
349
350
# File 'lib/shacl/algebra/shape.rb', line 347

def builtin_minInclusive(term, node, path, value_nodes, **options)
  compare(:>=, [term], node, path, value_nodes,
          RDF::Vocab::SHACL.MinInclusiveConstraintComponent, **options)
end

#builtin_minLength(term, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

Specifies the minimum string length of each value node that satisfies the condition. This can be applied to any literals and IRIs, but not to blank nodes.



359
360
361
362
363
364
365
366
367
368
369
# File 'lib/shacl/algebra/shape.rb', line 359

def builtin_minLength(term, node, path, value_nodes, **options)
  value_nodes.map do |n|
    compares = !n.node? && n.to_s.length >= term.to_i
    satisfy(focus: node, path: path,
      value: n,
      message: "is#{' not' unless compares} a literal with length >= #{term.to_sxp}",
      resultSeverity: (options.fetch(:severity) unless compares),
      component: RDF::Vocab::SHACL.MinLengthConstraintComponent,
      **options)
  end.flatten.compact
end

#builtin_nodeKind(term, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

Specifies a condition to be satisfied by the RDF node kind of each value node.

Examples:

ex:NodeKindExampleShape
  a sh:NodeShape ;
  sh:targetObjectsOf ex:knows ;
  sh:nodeKind sh:IRI .


404
405
406
407
408
409
410
411
412
413
414
# File 'lib/shacl/algebra/shape.rb', line 404

def builtin_nodeKind(term, node, path, value_nodes, **options)
  value_nodes.map do |n|
    compares = NODE_KIND_COMPARE.fetch(n.class, []).include?(term)
    satisfy(focus: node, path: path,
      value: n,
      message: "is#{' not' unless compares} a node kind match of #{term.to_sxp}",
      resultSeverity: (options.fetch(:severity) unless compares),
      component: RDF::Vocab::SHACL.NodeKindConstraintComponent,
      **options)
  end.flatten.compact
end

#builtin_pattern(pattern, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

Specifies a regular expression that each value node matches to satisfy the condition.

Examples:

ex:PatternExampleShape
  a sh:NodeShape ;
  sh:targetNode ex:Bob, ex:Alice, ex:Carol ;
  sh:property [
    sh:path ex:bCode ;
    sh:pattern "^B" ;    # starts with 'B'
    sh:flags "i" ;       # Ignore case
  ] .


433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'lib/shacl/algebra/shape.rb', line 433

def builtin_pattern(pattern, node, path, value_nodes, **options)
  flags = options[:flags].to_s
  regex_opts = 0 |
  regex_opts |= Regexp::MULTILINE  if flags.include?(?m)
  regex_opts |= Regexp::IGNORECASE if flags.include?(?i)
  regex_opts |= Regexp::EXTENDED   if flags.include?(?x)
  pat = Regexp.new(pattern, regex_opts)

  value_nodes.map do |n|
    compares = !n.node? && pat.match?(n.to_s)
    satisfy(focus: node, path: path,
      value: n,
      message: "is#{' not' unless compares} a match #{pat.inspect}",
      resultSeverity: (options.fetch(:severity) unless compares),
      component: RDF::Vocab::SHACL.PatternConstraintComponent,
      **options)
  end.flatten.compact
end

#targetNodesArray<RDF::Term>

Returns the nodes matching this particular shape, based upon the shape properties:

* `targetNode`
* `targetClass`
* `targetSubjectsOf`
* `targetObjectsOf`
* `id` 


15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/shacl/algebra/shape.rb', line 15

def targetNodes
  (Array(@options[:targetNode]) +
  Array(@options[:targetClass]).map do |cls|
    graph.query({predicate: RDF.type, object: cls}).subjects
  end +
  Array(@options[:targetSubjectsOf]).map do |pred|
    graph.query({predicate: pred}).subjects
  end +
  Array(@options[:targetObjectsOf]).map do |pred|
    graph.query({predicate: pred}).objects
  end + (
    Array(type).include?(RDF::RDFS.Class) ?
      graph.query({predicate: RDF.type, object: id}).subjects :
      []
  )).flatten.uniq
end