Merged updates from trunk into stable branch
[feedcatcher.git] / vendor / rails / activesupport / lib / active_support / xml_mini / rexml.rb
diff --git a/vendor/rails/activesupport/lib/active_support/xml_mini/rexml.rb b/vendor/rails/activesupport/lib/active_support/xml_mini/rexml.rb
new file mode 100644 (file)
index 0000000..a8fdeca
--- /dev/null
@@ -0,0 +1,108 @@
+# = XmlMini ReXML implementation
+module ActiveSupport
+  module XmlMini_REXML #:nodoc:
+    extend self
+
+    CONTENT_KEY = '__content__'.freeze
+
+    # Parse an XML Document string into a simple hash
+    #
+    # Same as XmlSimple::xml_in but doesn't shoot itself in the foot,
+    # and uses the defaults from ActiveSupport
+    #
+    # string::
+    #   XML Document string to parse
+    def parse(string)
+      require 'rexml/document' unless defined?(REXML::Document)
+      doc = REXML::Document.new(string)
+      merge_element!({}, doc.root)
+    end
+
+    private
+      # Convert an XML element and merge into the hash
+      #
+      # hash::
+      #   Hash to merge the converted element into.
+      # element::
+      #   XML element to merge into hash
+      def merge_element!(hash, element)
+        merge!(hash, element.name, collapse(element))
+      end
+
+      # Actually converts an XML document element into a data structure.
+      #
+      # element::
+      #   The document element to be collapsed.
+      def collapse(element)
+        hash = get_attributes(element)
+
+        if element.has_elements?
+          element.each_element {|child| merge_element!(hash, child) }
+          merge_texts!(hash, element) unless empty_content?(element)
+          hash
+        else
+          merge_texts!(hash, element)
+        end
+      end
+
+      # Merge all the texts of an element into the hash
+      #
+      # hash::
+      #   Hash to add the converted emement to.
+      # element::
+      #   XML element whose texts are to me merged into the hash
+      def merge_texts!(hash, element)
+        unless element.has_text?
+          hash
+        else
+          # must use value to prevent double-escaping
+          merge!(hash, CONTENT_KEY, element.texts.sum(&:value))
+        end
+      end
+
+      # Adds a new key/value pair to an existing Hash. If the key to be added
+      # already exists and the existing value associated with key is not
+      # an Array, it will be wrapped in an Array. Then the new value is
+      # appended to that Array.
+      #
+      # hash::
+      #   Hash to add key/value pair to.
+      # key::
+      #   Key to be added.
+      # value::
+      #   Value to be associated with key.
+      def merge!(hash, key, value)
+        if hash.has_key?(key)
+          if hash[key].instance_of?(Array)
+            hash[key] << value
+          else
+            hash[key] = [hash[key], value]
+          end
+        elsif value.instance_of?(Array)
+          hash[key] = [value]
+        else
+          hash[key] = value
+        end
+        hash
+      end
+
+      # Converts the attributes array of an XML element into a hash.
+      # Returns an empty Hash if node has no attributes.
+      #
+      # element::
+      #   XML element to extract attributes from.
+      def get_attributes(element)
+        attributes = {}
+        element.attributes.each { |n,v| attributes[n] = v }
+        attributes
+      end
+
+      # Determines if a document element has text content
+      #
+      # element::
+      #   XML element to be checked.
+      def empty_content?(element)
+        element.texts.join.blank?
+      end
+  end
+end