class RDF::Literal
An RDF literal.
Subclasses of {RDF::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 {RDF::Value} instances call {RDF::Value#type_error}, which, returns false. Implementations wishing to have {RDF::TypeError} raised should mix-in {RDF::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 literals. Internally, plain literals are given the `xsd:string` datatype and language tagged literals 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 literals. 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 literals.
-
{RDF::Literal::Boolean}
-
{RDF::Literal::Date}
-
{RDF::Literal::DateTime}
-
{RDF::Literal::Decimal}
-
{RDF::Literal::Double}
-
{RDF::Literal::Integer}
-
{RDF::Literal::Time}
@example Creating a plain literal
value = RDF::Literal.new("Hello, world!") value.plain? #=> true`
@example Creating a language-tagged literal (1)
value = RDF::Literal.new("Hello!", language: :en) value.has_language? #=> true value.language #=> :en
@example Creating a language-tagged literal (2)
RDF::Literal.new("Wazup?", language: :"en-US") RDF::Literal.new("Hej!", language: :sv) RDF::Literal.new("¡Hola!", language: :es)
@example Creating an explicitly datatyped literal
value = RDF::Literal.new("2009-12-31", datatype: RDF::XSD.date) value.has_datatype? #=> true value.datatype #=> RDF::XSD.date
@example Creating an implicitly datatyped literal
value = RDF::Literal.new(Date.today) value.has_datatype? #=> true value.datatype #=> RDF::XSD.date
@example Creating implicitly datatyped literals
RDF::Literal.new(false).datatype #=> XSD.boolean RDF::Literal.new(true).datatype #=> XSD.boolean RDF::Literal.new(123).datatype #=> XSD.integer RDF::Literal.new(9223372036854775807).datatype #=> XSD.integer RDF::Literal.new(3.1415).datatype #=> XSD.double RDF::Literal.new(Time.now).datatype #=> XSD.time RDF::Literal.new(Date.new(2010)).datatype #=> XSD.date RDF::Literal.new(DateTime.new(2010)).datatype #=> XSD.dateTime
@see www.w3.org/TR/rdf11-concepts/#section-Graph-Literal @see www.w3.org/TR/rdf11-concepts/#section-Datatypes
Constants
- FALSE
- TRUE
- ZERO
Attributes
@return [URI] The XML Schema datatype URI (optional).
@return [Symbol] The language tag (optional).
Public Class Methods
@private Return Hash mapping from datatype URI to class
# File lib/rdf/model/literal.rb, line 89 def self.datatype_map @@datatype_map ||= Hash[ @@subclasses .select {|klass| klass.const_defined?(:DATATYPE)} .map {|klass| [klass.const_get(:DATATYPE).to_s, klass]} ] end
@private Return datatype class for uri, or nil if none is found
# File lib/rdf/model/literal.rb, line 100 def self.datatyped_class(uri) datatype_map[uri] end
Literals without a datatype are given either xsd:string or rdf:langString depending on if there is language
@param [Object] value @option options [Symbol] :language (nil)
Language is downcased to ensure proper matching
@option options [String] :lexical (nil)
Supplied lexical representation of this literal, otherwise it comes from transforming `value` to a string form See {#to_s}.
@option options [URI] :datatype (nil) @option options [Boolean] :validate (false) @option options [Boolean] :canonicalize (false) @raise [ArgumentError]
if there is a language and datatype is no rdf:langString or datatype is rdf:langString and there is no language
@see www.w3.org/TR/rdf11-concepts/#section-Graph-Literal @see www.w3.org/TR/rdf11-concepts/#section-Datatypes
# File lib/rdf/model/literal.rb, line 163 def initialize(value, options = {}) @object = value.freeze @string = options[:lexical] if options[:lexical] @string = value if !defined?(@string) && value.is_a?(String) @string = @string.encode(Encoding::UTF_8).freeze if @string @object = @string if @string && @object.is_a?(String) @language = options[:language].to_s.downcase.to_sym if options[:language] @datatype = RDF::URI(options[:datatype]).freeze if options[:datatype] @datatype ||= self.class.const_get(:DATATYPE) if self.class.const_defined?(:DATATYPE) @datatype ||= @language ? RDF.langString : RDF::XSD.string raise ArgumentError, "datatype of rdf:langString requires a language" if !@language && @datatype == RDF::langString end
@private
# File lib/rdf/model/literal.rb, line 106 def self.new(value, options = {}) raise ArgumentError, "datatype with language must be rdf:langString" if options[:language] && (options[:datatype] || RDF.langString).to_s != RDF.langString.to_s klass = case when !self.equal?(RDF::Literal) self # subclasses can be directly constructed without type dispatch when typed_literal = datatyped_class(options[: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 ::DateTime then RDF::Literal::DateTime when ::Date then RDF::Literal::Date when ::Time then RDF::Literal::Time # FIXME: Ruby's Time class can represent datetimes as well when ::Symbol then RDF::Literal::Token else self end end literal = klass.allocate literal.send(:initialize, value, options) literal.validate! if options[:validate] literal.canonicalize! if options[:canonicalize] literal end
Private Class Methods
@private @return [void]
# File lib/rdf/model/literal.rb, line 66 def self.inherited(child) @@subclasses << child @@datatype_map = nil super end
Public Instance Methods
Returns `true` if this literal is equivalent to `other` (with type check).
@example
RDF::Literal(1) == RDF::Literal(1.0) #=> true
@param [Object] other @return [Boolean] `true` or `false`
@see www.w3.org/TR/rdf-sparql-query/#func-RDFterm-equal @see www.w3.org/TR/rdf-concepts/#section-Literal-Equality
# File lib/rdf/model/literal.rb, line 285 def ==(other) case other when Literal case when self.eql?(other) true when self.has_language? && self.language.to_s == other.language.to_s # 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) # Comoparing 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
Converts this literal into its canonical lexical representation.
Subclasses should override this as needed and appropriate.
@return [RDF::Literal] `self` @since 0.3.0
# File lib/rdf/model/literal.rb, line 406 def canonicalize! self end
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
-
The first argument is a plain literal with language tag and the second argument is a simple literal or literal typed as xsd:string
@example
compatible?("abc" "b") #=> true compatible?("abc" "b"^^xsd:string) #=> true compatible?("abc"^^xsd:string "b") #=> true compatible?("abc"^^xsd:string "b"^^xsd:string) #=> true compatible?("abc"@en "b") #=> true compatible?("abc"@en "b"^^xsd:string) #=> true compatible?("abc"@en "b"@en) #=> true compatible?("abc"@fr "b"@ja) #=> false compatible?("abc" "b"@ja) #=> false compatible?("abc" "b"@en) #=> false compatible?("abc"^^xsd:string "b"@en) #=> false
@see www.w3.org/TR/sparql11-query/#func-arg-compatibility @since 2.0
# File lib/rdf/model/literal.rb, line 221 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 has_language? ? (language == other.language || other.datatype == RDF::XSD.string) : other.datatype == RDF::XSD.string end
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 @return [Boolean]
# File lib/rdf/model/literal.rb, line 385 def comperable_datatype?(other) return false unless self.plain? || self.has_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.has_language? else # An unknown datatype may not be used for comparison, unless it has a language? (open-eq-8) self.has_language? end end
Determins if `self` is the same term as `other`.
@example
RDF::Literal(1).eql?(RDF::Literal(1.0)) #=> false
@param [Object] other @return [Boolean] `true` or `false`
# File lib/rdf/model/literal.rb, line 265 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.datatype.eql?(other.datatype)) end
Escape a literal using ECHAR escapes.
ECHAR ::= '\' [tbnrf"'\]
@note N-Triples only requires '"nr' to be escaped.
@param [String] string @return [String] @see {RDF::Term#escape}
# File lib/rdf/model/literal.rb, line 420 def escape(string) string.gsub('\', '\\'). gsub("\t", '\t'). gsub("\b", '\b'). gsub("\n", '\n'). gsub("\r", '\r'). gsub("\f", '\f'). gsub('"', '\"'). freeze end
@private
# File lib/rdf/model/literal.rb, line 251 def freeze hash.freeze value_hash.freeze super end
Returns `true` if this is a datatyped literal.
For historical reasons, this excludes xsd:string and rdf:langString
@return [Boolean] `true` or `false` @see www.w3.org/TR/rdf-concepts/#dfn-typed-literal
# File lib/rdf/model/literal.rb, line 349 def has_datatype? !plain? && !language? end
Returns `true` if this is a language-tagged literal.
@return [Boolean] `true` or `false` @see www.w3.org/TR/rdf-concepts/#dfn-plain-literal
# File lib/rdf/model/literal.rb, line 337 def has_language? datatype == RDF.langString end
Returns a hash code for this literal.
@return [Fixnum]
# File lib/rdf/model/literal.rb, line 236 def hash @hash ||= [to_s, datatype, language].hash end
Returns a human-readable value for the literal
@return [String] @since 1.1.6
# File lib/rdf/model/literal.rb, line 444 def humanize(lang = :en) to_s.freeze end
Returns a developer-friendly representation of `self`.
@return [String]
# File lib/rdf/model/literal.rb, line 452 def inspect sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, RDF::NTriples.serialize(self)) end
Returns `true`.
@return [Boolean] `true` or `false`
# File lib/rdf/model/literal.rb, line 194 def literal? true end
@return [Object]
# File lib/rdf/model/literal.rb, line 186 def object defined?(@object) ? @object : value end
Returns `true` if this is a plain literal. A plain literal may have a language, but may not have a datatype. For all practical purposes, this includes xsd:string literals too.
@return [Boolean] `true` or `false` @see www.w3.org/TR/rdf-concepts/#dfn-plain-literal
# File lib/rdf/model/literal.rb, line 318 def plain? [RDF.langString, RDF::XSD.string].include?(datatype) end
Returns `true` if this is a simple literal. A simple literal has no datatype or language.
@return [Boolean] `true` or `false` @see www.w3.org/TR/sparql11-query/#simple_literal
# File lib/rdf/model/literal.rb, line 328 def simple? datatype == RDF::XSD.string end
Returns the value as a string.
@return [String]
# File lib/rdf/model/literal.rb, line 435 def to_s @object.to_s.freeze end
Returns `true` if the value adheres to the defined grammar of the datatype.
@return [Boolean] `true` or `false` @since 0.2.1
# File lib/rdf/model/literal.rb, line 362 def valid? grammar = self.class.const_get(:GRAMMAR) rescue nil grammar.nil? || !!(value =~ grammar) end
Validates the value using {RDF::Value#valid?}, raising an error if the value is invalid.
@return [RDF::Literal] `self` @raise [ArgumentError] if the value is invalid @since 0.2.1
# File lib/rdf/model/literal.rb, line 374 def validate! raise ArgumentError, "#{to_s.inspect} is not a valid <#{datatype.to_s}> literal" if invalid? self end
Returns the value as a string.
@return [String]
# File lib/rdf/model/literal.rb, line 180 def value @string || to_s end
Returns a hash code for the value.
@return [Fixnum]
# File lib/rdf/model/literal.rb, line 245 def value_hash @value_hash ||= value.hash end