Class: SHACL::Algebra::Operator Abstract
- Inherits:
-
SPARQL::Algebra::Operator
- Object
- SPARQL::Algebra::Operator
- SHACL::Algebra::Operator
- Extended by:
- JSON::LD::Utils
- Includes:
- RDF::Util::Logger
- Defined in:
- lib/shacl/algebra/operator.rb
Overview
The SHACL operator.
Constant Summary collapse
- ALL_KEYS =
All keys associated with shapes which are set in options
%i( id type label name comment description deactivated severity order group defaultValue path targetNode targetClass targetSubjectsOf targetObjectsOf class datatype nodeKind minCount maxCount minExclusive minInclusive maxExclusive maxInclusive minLength maxLength pattern flags languageIn uniqueLang qualifiedValueShapesDisjoint qualifiedMinCount qualifiedMaxCount equals disjoint lessThan lessThanOrEquals closed ignoredProperties hasValue in ).freeze
Instance Attribute Summary collapse
-
#graph ⇒ Object
Graph against which shapes are validaed.
-
#options ⇒ Object
Initialization options.
Class Method Summary collapse
-
.apply_op(op, values) ⇒ Object
Recursively apply operand to sucessive values until the argument count which is expected is achieved.
-
.from_expanded_value(item, **options) ⇒ RDF::Term
Interpret a JSON-LD expanded value.
-
.from_json(operator, **options) ⇒ Operator
Creates an operator instance from a parsed SHACL representation.
-
.iri(value, base: RDF::Vocab::SHACL.to_uri, vocab: true, **options) ⇒ RDF::Value
Create URIs.
-
.parse_path(path, **options) ⇒ RDF::URI, SPARQL::Algebra::Expression
Parse the “patH” attribute into a SPARQL Property Path and evaluate to find related nodes.
-
.to_rdf(term, item, **options) ⇒ Object
Turn a JSON-LD value into its RDF representation.
Instance Method Summary collapse
-
#comment ⇒ RDF::Literal
Any comment associated with this operator.
-
#conforms(node, depth: 0, **options) ⇒ Array<ValidationResult>
Validates the specified ‘node` within `graph`, a list of ValidationResult.
-
#deactivated? ⇒ Boolean
Is this shape deactivated?.
-
#id ⇒ RDF::Resource
The ID of this operator.
-
#iri(value, base: RDF::Vocab::SHACL.to_uri, vocab: true, **options) ⇒ RDF::Value
Create URIs.
-
#label ⇒ RDF::Literal
Any label associated with this operator.
-
#not_satisfied(focus:, shape:, component:, resultSeverity: RDF::Vocab::SHACL.Violation, path: nil, value: nil, details: nil, message: nil, **options) ⇒ Array<SHACL::ValidationResult>
Create a result that does not satisfies the shape.
-
#satisfy(focus:, shape:, component:, resultSeverity: nil, path: nil, value: nil, details: nil, message: nil, **options) ⇒ Array<SHACL::ValidationResult>
Create a result that satisfies the shape.
- #to_sxp_bin ⇒ Object
-
#type ⇒ Array<RDF::URI>
The types associated with this operator.
Instance Attribute Details
#graph ⇒ Object
Graph against which shapes are validaed
36 37 38 |
# File 'lib/shacl/algebra/operator.rb', line 36 def graph @graph end |
#options ⇒ Object
Initialization options
33 34 35 |
# File 'lib/shacl/algebra/operator.rb', line 33 def @options end |
Class Method Details
.apply_op(op, values) ⇒ Object
Recursively apply operand to sucessive values until the argument count which is expected is achieved
235 236 237 238 239 240 |
# File 'lib/shacl/algebra/operator.rb', line 235 def apply_op(op, values) if values.length > op.arity values = values.first, apply_op(op, values[1..-1]) end op.new(*values) end |
.from_expanded_value(item, **options) ⇒ RDF::Term
Interpret a JSON-LD expanded value
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/shacl/algebra/operator.rb', line 160 def (item, **) if item['@value'] value, datatype = item.fetch('@value'), item.fetch('type', nil) case value when TrueClass, FalseClass value = value.to_s datatype ||= RDF::XSD.boolean.to_s when Numeric # Don't serialize as double if there are no fractional bits as_double = value.ceil != value || value >= 1e21 || datatype == RDF::XSD.double lit = if as_double RDF::Literal::Double.new(value, canonicalize: true) else RDF::Literal.new(value.numerator, canonicalize: true) end datatype ||= lit.datatype value = lit.to_s.sub("E+", "E") else datatype ||= item.has_key?('@language') ? RDF.langString : RDF::XSD.string end datatype = iri(datatype) if datatype language = item.fetch('@language', nil) if datatype == RDF.langString RDF::Literal.new(value, datatype: datatype, language: language) elsif item['id'] self.iri(item['id'], **) else RDF::Node.new end end |
.from_json(operator, **options) ⇒ Operator
Creates an operator instance from a parsed SHACL representation
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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/shacl/algebra/operator.rb', line 46 def from_json(operator, **) operands = [] node_opts = .dup operator.each do |k, v| next if v.nil? case k # List properties when 'and' elements = as_array(v).map {|vv| SHACL::Algebra.from_json(vv, **)} operands << And.new(*elements, **.dup) when 'class' then node_opts[:class] = as_array(v).map {|vv| iri(vv, **)} if v when 'datatype' then node_opts[:datatype] = iri(v, **) when 'disjoint' then node_opts[:disjoint] = as_array(v).map {|vv| iri(vv, **)} if v when 'equals' then node_opts[:equals] = iri(v, **) when 'id' then node_opts[:id] = iri(v, vocab: false, **) when 'ignoredProperties' then node_opts[:ignoredProperties] = as_array(v).map {|vv| iri(vv, **)} if v when 'lessThan' then node_opts[:lessThan] = iri(v, **) when 'lessThanOrEquals' then node_opts[:lessThanOrEquals] = iri(v, **) when 'node' operands.push(*as_array(v).map {|vv| NodeShape.from_json(vv, **)}) when 'nodeKind' then node_opts[:nodeKind] = iri(v, **) when 'not' elements = as_array(v).map {|vv| SHACL::Algebra.from_json(vv, **)} operands << Not.new(*elements, **.dup) when 'or' elements = as_array(v).map {|vv| SHACL::Algebra.from_json(vv, **)} operands << Or.new(*elements, **.dup) when 'path' then node_opts[:path] = parse_path(v, **) when 'property' operands.push(*as_array(v).map {|vv| PropertyShape.from_json(vv, **)}) when 'qualifiedValueShape' elements = as_array(v).map {|vv| SHACL::Algebra.from_json(vv, **)} operands << QualifiedValueShape.new(*elements, **.dup) when 'severity' then node_opts[:severity] = iri(v, **) when 'targetClass' then node_opts[:targetClass] = as_array(v).map {|vv| iri(vv, **)} if v when 'targetNode' node_opts[:targetNode] = as_array(v).map do |vv| (vv, **) end if v when 'targetObjectsOf' then node_opts[:targetObjectsOf] = as_array(v).map {|vv| iri(vv, **)} if v when 'targetSubjectsOf' then node_opts[:targetSubjectsOf] = as_array(v).map {|vv| iri(vv, **)} if v when 'type' then node_opts[:type] = as_array(v).map {|vv| iri(vv, **)} if v when 'xone' elements = as_array(v).map {|vv| SHACL::Algebra.from_json(vv, **)} operands << Xone.new(*elements, **.dup) else # Add as a plain option if it is recognized node_opts[k.to_sym] = to_rdf(k.to_sym, v, **) if ALL_KEYS.include?(k.to_sym) end end new(*operands, **node_opts) end |
.iri(value, base: RDF::Vocab::SHACL.to_uri, vocab: true, **options) ⇒ RDF::Value
Create URIs
104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/shacl/algebra/operator.rb', line 104 def iri(value, base: RDF::Vocab::SHACL.to_uri, vocab: true, **) # Context will have been pre-loaded @context ||= JSON::LD::Context.parse("http://github.com/ruby-rdf/shacl/") value = value['id'] || value['@id'] if value.is_a?(Hash) result = @context.(value, base: base, vocab: vocab) result = RDF::URI(result) if result.is_a?(String) if result.respond_to?(:qname) && result.qname result = RDF::URI.new(result.to_s) if result.frozen? result.lexical = result.qname.join(':') end result end |
.parse_path(path, **options) ⇒ RDF::URI, SPARQL::Algebra::Expression
Parse the “patH” attribute into a SPARQL Property Path and evaluate to find related nodes.
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/shacl/algebra/operator.rb', line 196 def parse_path(path, **) case path when RDF::URI then path when String then iri(path) when Hash # Creates a SPARQL S-Expression resulting in a query which can be used to find corresponding { alternativePath: :alt, inversePath: :reverse, oneOrMorePath: :"path+", "@list": :seq, zeroOrMorePath: :"path*", zeroOrOnePath: :"path?", }.each do |prop, op_sym| if path[prop.to_s] value = path[prop.to_s] value = value['@list'] if value.is_a?(Hash) && value.key?('@list') value = [value] if !value.is_a?(Array) value = value.map {|e| parse_path(e, **)} op = SPARQL::Algebra::Operator.for(op_sym) if value.length > op.arity # Divide into the first operand followed by the operator re-applied to the reamining operands value = value.first, apply_op(op, value[1..-1]) end return op.new(*value) end end if path['id'] iri(path['id']) else log_error('PropertyPath', "Can't handle path", **) {path.to_sxp} end else log_error('PropertyPath', "Can't handle path", **) {path.to_sxp} end end |
.to_rdf(term, item, **options) ⇒ Object
Turn a JSON-LD value into its RDF representation
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/shacl/algebra/operator.rb', line 123 def to_rdf(term, item, **) @context ||= JSON::LD::Context.parse("http://github.com/ruby-rdf/shacl/") return item.map {|v| to_rdf(term, v, **)} if item.is_a?(Array) case when item.is_a?(TrueClass) || item.is_a?(FalseClass) || item.is_a?(Numeric) return RDF::Literal(item) when value?(item) value, datatype = item.fetch('@value'), item.fetch('type', nil) case value when TrueClass, FalseClass, Numeric return RDF::Literal(value) else datatype ||= item.has_key?('@direction') ? RDF::URI("https://www.w3.org/ns/i18n##{item.fetch('@language', '').downcase}_#{item['@direction']}") : (item.has_key?('@language') ? RDF.langString : RDF::XSD.string) end datatype = iri(datatype) if datatype # Initialize literal as an RDF literal using value and datatype. If element has the key @language and datatype is xsd:string, then add the value associated with the @language key as the language of the object. language = item.fetch('@language', nil) if datatype == RDF.langString return RDF::Literal.new(value, datatype: datatype, language: language) when node?(item) return iri(item, **) when list?(item) RDF::List(*item['@list'].map {|v| to_rdf(term, v, **)}) when item.is_a?(String) RDF::Literal(item) else raise "Can't transform #{item.inspect} to RDF on property #{term}" end end |
Instance Method Details
#comment ⇒ RDF::Literal
Any comment associated with this operator
262 |
# File 'lib/shacl/algebra/operator.rb', line 262 def comment; @options[:comment]; end |
#conforms(node, depth: 0, **options) ⇒ Array<ValidationResult>
Validates the specified ‘node` within `graph`, a list of ValidationResult.
A node conforms if it is not deactivated and all of its operands conform.
281 282 283 |
# File 'lib/shacl/algebra/operator.rb', line 281 def conforms(node, depth: 0, **) raise NotImplemented end |
#deactivated? ⇒ Boolean
Is this shape deactivated?
258 |
# File 'lib/shacl/algebra/operator.rb', line 258 def deactivated?; @options[:deactivated] == RDF::Literal::TRUE; end |
#id ⇒ RDF::Resource
The ID of this operator
246 |
# File 'lib/shacl/algebra/operator.rb', line 246 def id; @options[:id]; end |
#iri(value, base: RDF::Vocab::SHACL.to_uri, vocab: true, **options) ⇒ RDF::Value
Create URIs
270 271 272 |
# File 'lib/shacl/algebra/operator.rb', line 270 def iri(value, base: RDF::Vocab::SHACL.to_uri, vocab: true, **) self.class.iri(value, base: base, vocab: vocab, **) end |
#label ⇒ RDF::Literal
Any label associated with this operator
254 |
# File 'lib/shacl/algebra/operator.rb', line 254 def label; @options[:label]; end |
#not_satisfied(focus:, shape:, component:, resultSeverity: RDF::Vocab::SHACL.Violation, path: nil, value: nil, details: nil, message: nil, **options) ⇒ Array<SHACL::ValidationResult>
Create a result that does not satisfies the shape.
323 324 325 326 327 |
# File 'lib/shacl/algebra/operator.rb', line 323 def not_satisfied(focus:, shape:, component:, resultSeverity: RDF::Vocab::SHACL.Violation, path: nil, value: nil, details: nil, message: nil, **) log_info(self.class.const_get(:NAME), "not satisfied #{value.to_sxp if value}#{': ' + if }", **) [SHACL::ValidationResult.new(focus, path, shape, resultSeverity, component, details, value, )] end |
#satisfy(focus:, shape:, component:, resultSeverity: nil, path: nil, value: nil, details: nil, message: nil, **options) ⇒ Array<SHACL::ValidationResult>
Create a result that satisfies the shape.
305 306 307 308 309 |
# File 'lib/shacl/algebra/operator.rb', line 305 def satisfy(focus:, shape:, component:, resultSeverity: nil, path: nil, value: nil, details: nil, message: nil, **) log_debug(self.class.const_get(:NAME), "#{'not ' if resultSeverity}satisfied #{value.to_sxp if value}#{': ' + if }", **) [SHACL::ValidationResult.new(focus, path, shape, resultSeverity, component, details, value, )] end |
#to_sxp_bin ⇒ Object
285 286 287 288 289 290 291 |
# File 'lib/shacl/algebra/operator.rb', line 285 def to_sxp_bin expressions = ALL_KEYS.inject([self.class.const_get(:NAME)]) do |memo, sym| @options[sym] ? memo.push([sym, *@options[sym]]) : memo end + operands expressions.to_sxp_bin end |
#type ⇒ Array<RDF::URI>
The types associated with this operator
250 |
# File 'lib/shacl/algebra/operator.rb', line 250 def type; @options[:type]; end |