Class: RDF::Literal
- Inherits:
-
Object
- Object
- RDF::Literal
- Includes:
- Term
- Defined in:
- lib/rdf/model/literal.rb,
lib/rdf/model/literal/date.rb,
lib/rdf/model/literal/time.rb,
lib/rdf/model/literal/token.rb,
lib/rdf/model/literal/double.rb,
lib/rdf/model/literal/boolean.rb,
lib/rdf/model/literal/decimal.rb,
lib/rdf/model/literal/integer.rb,
lib/rdf/model/literal/numeric.rb,
lib/rdf/model/literal/datetime.rb,
lib/rdf/model/literal/temporal.rb
Overview
An RDF literal.
Subclasses of Literal should define DATATYPE and GRAMMAR constants, which are used for identifying the appropriate class to use for a datatype URI and to perform lexical matching on the value.
Literal comparison with other Value instances call Value#type_error, which, returns false. Implementations wishing to have TypeError raised should mix-in TypeCheck. This is required for strict SPARQL conformance.
Specific typed literals may have behavior different from the default implementation. See the following defined sub-classes for specific documentation. Additional sub-classes may be defined, and will interoperate by defining ‘DATATYPE` and `GRAMMAR` constants, in addition other required overrides of RDF::Literal behavior.
In RDF 1.1, all literals are typed, including plain literals and language-tagged strings. Internally, plain literals are given the ‘xsd:string` datatype and language-tagged strings are given the `rdf:langString` datatype. Creating a plain literal, without a datatype or language, will automatically provide the `xsd:string` datatype; similar for language-tagged strings. Note that most serialization formats will remove this datatype. Code which depends on a literal having the `xsd:string` datatype being different from a plain literal (formally, without a datatype) may break. However note that the `#has_datatype?` will continue to return `false` for plain or language-tagged strings.
RDF 1.2 adds **directional language-tagged strings** which are effectively a subclass of **language-tagged strings** contining an additional direction component with value either ltr or rtl for Left-to-Right or Right-to-Left. This determines the general direction of a string when presented in n a user agent, where it might be in conflict with the inherent direction of the leading Unicode code points. Directional language-tagged strings are given the ‘rdf:langString` datatype.
Defined Under Namespace
Classes: Boolean, Date, DateTime, Decimal, Double, Integer, Numeric, Temporal, Time, Token
Constant Summary collapse
- TRUE =
RDF::Literal.new(true)
- FALSE =
RDF::Literal.new(false)
- ZERO =
RDF::Literal.new(0)
- XSD_STRING =
RDF::URI("http://www.w3.org/2001/XMLSchema#string")
Instance Attribute Summary collapse
-
#datatype ⇒ URI
The XML Schema datatype URI (optional).
-
#direction ⇒ Symbol
The base direction (optional).
-
#language ⇒ Symbol
The language-tag (optional).
Class Method Summary collapse
-
.datatype_map ⇒ Object
Return Hash mapping from datatype URI to class.
-
.datatyped_class(uri) ⇒ Object
Return datatype class for uri, or nil if none is found.
- .new(value, language: nil, datatype: nil, direction: nil, lexical: nil, validate: false, canonicalize: false, **options) ⇒ Object
Instance Method Summary collapse
-
#<=>(other) ⇒ Integer
Compares ‘self` to `other` for sorting purposes (with type check).
-
#==(other) ⇒ Boolean
(also: #===)
Returns ‘true` if this literal is equivalent to `other` (with type check).
-
#canonicalize! ⇒ RDF::Literal
Converts this literal into its canonical lexical representation.
-
#compatible?(other) ⇒ Boolean
Term compatibility according to SPARQL.
-
#comperable_datatype2?(other) ⇒ Boolean
Returns ‘true` if the literals are comperable.
-
#comperable_datatype?(other) ⇒ Boolean
Returns ‘true` if the literal has a datatype and the comparison should return false instead of raise a type error.
-
#datatype? ⇒ Boolean
(also: #has_datatype?, #typed?, #datatyped?)
Returns ‘true` if this is a datatyped literal.
-
#direction? ⇒ Boolean
Returns ‘true` if this is a directional language-tagged string.
-
#english? ⇒ Boolean
Returns ‘true` if this is a language-tagged literal in the English language.
-
#eql?(other) ⇒ Boolean
Determins if ‘self` is the same term as `other`.
-
#escape(string) ⇒ String
Escape a literal using ECHAR escapes.
- #freeze ⇒ Object
-
#hash ⇒ Integer
Returns a hash code for this literal.
-
#humanize(lang = :en) ⇒ String
Returns a human-readable value for the literal.
-
#initialize(value, language: nil, datatype: nil, direction: nil, lexical: nil, validate: false, canonicalize: false, **options) ⇒ Literal
constructor
Literals without a datatype are given either ‘xsd:string`, `rdf:langString`, or `rdf:dirLangString`, depending on if there is `language` and/or `direction`.
-
#inspect ⇒ String
Returns a developer-friendly representation of ‘self`.
-
#language? ⇒ Boolean
(also: #has_language?)
Returns ‘true` if this is a language-tagged string.
-
#literal? ⇒ Boolean
Returns ‘true`.
- #object ⇒ Object
-
#plain? ⇒ Boolean
Returns ‘true` if this is a plain literal.
-
#simple? ⇒ Boolean
Returns ‘true` if this is a simple literal.
-
#squish(*other_string) ⇒ RDF::Literal
Returns the literal, first removing all whitespace on both ends of the value, and then changing remaining consecutive whitespace groups into one space each.
-
#squish! ⇒ Object
Performs a destructive #squish.
-
#to_s ⇒ String
Returns the value as a string.
-
#valid? ⇒ Boolean
Returns ‘true` if the value adheres to the defined grammar of the datatype.
-
#validate! ⇒ RDF::Literal
Validates the value using Value#valid?, raising an error if the value is invalid.
-
#value ⇒ String
Returns the value as a string.
-
#value_hash ⇒ Integer
Returns a hash code for the value.
Methods included from Term
#term?, #terms, #to_base, #to_term
Methods included from Value
#anonymous?, #canonicalize, #constant?, #graph?, #inspect!, #invalid?, #iri?, #list?, #node?, #resource?, #start_with?, #statement?, #term?, #to_nquads, #to_ntriples, #to_rdf, #to_term, #type_error, #uri?, #variable?
Constructor Details
#initialize(value, language: nil, datatype: nil, direction: nil, lexical: nil, validate: false, canonicalize: false, **options) ⇒ Literal
Literals without a datatype are given either ‘xsd:string`, `rdf:langString`, or `rdf:dirLangString`, depending on if there is `language` and/or `direction`.
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/rdf/model/literal.rb', line 190 def initialize(value, language: nil, datatype: nil, direction: nil, lexical: nil, validate: false, canonicalize: false, **) @object = value.freeze @string = lexical if lexical @string = value if !defined?(@string) && value.is_a?(String) @string = @string.encode(Encoding::UTF_8).freeze if instance_variable_defined?(:@string) @object = @string if instance_variable_defined?(:@string) && @object.is_a?(String) @language = language.to_s.downcase.to_sym if language @direction = direction.to_s.to_sym if direction @datatype = RDF::URI(datatype).freeze if datatype @datatype ||= self.class.const_get(:DATATYPE) if self.class.const_defined?(:DATATYPE) @datatype ||= if instance_variable_defined?(:@language) && @language && instance_variable_defined?(:@direction) && @direction RDF.dirLangString elsif instance_variable_defined?(:@language) && @language RDF.langString else XSD_STRING end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
# ⇒ String (protected)
603 604 605 606 607 608 609 |
# File 'lib/rdf/model/literal.rb', line 603 def method_missing(name, *args) case name when :to_str return to_s if [RDF.langString, RDF.dirLangString, XSD_STRING].include?(@datatype) end super end |
Instance Attribute Details
#datatype ⇒ URI
Returns The XML Schema datatype URI (optional).
167 168 169 |
# File 'lib/rdf/model/literal.rb', line 167 def datatype @datatype end |
#direction ⇒ Symbol
Returns The base direction (optional). Implies ‘datatype` is `rdf:dirLangString`.
164 165 166 |
# File 'lib/rdf/model/literal.rb', line 164 def direction @direction end |
#language ⇒ Symbol
Returns The language-tag (optional). Implies ‘datatype` is `rdf:langString`.
161 162 163 |
# File 'lib/rdf/model/literal.rb', line 161 def language @language end |
Class Method Details
.datatype_map ⇒ Object
Return Hash mapping from datatype URI to class
103 104 105 106 107 108 109 |
# File 'lib/rdf/model/literal.rb', line 103 def self.datatype_map @@datatype_map ||= Hash[ @@subclasses .select {|klass| klass.const_defined?(:DATATYPE)} .map {|klass| [klass.const_get(:DATATYPE).to_s, klass]} ] end |
.datatyped_class(uri) ⇒ Object
Return datatype class for uri, or nil if none is found
114 115 116 |
# File 'lib/rdf/model/literal.rb', line 114 def self.datatyped_class(uri) datatype_map[uri] end |
.new(value, language: nil, datatype: nil, direction: nil, lexical: nil, validate: false, canonicalize: false, **options) ⇒ Object
120 121 122 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 |
# File 'lib/rdf/model/literal.rb', line 120 def self.new(value, language: nil, datatype: nil, direction: nil, lexical: nil, validate: false, canonicalize: false, **) if language && direction raise ArgumentError, "datatype with language and direction must be rdf:dirLangString" if (datatype || RDF.dirLangString).to_s != RDF.dirLangString.to_s elsif language raise ArgumentError, "datatype with language must be rdf:langString" if (datatype || RDF.langString).to_s != RDF.langString.to_s else raise ArgumentError, "datatype not compatible with language or direction" if language || direction end klass = case when !self.equal?(RDF::Literal) self # subclasses can be directly constructed without type dispatch when typed_literal = datatyped_class(datatype.to_s) typed_literal else case value when ::TrueClass then RDF::Literal::Boolean when ::FalseClass then RDF::Literal::Boolean when ::Integer then RDF::Literal::Integer when ::Float then RDF::Literal::Double when ::BigDecimal then RDF::Literal::Decimal when ::Rational then RDF::Literal::Double when ::DateTime then RDF::Literal::DateTime when ::Time then RDF::Literal::DateTime when ::Date then RDF::Literal::Date when ::Symbol then RDF::Literal::Token else self end end literal = klass.allocate literal.send(:initialize, value, language: language, datatype: datatype, direction: direction, **) literal.validate! if validate literal.canonicalize! if canonicalize literal end |
Instance Method Details
#<=>(other) ⇒ Integer
Compares ‘self` to `other` for sorting purposes (with type check).
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
# File 'lib/rdf/model/literal.rb', line 353 def <=>(other) case other when Literal case when self.eql?(other) 0 when self.language? && other.language? # Literals with languages can compare if languages are identical self.to_s <=> other.to_s when self.simple? && other.simple? self.to_s <=> other.to_s when !self.valid? type_error("#{self.inspect} is invalid") || 0 when !other.valid? type_error("#{other.inspect} is invalid") || 0 when self.comperable_datatype2?(other) self.object <=> other.object else type_error("#{self.inspect} and #{other.inspect} are not comperable") || 0 end when String self.simple? && self.value <=> other else 1 end end |
#==(other) ⇒ Boolean Also known as: ===
Returns ‘true` if this literal is equivalent to `other` (with type check).
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
# File 'lib/rdf/model/literal.rb', line 320 def ==(other) case other when Literal case when self.eql?(other) true when self.direction? && self.direction == other.direction # Literals with directions can compare if languages and directions are identical self.value_hash == other.value_hash && self.value == other.value when self.language? && self.language == other.language # Literals with languages can compare if languages are identical self.value_hash == other.value_hash && self.value == other.value when self.simple? && other.simple? self.value_hash == other.value_hash && self.value == other.value when other.comperable_datatype?(self) || self.comperable_datatype?(other) # Comparing plain with undefined datatypes does not generate an error, but returns false # From data-r2/expr-equal/eq-2-2. false else type_error("unable to determine whether #{self.inspect} and #{other.inspect} are equivalent") end when String self.simple? && self.value.eql?(other) else false end end |
#canonicalize! ⇒ RDF::Literal
Converts this literal into its canonical lexical representation.
Subclasses should override this as needed and appropriate.
523 524 525 |
# File 'lib/rdf/model/literal.rb', line 523 def canonicalize! self end |
#compatible?(other) ⇒ Boolean
Term compatibility according to SPARQL
Compatibility of two arguments is defined as:
-
The arguments are simple literals or literals typed as xsd:string
-
The arguments are plain literals with identical language-tags and directions
-
The first argument is a plain literal with language-tag and the second argument is a simple literal or literal typed as xsd:string
255 256 257 258 259 260 261 262 263 264 |
# File 'lib/rdf/model/literal.rb', line 255 def compatible?(other) return false unless other.literal? && plain? && other.plain? # * The arguments are simple literals or literals typed as xsd:string # * The arguments are plain literals with identical language-tags # * The first argument is a plain literal with language-tag and the second argument is a simple literal or literal typed as xsd:string language? || direction? ? (language == other.language && direction == other.direction || other.datatype == XSD_STRING) : other.datatype == XSD_STRING end |
#comperable_datatype2?(other) ⇒ Boolean
Returns ‘true` if the literals are comperable.
Used for <=> operator.
502 503 504 505 506 507 508 509 510 511 512 513 514 |
# File 'lib/rdf/model/literal.rb', line 502 def comperable_datatype2?(other) case self when RDF::Literal::Numeric, RDF::Literal::Boolean case other when RDF::Literal::Numeric, RDF::Literal::Boolean true else false end else self.datatype == other.datatype end end |
#comperable_datatype?(other) ⇒ Boolean
Returns ‘true` if the literal has a datatype and the comparison should return false instead of raise a type error.
This behavior is intuited from SPARQL data-r2/expr-equal/eq-2-2
482 483 484 485 486 487 488 489 490 491 492 493 494 |
# File 'lib/rdf/model/literal.rb', line 482 def comperable_datatype?(other) return false unless self.plain? || self.language? case other when RDF::Literal::Numeric, RDF::Literal::Boolean, RDF::Literal::Date, RDF::Literal::Time, RDF::Literal::DateTime # Invald types can be compared without raising a TypeError if literal has a language (open-eq-08) !other.valid? && self.language? else # An unknown datatype may not be used for comparison, unless it has a language? (open-eq-8) self.language? end end |
#datatype? ⇒ Boolean Also known as: has_datatype?, typed?, datatyped?
Returns ‘true` if this is a datatyped literal.
For historical reasons, this excludes xsd:string and rdf:langString
431 432 433 |
# File 'lib/rdf/model/literal.rb', line 431 def datatype? !plain? && !language? && !direction? end |
#direction? ⇒ Boolean
Returns ‘true` if this is a directional language-tagged string.
420 421 422 |
# File 'lib/rdf/model/literal.rb', line 420 def direction? datatype == RDF.dirLangString end |
#english? ⇒ Boolean
Returns ‘true` if this is a language-tagged literal in the English language.
460 461 462 |
# File 'lib/rdf/model/literal.rb', line 460 def english? /\Aen(?:-[A-Za-z]{2})?\z/ === language.to_s end |
#eql?(other) ⇒ Boolean
Determins if ‘self` is the same term as `other`.
299 300 301 302 303 304 305 306 307 |
# File 'lib/rdf/model/literal.rb', line 299 def eql?(other) self.equal?(other) || (self.class.eql?(other.class) && self.value_hash == other.value_hash && self.value.eql?(other.value) && self.language.to_s.eql?(other.language.to_s) && self.direction.to_s.eql?(other.direction.to_s) && self.datatype.eql?(other.datatype)) end |
#escape(string) ⇒ String
N-Triples only requires ‘"nr’ to be escaped.
Escape a literal using ECHAR escapes.
ECHAR ::= '\' [tbnrf"'\]
561 562 563 564 565 566 567 568 569 570 |
# File 'lib/rdf/model/literal.rb', line 561 def escape(string) string.gsub('\\', '\\\\'). gsub("\t", '\\t'). gsub("\b", '\\b'). gsub("\n", '\\n'). gsub("\r", '\\r'). gsub("\f", '\\f'). gsub('"', '\\"'). freeze end |
#freeze ⇒ Object
285 286 287 288 289 |
# File 'lib/rdf/model/literal.rb', line 285 def freeze hash.freeze value_hash.freeze super end |
#hash ⇒ Integer
Returns a hash code for this literal.
270 271 272 |
# File 'lib/rdf/model/literal.rb', line 270 def hash @hash ||= [to_s, datatype, language, direction].compact.hash end |
#humanize(lang = :en) ⇒ String
Returns a human-readable value for the literal
585 586 587 |
# File 'lib/rdf/model/literal.rb', line 585 def humanize(lang = :en) to_s.freeze end |
#inspect ⇒ String
Returns a developer-friendly representation of ‘self`.
593 594 595 |
# File 'lib/rdf/model/literal.rb', line 593 def inspect sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, RDF::NTriples.serialize(self)) end |
#language? ⇒ Boolean Also known as: has_language?
Returns ‘true` if this is a language-tagged string.
410 411 412 |
# File 'lib/rdf/model/literal.rb', line 410 def language? [RDF.langString, RDF.dirLangString].include?(datatype) end |
#literal? ⇒ Boolean
Returns ‘true`.
228 229 230 |
# File 'lib/rdf/model/literal.rb', line 228 def literal? true end |
#object ⇒ Object
220 221 222 |
# File 'lib/rdf/model/literal.rb', line 220 def object defined?(@object) ? @object : value end |
#plain? ⇒ Boolean
Returns ‘true` if this is a plain literal. A plain literal may have a language and direction, but may not have a datatype. For all practical purposes, this includes xsd:string literals too.
387 388 389 390 391 392 393 |
# File 'lib/rdf/model/literal.rb', line 387 def plain? [ RDF.langString, RDF.dirLangString, XSD_STRING ].include?(datatype) end |
#simple? ⇒ Boolean
Returns ‘true` if this is a simple literal. A simple literal has no datatype or language.
401 402 403 |
# File 'lib/rdf/model/literal.rb', line 401 def simple? datatype == XSD_STRING end |
#squish(*other_string) ⇒ RDF::Literal
Returns the literal, first removing all whitespace on both ends of the value, and then changing remaining consecutive whitespace groups into one space each.
Note that it handles both ASCII and Unicode whitespace.
534 535 536 |
# File 'lib/rdf/model/literal.rb', line 534 def squish(*other_string) self.dup.squish! end |
#squish! ⇒ Object
Performs a destructive #squish.
543 544 545 546 547 548 549 |
# File 'lib/rdf/model/literal.rb', line 543 def squish! @string = value. gsub(/\A[[:space:]]+/, ''). gsub(/[[:space:]]+\z/, ''). gsub(/[[:space:]]+/, ' ') self end |
#to_s ⇒ String
Returns the value as a string.
576 577 578 |
# File 'lib/rdf/model/literal.rb', line 576 def to_s @object.to_s.freeze end |
#valid? ⇒ Boolean
Returns ‘true` if the value adheres to the defined grammar of the datatype.
444 445 446 447 448 449 450 451 452 |
# File 'lib/rdf/model/literal.rb', line 444 def valid? BCP47.parse(language.to_s) if language? return false if direction? && !%i{ltr rtl}.include?(direction) return false if datatype? && datatype.invalid? grammar = self.class.const_get(:GRAMMAR) rescue nil grammar.nil? || value.match?(grammar) rescue BCP47::InvalidLanguageTag false end |
#validate! ⇒ RDF::Literal
Validates the value using Value#valid?, raising an error if the value is invalid.
471 472 473 474 |
# File 'lib/rdf/model/literal.rb', line 471 def validate! raise ArgumentError, "#{to_s.inspect} is not a valid <#{datatype.to_s}> literal" if invalid? self end |
#value ⇒ String
Returns the value as a string.
214 215 216 |
# File 'lib/rdf/model/literal.rb', line 214 def value instance_variable_defined?(:@string) && @string || to_s end |
#value_hash ⇒ Integer
Returns a hash code for the value.
279 280 281 |
# File 'lib/rdf/model/literal.rb', line 279 def value_hash @value_hash ||= value.hash end |