require 'rexml/document' module REXML module Object class ReflectiveElementList include Enumerable attr_reader :parent, :xpath def initialize(parent, *xpath) @parent = parent @xpath = xpath end def [](i) path = "#{self.xpath.first}[#{i+1}]" XPath.first(self.parent, path, self.xpath.last) end def []=(i, element) el = self[i] el ? el.replace_with(element) : self<<(element) end def <<(element) path = "#{self.xpath.first}[last()]" last = XPath.first(self.parent, path, self.xpath.last) last ? last.next_sibling = element : self.parent << element end def last path = "#{self.xpath.first}[last()]" XPath.first(self.parent, path, self.xpath.last) end def first path = "#{self.xpath.first}[1]" XPath.first(self.parent, path, self.xpath.last) end def delete_at(i) path = "#{self.xpath.first}[#{i+1}]" XPath.first(self.parent, path, self.xpath.last).remove end def size path = "count(#{self.xpath.first})" XPath.first(self.parent, path, self.xpath.last) end def to_a XPath.match(self.parent, *self.xpath) end def clear self.each { |el| el.remove } end def replace(ary) self.clear ary.each { |el| self<<(el) } end def each XPath.each(self.parent, *self.xpath) { |el| yield el } end end class ClassifiedElementList < ReflectiveElementList attr_reader :klass def initialize(klass, parent, *xpath) @klass = klass super(parent, *xpath) end def [](i) self.klass.new(super(i)) end def []=(i, obj) el = self[i] el ? el.node.replace_with(obj.node) : self<<(obj.node) end def <<(obj) super(obj.node) end def last self.klass.new(super()) end def first self.klass.new(super()) end def to_a super().collect{ |el| self.klass.new(el) } end def replace(ary) self.clear ary.each { |obj| self<<(obj) } end def each XPath.each(self.parent, *self.xpath) { |el| yield self.klass.new(el) } end def clear self.each { |el| el.node.remove } end def ==(ary) return false if self.size != ary.size self.each_with_index do |item, i| return false if item != ary[i] end return true end end class Base attr_reader :node, :xml_element_name def self.xsi_namespace; 'http://www.w3.org/2001/XMLSchema-instance' end def self.xsd_namespace; 'http://www.w3.org/2001/XMLSchema' end def self.xml_type; self.class.to_s.split('::').last end def initialize(defn=nil) @node = case defn when Document then defn.root when Element then defn when Hash then Element.new(self.xml_element_name) when NilClass then Element.new(self.xml_element_name) else Document.new(defn).root end initialize_members(defn) if defn.is_a?(Hash) end def xml_element_name; self.class.to_s.split('::').last end def find_namespace_by_href(href) self.node.namespaces.find{ |prefix, uri| uri == href } end def get_attribute(name, ns=nil) attribute = self.node.attributes.get_attribute_ns(ns, name) attribute ? attribute.value : nil end def set_attribute(name, value, ns=nil) self.node.attributes[attribute_xpath_for(name, ns)] = value.to_s end def get_element(name, ns=nil) XPath.first(self.node, *element_xpath_for(name, ns)) end def set_element(name, value, ns) el = get_element(name, ns) # The element doesn't exist... if !el el = REXML::Element.new(name) @node << el # so insert it end case value when String then el.text = value.to_s when REXML::Element then value.name = name; el.replace_with(value) end el.add_namespace(ns) if !find_namespace_by_href(ns) end def get_elements(name, ns=nil) ReflectiveElementList.new(self.node, *element_xpath_for(name, ns)) end def set_elements(name, values, ns=nil) get_elements(name, ns).replace(values) end def to_s formatter = Formatters::Default.new xml = '' formatter.write(self.node, xml) xml end protected def attribute_xpath_for(name, ns=nil) if ns namespace = find_namespace_by_href(ns) namespace ? "#{namespace.first}:#{name}" : name else name end end def element_xpath_for(name, ns=nil) ns ? ["x:#{name}", {'x' => ns}] : [name] end private def initialize_members(args) args.each do |name, value| send("#{name}=", value) end end end end end