module Redlander # RDF statement class Statement class << self private # @api private def finalize_statement(rdf_statement_ptr) proc { Redland.librdf_free_statement(rdf_statement_ptr) } end end # @api private def rdf_statement unless instance_variable_defined?(:@rdf_statement) @rdf_statement = case @source when FFI::Pointer @source when Hash # Create a new statement from nodes s = rdf_node_from(@source[:subject]) p = rdf_node_from(@source[:predicate]) o = rdf_node_from(@source[:object]) Redland.librdf_new_statement_from_nodes(Redlander.rdf_world, s, p, o) else raise NotImplementedError, "Cannot create Statement from '#{@source.inspect}'" end raise RedlandError, "Failed to create a new statement" if @rdf_statement.null? ObjectSpace.define_finalizer(self, self.class.send(:finalize_statement, @rdf_statement)) end @rdf_statement end # Create an RDF statement. # # @param [Hash] source # @option source [Node, String, URI, nil] :subject # @option source [Node, String, URI, nil] :predicate # @option source [Node, String, URI, nil] :object # @raise [NotImplementedError] if cannot create a Statement from the given source. # @raise [RedlandError] if it fails to create a Statement. def initialize(source = {}) # If FFI::Pointer is passed, wrap it instantly, # because it can be freed outside before it is used here. @source = source.is_a?(FFI::Pointer) ? wrap(source) : source end # Subject of the statment. # # @return [Node, nil] def subject if instance_variable_defined?(:@subject) @subject else rdf_node = Redland.librdf_statement_get_subject(rdf_statement) @subject = rdf_node.null? ? nil : Node.new(rdf_node) end end # Predicate of the statement. # # @return [Node, nil] def predicate if instance_variable_defined?(:@predicate) @predicate else rdf_node = Redland.librdf_statement_get_predicate(rdf_statement) @predicate = rdf_node.null? ? nil : Node.new(rdf_node) end end # Object of the statement. # # @return [Node, nil] def object if instance_variable_defined?(:@object) @object else rdf_node = Redland.librdf_statement_get_object(rdf_statement) @object = rdf_node.null? ? nil : Node.new(rdf_node) end end # Set the subject of the statement # # @param [Node, nil] node # @return [void] def subject=(node) Redland.librdf_statement_set_subject(rdf_statement, rdf_node_from(node)) @subject = node end # Set the predicate of the statement # # @param [Node, nil] node # @return [void] def predicate=(node) Redland.librdf_statement_set_predicate(rdf_statement, rdf_node_from(node)) @predicate = node end # Set the object of the statement # # @param [Node, nil] node # @return [void] def object=(node) Redland.librdf_statement_set_object(rdf_statement, rdf_node_from(node)) @object = node end def eql?(other_statement) subject == other_statement.subject && predicate == other_statement.predicate && object == other_statement.object end alias_method :==, :eql? def hash self.class.hash + to_s.hash end def to_s Redland.librdf_statement_to_string(rdf_statement) end private # @api private def wrap(s) if s.null? raise RedlandError, "Failed to create a new statement" else Redland.librdf_new_statement_from_statement(s) end end # Create a Node from the source # and get its rdf_node, or return nil # @api private def rdf_node_from(source) case source when NilClass nil when Node Redland.librdf_new_node_from_node(source.rdf_node) else Redland.librdf_new_node_from_node(Node.new(source).rdf_node) end end end end