File: active_support/xml_mini/jdom.rb

Overview
Module Structure
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: ActiveSupport#14
  module: XmlMini_JDOM#15
extends
  XmlMini_JDOM ( ActiveSupport )
has properties
constant: CONTENT_KEY #18
constant: NODE_TYPE_NAMES #20
method: parse / 1 #30
method: merge_element! / 2 #50
method: collapse / 1 #58
method: merge_texts! / 2 #80
method: merge! / 3 #101
method: get_attributes / 1 #121
method: texts / 1 #134
method: empty_content? / 1 #150

Code

   1  raise "JRuby is required to use the JDOM backend for XmlMini" unless RUBY_PLATFORM =~ /java/
   2 
   3  require 'jruby'
   4  include Java
   5 
   6  import javax.xml.parsers.DocumentBuilder unless defined? DocumentBuilder
   7  import javax.xml.parsers.DocumentBuilderFactory unless defined? DocumentBuilderFactory
   8  import java.io.StringReader unless defined? StringReader
   9  import org.xml.sax.InputSource unless defined? InputSource
  10  import org.xml.sax.Attributes unless defined? Attributes
  11  import org.w3c.dom.Node unless defined? Node
  12 
  13  # = XmlMini JRuby JDOM implementation
  14  module ActiveSupport
  15    module XmlMini_JDOM #:nodoc:
  16      extend self
  17 
  18      CONTENT_KEY = '__content__'.freeze
  19 
  20      NODE_TYPE_NAMES = %w{ATTRIBUTE_NODE CDATA_SECTION_NODE COMMENT_NODE DOCUMENT_FRAGMENT_NODE
  21      DOCUMENT_NODE DOCUMENT_TYPE_NODE ELEMENT_NODE ENTITY_NODE ENTITY_REFERENCE_NODE NOTATION_NODE
  22      PROCESSING_INSTRUCTION_NODE TEXT_NODE}
  23 
  24      node_type_map = {}
  25      NODE_TYPE_NAMES.each { |type| node_type_map[Node.send(type)] = type }
  26 
  27      # Parse an XML Document string into a simple hash using Java's jdom.
  28      # string::
  29      #   XML Document string to parse
  30      def parse(string)
  31        if string.blank?
  32          {}
  33        else
  34          @dbf = DocumentBuilderFactory.new_instance
  35          xml_string_reader = StringReader.new(string)
  36          xml_input_source = InputSource.new(xml_string_reader)
  37          doc = @dbf.new_document_builder.parse(xml_input_source)
  38          merge_element!({}, doc.document_element)
  39        end
  40      end
  41 
  42      private
  43 
  44      # Convert an XML element and merge into the hash
  45      #
  46      # hash::
  47      #   Hash to merge the converted element into.
  48      # element::
  49      #   XML element to merge into hash
  50      def merge_element!(hash, element)
  51        merge!(hash, element.tag_name, collapse(element))
  52      end
  53 
  54      # Actually converts an XML document element into a data structure.
  55      #
  56      # element::
  57      #   The document element to be collapsed.
  58      def collapse(element)
  59        hash = get_attributes(element)
  60 
  61        child_nodes = element.child_nodes
  62        if child_nodes.length > 0
  63          for i in 0...child_nodes.length
  64            child = child_nodes.item(i)
  65            merge_element!(hash, child) unless child.node_type == Node.TEXT_NODE
  66          end
  67          merge_texts!(hash, element) unless empty_content?(element)
  68          hash
  69        else
  70          merge_texts!(hash, element)
  71        end
  72      end
  73 
  74      # Merge all the texts of an element into the hash
  75      #
  76      # hash::
  77      #   Hash to add the converted emement to.
  78      # element::
  79      #   XML element whose texts are to me merged into the hash
  80      def merge_texts!(hash, element)
  81        text_children = texts(element)
  82        if text_children.join.empty?
  83          hash
  84        else
  85          # must use value to prevent double-escaping
  86          merge!(hash, CONTENT_KEY, text_children.join)
  87        end
  88      end
  89 
  90      # Adds a new key/value pair to an existing Hash. If the key to be added
  91      # already exists and the existing value associated with key is not
  92      # an Array, it will be wrapped in an Array. Then the new value is
  93      # appended to that Array.
  94      #
  95      # hash::
  96      #   Hash to add key/value pair to.
  97      # key::
  98      #   Key to be added.
  99      # value::
 100      #   Value to be associated with key.
 101      def merge!(hash, key, value)
 102        if hash.has_key?(key)
 103          if hash[key].instance_of?(Array)
 104            hash[key] << value
 105          else
 106            hash[key] = [hash[key], value]
 107          end
 108        elsif value.instance_of?(Array)
 109          hash[key] = [value]
 110        else
 111          hash[key] = value
 112        end
 113        hash
 114      end
 115 
 116      # Converts the attributes array of an XML element into a hash.
 117      # Returns an empty Hash if node has no attributes.
 118      #
 119      # element::
 120      #   XML element to extract attributes from.
 121      def get_attributes(element)
 122        attribute_hash = {}
 123        attributes = element.attributes
 124        for i in 0...attributes.length
 125           attribute_hash[attributes.item(i).name] =  attributes.item(i).value
 126         end
 127        attribute_hash
 128      end
 129 
 130      # Determines if a document element has text content
 131      #
 132      # element::
 133      #   XML element to be checked.
 134      def texts(element)
 135        texts = []
 136        child_nodes = element.child_nodes
 137        for i in 0...child_nodes.length
 138          item = child_nodes.item(i)
 139          if item.node_type == Node.TEXT_NODE
 140            texts << item.get_data
 141          end
 142        end
 143        texts
 144      end
 145 
 146      # Determines if a document element has text content
 147      #
 148      # element::
 149      #   XML element to be checked.
 150      def empty_content?(element)
 151        text = ''
 152        child_nodes = element.child_nodes
 153        for i in 0...child_nodes.length
 154          item = child_nodes.item(i)
 155          if item.node_type == Node.TEXT_NODE
 156            text << item.get_data.strip
 157          end
 158        end
 159        text.strip.length == 0
 160      end
 161    end
 162  end