Class: SHACL::Algebra::PropertyShape

Inherits:
Shape
  • Object
show all
Defined in:
lib/shacl/algebra/property_shape.rb

Constant Summary collapse

NAME =
:PropertyShape

Constants inherited from Shape

Shape::NODE_KIND_COMPARE

Constants inherited from Operator

Operator::ALL_KEYS

Instance Attribute Summary

Attributes inherited from Operator

#graph, #options

Instance Method Summary collapse

Methods inherited from Shape

#builtin_class, #builtin_datatype, #builtin_disjoint, #builtin_equals, #builtin_hasValue, #builtin_in, #builtin_languageIn, #builtin_maxExclusive, #builtin_maxInclusive, #builtin_maxLength, #builtin_minExclusive, #builtin_minInclusive, #builtin_minLength, #builtin_nodeKind, #builtin_pattern, #targetNodes

Methods inherited from Operator

apply_op, #comment, #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_lessThan(property, node, path, value_nodes, **options) ⇒ Array<SHACL::ValidationResult>

Specifies the condition that each value node is smaller than all the objects of the triples that have the focus node as subject and the value of sh:lessThan as predicate.

Examples:

ex:LessThanExampleShape
  a sh:NodeShape ;
  sh:property [
    sh:path ex:startDate ;
    sh:lessThan ex:endDate ;
  ] .

Parameters:

  • property (RDF::URI)

    the property of the focus node whose values must be equal to some value node.

  • node (RDF::Term)

    the focus node

  • path (RDF::URI, SPARQL::Algebra::Expression)

    (nil) the property path from the focus node to the value nodes.

  • value_nodes (Array<RDF::Term>)

Returns:



102
103
104
105
106
# File 'lib/shacl/algebra/property_shape.rb', line 102

def builtin_lessThan(property, node, path, value_nodes, **options)
  terms = graph.query({subject: node, predicate: property}).objects
  compare(:<, terms, node, path, value_nodes,
          RDF::Vocab::SHACL.LessThanConstraintComponent, **options)
end

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

Specifies the condition that each value node is smaller than or equal to all the objects of the triples that have the focus node as subject and the value of sh:lessThanOrEquals as predicate.

Parameters:

  • property (RDF::URI)

    the property of the focus node whose values must be equal to some value node.

  • node (RDF::Term)

    the focus node

  • path (RDF::URI, SPARQL::Algebra::Expression)

    (nil) the property path from the focus node to the value nodes.

  • value_nodes (Array<RDF::Term>)

Returns:



115
116
117
118
119
# File 'lib/shacl/algebra/property_shape.rb', line 115

def builtin_lessThanOrEquals(property, node, path, value_nodes, **options)
  terms = graph.query({subject: node, predicate: property}).objects
  compare(:<=, terms, node, path, value_nodes,
          RDF::Vocab::SHACL.LessThanOrEqualsConstraintComponent, **options)
end

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

Specifies the maximum number of value nodes.

Parameters:

  • count (Integer)
  • node (RDF::Term)

    the focus node

  • path (RDF::URI, SPARQL::Algebra::Expression)

    (nil) the property path from the focus node to the value nodes.

  • value_nodes (Array<RDF::Term>)

Returns:



132
133
134
135
136
137
138
# File 'lib/shacl/algebra/property_shape.rb', line 132

def builtin_maxCount(count, node, path, value_nodes, **options)
  satisfy(focus: node, path: path,
    message: "#{value_nodes.count} <= maxCount #{count}",
    resultSeverity: (options.fetch(:severity) unless value_nodes.count <= count.to_i),
    component: RDF::Vocab::SHACL.MaxCountConstraintComponent,
    **options)
end

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

Specifies the minimum number of value nodes.

Examples:

ex:MinCountExampleShape
  a sh:PropertyShape ;
  sh:targetNode ex:Alice, ex:Bob ;
  sh:path ex:name ;
  sh:minCount 1 .

Parameters:

  • count (Integer)
  • node (RDF::Term)

    the focus node

  • path (RDF::URI, SPARQL::Algebra::Expression)

    (nil) the property path from the focus node to the value nodes.

  • value_nodes (Array<RDF::Term>)

Returns:



154
155
156
157
158
159
160
# File 'lib/shacl/algebra/property_shape.rb', line 154

