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

Instance Attribute Summary

Attributes inherited from Operator

#graph, #options, #shapes_graph

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, #compare, #targetNodes

Methods inherited from Operator

add_component, apply_op, #comment, component_params, #deactivated?, from_expanded_value, from_json, #id, iri, #iri, #label, #not_satisfied, params, 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:



92
93
94
95
96
97
# File 'lib/shacl/algebra/property_shape.rb', line 92

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



106
107
108
109
110
111
# File 'lib/shacl/algebra/property_shape.rb', line 106

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



124
125
126
127
128
129
130
131
# File 'lib/shacl/algebra/property_shape.rb', line 124

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



147
148
149
150
151
152
153
154
# File 'lib/shacl/algebra/property_shape.rb', line 147

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



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/shacl/algebra/property_shape.rb', line 163

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

    focus node

  • 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
# 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[:severity] = @options[:severity] if @options[:severity]
  options[:severity] ||= RDF::Vocab::SHACL.Violation

  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?(QualifiedValueConstraintComponent) || op.is_a?(SPARQLConstraintComponent)
      # All value nodes are passed
      op.conforms(node, path: path, value_nodes: value_nodes, 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, SPARQL::Algebra::Expression

The path defined on this property shape

Returns:

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


73
74
75
# File 'lib/shacl/algebra/property_shape.rb', line 73

def path
  @options[:path]
end