# frozen_string_literal: true
require_relative "xml_utils"
require_relative "node_set"
module Moxml
class Node
include XmlUtils
attr_reader :native, :context
def initialize(native, context)
@native = native
@context = context
end
def document
Document.wrap(adapter.document(@native), context)
end
def parent
Node.wrap(adapter.parent(@native), context)
end
def children
NodeSet.new(adapter.children(@native), context)
end
def next_sibling
Node.wrap(adapter.next_sibling(@native), context)
end
def previous_sibling
Node.wrap(adapter.previous_sibling(@native), context)
end
def add_child(node)
node = prepare_node(node)
adapter.add_child(@native, node.native)
self
end
def add_previous_sibling(node)
node = prepare_node(node)
adapter.add_previous_sibling(@native, node.native)
self
end
def add_next_sibling(node)
node = prepare_node(node)
adapter.add_next_sibling(@native, node.native)
self
end
def remove
adapter.remove(@native)
self
end
def replace(node)
node = prepare_node(node)
adapter.replace(@native, node.native)
self
end
def to_xml(options = {})
adapter.serialize(@native, default_options.merge(options))
end
def xpath(expression, namespaces = {})
NodeSet.new(adapter.xpath(@native, expression, namespaces), context)
end
def at_xpath(expression, namespaces = {})
Node.wrap(adapter.at_xpath(@native, expression, namespaces), context)
end
def ==(other)
self.class == other.class && @native == other.native
end
def self.wrap(node, context)
return nil if node.nil?
klass = case adapter(context).node_type(node)
when :element then Element
when :text then Text
when :cdata then Cdata
when :comment then Comment
when :processing_instruction then ProcessingInstruction
when :document then Document
when :declaration then Declaration
when :doctype then Doctype
else self
end
klass.new(node, context)
end
protected
def adapter
context.config.adapter
end
def self.adapter(context)
context.config.adapter
end
private
def prepare_node(node)
case node
when String then Text.new(adapter.create_text(node), context)
when Node then node
else
raise ArgumentError, "Invalid node type: #{node.class}"
end
end
def default_options
{
encoding: context.config.default_encoding,
indent: context.config.default_indent,
# The short format of empty tags in Oga and Nokogiri isn't configurable
# Oga: (with a space)
# Nokogiri: (without a space)
# The expanded format is enforced to avoid this conflict
expand_empty: true
}
end
end
end