module RDF ## # An RDF statement. # # @example Creating an RDF statement # s = RDF::URI.new("http://rubygems.org/gems/rdf") # p = RDF::DC.creator # o = RDF::URI.new("http://ar.to/#self") # RDF::Statement.new(s, p, o) # # @example Creating an RDF statement with a context # RDF::Statement.new(s, p, o, :context => uri) # # @example Creating an RDF statement from a `Hash` # RDF::Statement.new({ # :subject => RDF::URI.new("http://rubygems.org/gems/rdf"), # :predicate => RDF::DC.creator, # :object => RDF::URI.new("http://ar.to/#self"), # }) # class Statement include RDF::Value ## # @private # @since 0.2.2 def self.from(statement, options = {}) case statement when Array, Query::Pattern context = statement[3] == false ? nil : statement[3] self.new(statement[0], statement[1], statement[2], options.merge(:context => context)) when Statement then statement when Hash then self.new(options.merge(statement)) else raise ArgumentError, "expected RDF::Statement, Hash, or Array, but got #{statement.inspect}" end end # @return [Object] attr_accessor :id # @return [RDF::Resource] attr_accessor :context # @return [RDF::Resource] attr_accessor :subject # @return [RDF::URI] attr_accessor :predicate # @return [RDF::Term] attr_accessor :object ## # @overload initialize(options = {}) # @param [Hash{Symbol => Object}] options # @option options [RDF::Resource] :subject (nil) # @option options [RDF::URI] :predicate (nil) # @option options [RDF::Term] :object (nil) # @option options [RDF::Resource] :context (nil) # Note, in RDF 1.1, a context MUST be an IRI. # # @overload initialize(subject, predicate, object, options = {}) # @param [RDF::Resource] subject # @param [RDF::URI] predicate # @param [RDF::Term] object # @param [Hash{Symbol => Object}] options # @option options [RDF::Resource] :context (nil) def initialize(subject = nil, predicate = nil, object = nil, options = {}) case subject when Hash @options = subject.dup @subject = @options.delete(:subject) @predicate = @options.delete(:predicate) @object = @options.delete(:object) else @options = !options.empty? ? options.dup : {} @subject = subject @predicate = predicate @object = object end @id = @options.delete(:id) if @options.has_key?(:id) @context = @options.delete(:context) initialize! end ## # @private def initialize! @context = Node.intern(@context) if @context.is_a?(Symbol) @subject = Node.intern(@subject) if @subject.is_a?(Symbol) @predicate = Node.intern(@predicate) if @predicate.is_a?(Symbol) @object = case @object when nil then nil when Symbol then Node.intern(@object) when Term then @object else Literal.new(@object) end end ## # Returns `true` to indicate that this value is a statement. # # @return [Boolean] def statement? true end ## # @return [Boolean] def invalid? !valid? end ## # @return [Boolean] def valid? has_subject? && subject.valid? && has_predicate? && predicate.valid? && has_object? && object.valid? && (has_context? ? context.valid? : true ) end ## # @return [Boolean] def asserted? !quoted? end ## # @return [Boolean] def quoted? false end ## # @return [Boolean] def inferred? false end ## # @return [Boolean] def has_graph? has_context? end ## # @return [Boolean] def has_context? !!context end ## # @return [Boolean] def has_subject? !!subject end ## # @return [Boolean] def has_predicate? !!predicate end ## # @return [Boolean] def has_object? !!object end ## # Returns `true` if the subject or object of this statement is a blank # node. # # @return [Boolean] def has_blank_nodes? (has_object? && object.node?) || (has_subject? && subject.node?) end ## # @param [Statement] other # @return [Boolean] def eql?(other) other.is_a?(Statement) && self == other && (self.context || false) == (other.context || false) end ## # @param [Object] other # @return [Boolean] def ==(other) to_a == other.to_a end ## # @param [Statement] other # @return [Boolean] def ===(other) return false if has_context? && !context.eql?(other.context) return false if has_subject? && !subject.eql?(other.subject) return false if has_predicate? && !predicate.eql?(other.predicate) return false if has_object? && !object.eql?(other.object) return true end ## # @param [Integer] index # @return [RDF::Term] def [](index) case index when 0 then self.subject when 1 then self.predicate when 2 then self.object when 3 then self.context else nil end end ## # @param [Integer] index # @param [RDF::Term] value # @return [RDF::Term] def []=(index, value) case index when 0 then self.subject = value when 1 then self.predicate = value when 2 then self.object = value when 3 then self.context = value else nil end end ## # @return [Array(RDF::Term)] def to_quad [subject, predicate, object, context] end ## # @return [Array(RDF::Term)] def to_triple [subject, predicate, object] end alias_method :to_a, :to_triple alias_method :to_ary, :to_triple ## # Returns the terms of this statement as a `Hash`. # # @param [Symbol] subject_key # @param [Symbol] predicate_key # @param [Symbol] object_key # @return [Hash{Symbol => RDF::Term}] def to_hash(subject_key = :subject, predicate_key = :predicate, object_key = :object, context_key = :context) {subject_key => subject, predicate_key => predicate, object_key => object, context_key => context} end ## # Returns a string representation of this statement. # # @return [String] def to_s (context ? to_quad : to_triple).map do |term| term.respond_to?(:to_base) ? term.to_base : term.inspect end.join(" ") + " ." end ## # Returns a graph containing this statement in reified form. # # @param [Hash{Symbol => Object}] options # @return [RDF::Graph] # @see http://www.w3.org/TR/rdf-primer/#reification def reified(options = {}) RDF::Graph.new(options[:context]) do |graph| subject = options[:subject] || RDF::Node.new(options[:id]) graph << [subject, RDF.type, RDF[:Statement]] graph << [subject, RDF.subject, self.subject] graph << [subject, RDF.predicate, self.predicate] graph << [subject, RDF.object, self.object] end end end end