Class: SHACL::Algebra::Shape

Inherits:
Operator show all
Defined in:
vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/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

Returns:

{
  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::BUILTIN_KEYS, Operator::PARAMETERS

Constants included from RDF::Util::Logger

RDF::Util::Logger::IOWrapper

Constants inherited from SPARQL::Algebra::Operator

SPARQL::Algebra::Operator::ARITY, SPARQL::Algebra::Operator::IsURI, SPARQL::Algebra::Operator::URI

Constants included from SPARQL::Algebra::Expression

SPARQL::Algebra::Expression::PATTERN_PARENTS

Instance Attribute Summary

Attributes inherited from Operator

#graph, #options, #shapes_graph

Attributes inherited from SPARQL::Algebra::Operator

#operands

Instance Method Summary collapse

Methods inherited from Operator

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

Methods included from JSON::LD::Utils

#add_value, #as_array, #as_resource, #blank_node?, #compare_values, #graph?, #has_value?, #index?, #list?, #node?, #node_or_ref?, #node_reference?, #property?, #simple_graph?, #value?

Methods included from RDF::Util::Logger

#log_debug, #log_depth, #log_error, #log_fatal, #log_info, #log_recover, #log_recovering?, #log_statistics, #log_warn, #logger

Methods inherited from SPARQL::Algebra::Operator

#aggregate?, arity, base_uri, #base_uri, base_uri=, #bind, #boolean, #constant?, #deep_dup, #each_descendant, #eql?, #evaluatable?, evaluate, #executable?, #first_ancestor, for, #formulae, #initialize, #inspect, #ndvars, #node?, #operand, #optimize, #optimize!, #parent, #parent=, prefixes, #prefixes, prefixes=, #rewrite, #to_binary, #to_sparql, to_sparql, #to_sxp, #to_sxp_bin, #validate!, #variable?, #variables, #vars

Methods included from SPARQL::Algebra::Expression

cast, #constant?, #evaluate, extension, extension?, extensions, for, #invalid?, new, #node?, open, #optimize, #optimize!, parse, register_extension, #to_sxp_bin, #valid?, #validate!, #variable?

Constructor Details

This class inherits a constructor from SPARQL::Algebra::Operator

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 ;
	] .

Parameters:

Returns:



52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/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 ;
	] .

Parameters:

Returns:



83
84
85
86
87
88
89
90
91
92
93
94
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/lib/shacl/algebra/shape.rb', line 83

def builtin_datatype(datatype, node, path, value_nodes, **options)
  datatype = datatype.first if datatype.is_a?(Array)
  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 ;
	] .

Parameters:

Returns:



112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/lib/shacl/algebra/shape.rb', line 112

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 ;
	] .

Parameters:

Returns:



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/lib/shacl/algebra/shape.rb', line 143

def builtin_equals(property, node, path, value_nodes, **options)
  property = property.first if property.is_a?(Array)
  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 ;
	] .

Parameters:

Returns:



183
184
185
186
187
188
189
190
191
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/lib/shacl/algebra/shape.rb', line 183

def builtin_hasValue(term, node, path, value_nodes, **options)
  term = term.first if term.is_a?(Array)
  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 ) ;
	] .

Parameters:

Returns:



209
210
211
212
213
214
215
216
217
218
219
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/lib/shacl/algebra/shape.rb', line 209

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" ) ;
	] .

Parameters:

Returns:



236
237
238
239
240
241
242
243
244
245
246
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/lib/shacl/algebra/shape.rb', line 236

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 ;
	] .

Parameters:

Returns:



265
266
267
268
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/lib/shacl/algebra/shape.rb', line 265

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 ;
	] .

Parameters:

Returns:



287
288
289
290
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/lib/shacl/algebra/shape.rb', line 287

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.

Parameters:

Returns:



299
300
301
302
303
304
305
306
307
308
309
310
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/lib/shacl/algebra/shape.rb', line 299

def builtin_maxLength(term, node, path, value_nodes, **options)
  term = term.first if term.is_a?(Array)
  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 ;
	] .

Parameters:

Returns:



329
330
331
332
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/lib/shacl/algebra/shape.rb', line 329

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 ;
	] .

Parameters:

Returns:



351
352
353
354
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/lib/shacl/algebra/shape.rb', line 351

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.

Parameters:

Returns:



363
364
365
366
367
368
369
370
371
372
373
374
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/lib/shacl/algebra/shape.rb', line 363

def builtin_minLength(term, node, path, value_nodes, **options)
  term = term.first if term.is_a?(Array)
  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 .

Parameters:

Returns:



409
410
411
412
413
414
415
416
417
418
419
420
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/lib/shacl/algebra/shape.rb', line 409

def builtin_nodeKind(term, node, path, value_nodes, **options)
  term = term.first if term.is_a?(Array)
  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

#compare(method, terms, node, path, value_nodes, component, **options) ⇒ Object (protected)

Common comparison logic for lessThan, lessThanOrEqual, max/minInclusive/Exclusive



425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/lib/shacl/algebra/shape.rb', line 425

def compare(method, terms, node, path, value_nodes, component, **options)
  terms = [terms] unless terms.is_a?(Array)
  value_nodes.map do |left|
    results = terms.map do |right|
      case left
      when RDF::Literal
        unless right.literal? && (
          (left.simple? && right.simple?) ||
          (left.is_a?(RDF::Literal::Numeric) && right.is_a?(RDF::Literal::Numeric)) ||
          (left.datatype == right.datatype && left.language == right.language))
          :incomperable
        else
          left.send(method, right)
        end
      when RDF::URI
        right.uri? && left.send(method, right)
      else
        :incomperable
      end
    end

    if results.include?(:incomperable)
      not_satisfied(focus: node, path: path,
        value: left,
        message: "is incomperable with #{terms.to_sxp}",
        resultSeverity: options.fetch(:severity),
        component: component,
        **options)
    elsif results.include?(false)
      not_satisfied(focus: node, path: path,
        value: left,
        message: "is not #{method} than #{terms.to_sxp}",
        resultSeverity: options.fetch(:severity),
        component: component,
        **options)
    else
      satisfy(focus: node, path: path,
        value: left,
        message: "is #{method} than #{terms.to_sxp}",
        component: component,
        **options)
    end
  end.flatten.compact
end

#targetNodesArray<RDF::Term>

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

  • targetNode
  • targetClass
  • targetSubjectsOf
  • targetObjectsOf
  • id – where type includes rdfs:Class

Returns:



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'vendor/bundler/ruby/3.2.0/bundler/gems/shacl-63ac93ccd0e9/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