def builtin_minCount(count, node, path, value_nodes, **options)
  satisfy(focus: node, path: path,
    message: "#{value_nodes.count} >= minCount #{count}",
    resultSeverity: (options.fetch(:severity) unless value_nodes.count >= count.to_i),
    component: RDF::Vocab::SHACL.MinCountConstraintComponent,
    **options)
end

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

The property ‘sh:uniqueLang` can be set to `true` to specify that no pair of value nodes may use the same language tag.

Parameters:

  • uniq (Boolean)
  • node (RDF::Term)

    the focus node

  • path (RDF::URI, SPARQL::Algebra::Expression)

    (nil) the property path from the focus node to the value nodes.

  • value_nodes (Array<RDF::Term>)

Returns:



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/shacl/algebra/property_shape.rb', line 169

def builtin_uniqueLang(uniq, node, path, value_nodes, **options)
  if !value_nodes.all?(&:literal?)
    not_satisfied(focus: node, path: path,
      message: "not all values are literals",
      resultSeverity: options.fetch(:severity),
      component: RDF::Vocab::SHACL.UniqueLangConstraintComponent,
      **options)
  elsif value_nodes.map(&:language).compact.length != value_nodes.map(&:language).compact.uniq.length
    not_satisfied(focus: node, path: path,
      message: "not all values have unique language tags",
      resultSeverity: options.fetch(:severity),
      component: RDF::Vocab::SHACL.UniqueLangConstraintComponent,
      **options)
  else
    satisfy(focus: node, path: path,
      message: "all literals have unique language tags",
      component: RDF::Vocab::SHACL.UniqueLangConstraintComponent,
      **options)
  end
end

#conforms(node, depth: 0, **options) ⇒ Array<SHACL::ValidationResult>

Validates the specified ‘property` within `graph`, a list of ValidationResult.

A property conforms the nodes found by evaluating it’s ‘path` all conform.

Parameters:

  • node (RDF::Term)
  • options (Hash{Symbol => Object})

Returns:



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/shacl/algebra/property_shape.rb', line 16

def conforms(node, depth: 0, **options)
  return [] if deactivated?
  options = id ? options.merge(shape: id) : options
  options = options.merge(severity: RDF::Vocab::SHACL.Violation)

  # Add some instance options to the argument
  options = i{
    flags
    qualifiedMinCount
    qualifiedMaxCount
    qualifiedValueShapesDisjoint
    severity
  }.inject(options) do |memo, sym|
    @options[sym] ? memo.merge(sym => @options[sym]) : memo
  end

  path = @options[:path]
  log_debug(NAME, depth: depth) {SXP::Generator.string({id: id, node: node, path: path}.to_sxp_bin)}
  log_error(NAME, "no path", depth: depth) unless path

  # Turn the `path` attribute into a SPARQL Property Path and evaluate to find related nodes.
  value_nodes = if path.is_a?(RDF::URI)
    graph.query({subject: node, predicate: path}).objects
  elsif path.evaluatable?
    path.execute(graph,
      subject: node,
      object: RDF::Query::Variable.new(:object)).map do
        |soln| soln[:object]
    end.compact.uniq
  else
    log_error(NAME, "Can't handle path", depth: depth) {path.to_sxp}
    []
  end

  # Evaluate against builtins
  builtin_results = @options.map do |k, v|
    self.send("builtin_#{k}".to_sym, v, node, path, value_nodes, depth: depth + 1, **options) if self.respond_to?("builtin_#{k}".to_sym)
  end.flatten.compact

  # Evaluate against operands
  op_results = operands.map do |op|
    if op.is_a?(QualifiedValueShape)
      # All value nodes are passed
      op.conforms(node, value_nodes: value_nodes, path: path, depth: depth + 1, **options)
    else
      value_nodes.map do |n|
       res = op.conforms(n, path: path, depth: depth + 1, **options)
       if op.is_a?(NodeShape) && !res.all?(&:conform?)
         # Special case for embedded NodeShape
         not_satisfied(focus: node, path: path,
           value: n,
           message: "node does not conform to #{op.id}",
           resultSeverity: options.fetch(:severity),
           component: RDF::Vocab::SHACL.NodeConstraintComponent,
           **options)
       else
         res
       end
     end
    end
  end.flatten.compact

  builtin_results + op_results
end

#pathRDF::URI,

The path defined on this property shape

Returns:

  • (RDF::URI, )


83
84
85
# File 'lib/shacl/algebra/property_shape.rb', line 83

def path
  @options[:path]
end