]> git.sesse.net Git - xml-template/blobdiff - ruby/xmltemplate.rb
Hand-merge from borked repository. Main changes are a Ruby version (with
[xml-template] / ruby / xmltemplate.rb
diff --git a/ruby/xmltemplate.rb b/ruby/xmltemplate.rb
new file mode 100644 (file)
index 0000000..3f4e11a
--- /dev/null
@@ -0,0 +1,128 @@
+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
+