--- /dev/null
+require "rexml/document"
+
+module XMLTemplate
+ def XMLTemplate.process_file(filename, obj, clean = true)
+ file = File.new(filename)
+ doc = REXML::Document.new file
+ XMLTemplate.process!(doc, obj, clean)
+ return doc
+ end
+
+ def XMLTemplate.process!(node, obj, clean = true)
+ if obj.is_a?(String) # overwrite
+ node.children.delete_if { true }
+ node.text = obj
+ elsif obj.is_a?(REXML::Element) # overwrite
+ if obj.is_a?(REXML::Document)
+ obj = obj.root
+ end
+
+ node.children.delete_if { true }
+
+ newobj = obj.deep_clone
+ node.add(newobj)
+
+ process!(newobj, {}, clean)
+ elsif obj.is_a?(Hash) and node.is_a?(REXML::Element) # substitute
+ node.children.each do |child|
+ processed = false
+ attributes_to_delete = []
+
+ if child.is_a?(REXML::Element)
+ if child.namespace == "http://template.sesse.net/"
+ id = child.local_name
+ else
+ # workaround for .to_a being braindamaged
+ attr_list = []
+ child.attributes.each_attribute { |a| attr_list.push(a) }
+ id_attr = attr_list.find { |a|
+ a.namespace == "http://template.sesse.net/" and a.local_name == "id"
+ }
+ if id_attr.nil?
+ id = nil
+ else
+ id = id_attr.value
+ end
+ end
+
+ if clean
+ child.attributes.each_attribute do |a|
+ if a.namespace == "http://template.sesse.net/" then
+ attributes_to_delete.push(a)
+ elsif a.prefix == "xmlns" and a.value == "http://template.sesse.net/" then
+ attributes_to_delete.push(a)
+ end
+ end
+ end
+
+ # check all substitutions to see if we fuond anything appropriate
+ obj.keys.each do |key|
+ if (Regexp.new("^" + Regexp.escape(child.local_name) + "/") =~ key) or
+ (not id.nil? and Regexp.new("^#" + Regexp.escape(id) + "/") =~ key)
+ child.attributes[key.split("/")[1]] = obj[key]
+ end
+
+ if not processed
+ if key == child.local_name or (not id.nil? and key == "#" + id)
+ process!(child, obj[key], clean)
+ processed = true
+ end
+ end
+ end
+ end
+
+ if not processed
+ process!(child, obj, clean)
+ end
+
+ #
+ # We need to do this _after_ processing all the sub-parts,
+ # since if we remove xmlns: attributes before processing,
+ # REXML loses track of the namespaces of the child elements
+ # (since the prefix declaration is no longer there).
+ #
+ attributes_to_delete.each { |a| child.attributes.delete(a) }
+ end
+ elsif obj.is_a?(Array) # repeat
+ doc = node.document
+ frag = REXML::Element.new
+
+ node.children.each { |e| frag.add(e) }
+ node.children.delete_if { true }
+
+ obj.find_all { |x| not x.nil? } .each do |instance|
+ newnode = frag.deep_clone
+ node.add(newnode)
+ process!(newnode, instance, clean)
+ end
+ end
+
+ if clean then
+ clean!(node)
+ end
+ end
+
+ def XMLTemplate.alternate(tag, array, *elems)
+ i = 0
+ array_copy = array.clone
+
+ array_copy.each do |ref|
+ if not ref.nil?
+ ref[tag] = elems[i % elems.length]
+ i = i + 1
+ end
+ end
+
+ return array_copy
+ end
+
+ def XMLTemplate.clean!(node)
+ if node.is_a?(REXML::Element) and (node.namespace == "http://template.sesse.net/" or node.name == "UNDEFINED")
+ # as this is a dummy node, we want to remove it and move everything further up
+ # after we've done any required replacements
+ node.children.each { |n| node.parent.insert_before(node, n) }
+ node.parent.delete(node)
+ end
+ end
+end
+