diff options
Diffstat (limited to 'activesupport/lib/active_support')
20 files changed, 376 insertions, 222 deletions
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index fb4b5f0f3c..a44344806d 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -110,8 +110,10 @@ class Module allow_nil = options[:allow_nil] && "#{to} && " + file, line = caller[0].split(":") + methods.each do |method| - module_eval(<<-EOS, "(__DELEGATION__)", 1) + module_eval(<<-EOS, file, line.to_i) def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block) #{allow_nil}#{to}.__send__(#{method.inspect}, *args, &block) # client && client.__send__(:name, *args, &block) end # end diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index ec54efe08e..dec56715be 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -3,7 +3,7 @@ require 'active_support/inflector' module ActiveSupport module Deprecation class DeprecationProxy #:nodoc: - instance_methods.each { |m| undef_method m unless m =~ /^__/ } + instance_methods.each { |m| undef_method m unless m =~ /^__|^object_id$/ } # Don't give a deprecation warning on inspect since test/unit and error # logs rely on it for diagnostics. diff --git a/activesupport/lib/active_support/json.rb b/activesupport/lib/active_support/json.rb index fc433de582..5072992cdf 100644 --- a/activesupport/lib/active_support/json.rb +++ b/activesupport/lib/active_support/json.rb @@ -11,6 +11,34 @@ module ActiveSupport # matches YAML-formatted dates DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/ + module Encoding #:nodoc: + mattr_accessor :escape_regex + + ESCAPED_CHARS = { + "\010" => '\b', + "\f" => '\f', + "\n" => '\n', + "\r" => '\r', + "\t" => '\t', + '"' => '\"', + '\\' => '\\\\', + '>' => '\u003E', + '<' => '\u003C', + '&' => '\u0026' + } + + def self.escape(string) + string = string.dup.force_encoding(::Encoding::BINARY) if string.respond_to?(:force_encoding) + json = string.gsub(escape_regex) { |s| ESCAPED_CHARS[s] }. + gsub(/([\xC0-\xDF][\x80-\xBF]| + [\xE0-\xEF][\x80-\xBF]{2}| + [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s| + s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&') + } + %("#{json}") + end + end + class << self attr_reader :backend delegate :decode, :to => :backend @@ -34,9 +62,7 @@ module ActiveSupport end class << self - def escape_html_entities_in_json - @escape_html_entities_in_json - end + attr_reader :escape_html_entities_in_json def escape_html_entities_in_json=(value) ActiveSupport::JSON::Encoding.escape_regex = \ @@ -48,8 +74,9 @@ module ActiveSupport @escape_html_entities_in_json = value end end - - JSON.backend = 'Yaml' end -require 'active_support/json/encoding'
\ No newline at end of file +ActiveSupport.escape_html_entities_in_json = true +ActiveSupport::JSON.backend = 'Yaml' + +require 'active_support/json/encoding' diff --git a/activesupport/lib/active_support/json/encoders/date.rb b/activesupport/lib/active_support/json/encoders/date.rb index 79c3957362..9adb3c20e2 100644 --- a/activesupport/lib/active_support/json/encoders/date.rb +++ b/activesupport/lib/active_support/json/encoders/date.rb @@ -1,23 +1,22 @@ class Date - # Returns a JSON string representing the date. If ActiveSupport.use_standard_json_time_format is set to true, the - # ISO 8601 format is used. - # - # ==== Examples - # - # # With ActiveSupport.use_standard_json_time_format = true - # Date.new(2005,2,1).to_json - # # => "2005-02-01" - # - # # With ActiveSupport.use_standard_json_time_format = false - # Date.new(2005,2,1).to_json - # # => "2005/02/01" - def rails_to_json(options = nil) - if ActiveSupport.use_standard_json_time_format - %("#{strftime("%Y-%m-%d")}") - else - %("#{strftime("%Y/%m/%d")}") + private + # Returns a JSON string representing the date. If ActiveSupport.use_standard_json_time_format is set to true, the + # ISO 8601 format is used. + # + # ==== Examples + # + # # With ActiveSupport.use_standard_json_time_format = true + # Date.new(2005,2,1).to_json + # # => "2005-02-01" + # + # # With ActiveSupport.use_standard_json_time_format = false + # Date.new(2005,2,1).to_json + # # => "2005/02/01" + def rails_to_json(*) + if ActiveSupport.use_standard_json_time_format + %("#{strftime("%Y-%m-%d")}") + else + %("#{strftime("%Y/%m/%d")}") + end end - end - - alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/date_time.rb b/activesupport/lib/active_support/json/encoders/date_time.rb index cdfc39b9f3..3a29292b24 100644 --- a/activesupport/lib/active_support/json/encoders/date_time.rb +++ b/activesupport/lib/active_support/json/encoders/date_time.rb @@ -1,23 +1,22 @@ class DateTime - # Returns a JSON string representing the datetime. If ActiveSupport.use_standard_json_time_format is set to true, the - # ISO 8601 format is used. - # - # ==== Examples - # - # # With ActiveSupport.use_standard_json_time_format = true - # DateTime.civil(2005,2,1,15,15,10).to_json - # # => "2005-02-01T15:15:10+00:00" - # - # # With ActiveSupport.use_standard_json_time_format = false - # DateTime.civil(2005,2,1,15,15,10).to_json - # # => "2005/02/01 15:15:10 +0000" - def rails_to_json(options = nil) - if ActiveSupport.use_standard_json_time_format - xmlschema.inspect - else - strftime('"%Y/%m/%d %H:%M:%S %z"') + private + # Returns a JSON string representing the datetime. If ActiveSupport.use_standard_json_time_format is set to true, the + # ISO 8601 format is used. + # + # ==== Examples + # + # # With ActiveSupport.use_standard_json_time_format = true + # DateTime.civil(2005,2,1,15,15,10).to_json + # # => "2005-02-01T15:15:10+00:00" + # + # # With ActiveSupport.use_standard_json_time_format = false + # DateTime.civil(2005,2,1,15,15,10).to_json + # # => "2005/02/01 15:15:10 +0000" + def rails_to_json(*) + if ActiveSupport.use_standard_json_time_format + xmlschema.inspect + else + strftime('"%Y/%m/%d %H:%M:%S %z"') + end end - end - - alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/enumerable.rb b/activesupport/lib/active_support/json/encoders/enumerable.rb index e1c3ec249d..898990a59c 100644 --- a/activesupport/lib/active_support/json/encoders/enumerable.rb +++ b/activesupport/lib/active_support/json/encoders/enumerable.rb @@ -1,14 +1,13 @@ module Enumerable - # Returns a JSON string representing the enumerable. Any +options+ - # given will be passed on to its elements. For example: - # - # users = User.find(:all) - # # => users.to_json(:only => :name) - # - # will pass the <tt>:only => :name</tt> option to each user. - def rails_to_json(options = {}) #:nodoc: - "[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ','}]" - end - - alias to_json rails_to_json + private + # Returns a JSON string representing the enumerable. Any +options+ + # given will be passed on to its elements. For example: + # + # users = User.find(:all) + # # => users.to_json(:only => :name) + # + # will pass the <tt>:only => :name</tt> option to each user. + def rails_to_json(options = nil, *args) #:nodoc: + "[#{map { |value| ActiveSupport::JSON.encode(value, options, *args) } * ','}]" + end end diff --git a/activesupport/lib/active_support/json/encoders/false_class.rb b/activesupport/lib/active_support/json/encoders/false_class.rb index a7657cca37..eb975fe542 100644 --- a/activesupport/lib/active_support/json/encoders/false_class.rb +++ b/activesupport/lib/active_support/json/encoders/false_class.rb @@ -1,7 +1,6 @@ class FalseClass - def rails_to_json(options = nil) #:nodoc: - 'false' - end - - alias to_json rails_to_json + private + def rails_to_json(*) + 'false' + end end diff --git a/activesupport/lib/active_support/json/encoders/hash.rb b/activesupport/lib/active_support/json/encoders/hash.rb index 19b97d7b8c..4771484843 100644 --- a/activesupport/lib/active_support/json/encoders/hash.rb +++ b/activesupport/lib/active_support/json/encoders/hash.rb @@ -1,50 +1,51 @@ require 'active_support/core_ext/array/wrap' class Hash - # Returns a JSON string representing the hash. - # - # Without any +options+, the returned JSON string will include all - # the hash keys. For example: - # - # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json - # # => {"name": "Konata Izumi", "1": 2, "age": 16} - # - # The keys in the JSON string are unordered due to the nature of hashes. - # - # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the - # attributes included, and will accept 1 or more hash keys to include/exclude. - # - # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:only => [:name, 'age']) - # # => {"name": "Konata Izumi", "age": 16} - # - # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:except => 1) - # # => {"name": "Konata Izumi", "age": 16} - # - # The +options+ also filter down to any hash values. This is particularly - # useful for converting hashes containing ActiveRecord objects or any object - # that responds to options in their <tt>to_json</tt> method. For example: - # - # users = User.find(:all) - # { :users => users, :count => users.size }.to_json(:include => :posts) - # - # would pass the <tt>:include => :posts</tt> option to <tt>users</tt>, - # allowing the posts association in the User model to be converted to JSON - # as well. - def rails_to_json(options = {}) #:nodoc: - hash_keys = self.keys + private + # Returns a JSON string representing the hash. + # + # Without any +options+, the returned JSON string will include all + # the hash keys. For example: + # + # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json + # # => {"name": "Konata Izumi", "1": 2, "age": 16} + # + # The keys in the JSON string are unordered due to the nature of hashes. + # + # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the + # attributes included, and will accept 1 or more hash keys to include/exclude. + # + # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:only => [:name, 'age']) + # # => {"name": "Konata Izumi", "age": 16} + # + # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:except => 1) + # # => {"name": "Konata Izumi", "age": 16} + # + # The +options+ also filter down to any hash values. This is particularly + # useful for converting hashes containing ActiveRecord objects or any object + # that responds to options in their <tt>to_json</tt> method. For example: + # + # users = User.find(:all) + # { :users => users, :count => users.size }.to_json(:include => :posts) + # + # would pass the <tt>:include => :posts</tt> option to <tt>users</tt>, + # allowing the posts association in the User model to be converted to JSON + # as well. + def rails_to_json(options = nil, *args) #:nodoc: + hash_keys = self.keys - if except = options[:except] - hash_keys = hash_keys - Array.wrap(except) - elsif only = options[:only] - hash_keys = hash_keys & Array.wrap(only) - end - - result = '{' - result << hash_keys.map do |key| - "#{ActiveSupport::JSON.encode(key.to_s)}:#{ActiveSupport::JSON.encode(self[key], options)}" - end * ',' - result << '}' - end + if options + if except = options[:except] + hash_keys = hash_keys - Array.wrap(except) + elsif only = options[:only] + hash_keys = hash_keys & Array.wrap(only) + end + end - alias to_json rails_to_json + result = '{' + result << hash_keys.map do |key| + "#{ActiveSupport::JSON.encode(key.to_s)}:#{ActiveSupport::JSON.encode(self[key], options, *args)}" + end * ',' + result << '}' + end end diff --git a/activesupport/lib/active_support/json/encoders/nil_class.rb b/activesupport/lib/active_support/json/encoders/nil_class.rb index b31e1dd249..8c51dba384 100644 --- a/activesupport/lib/active_support/json/encoders/nil_class.rb +++ b/activesupport/lib/active_support/json/encoders/nil_class.rb @@ -1,7 +1,6 @@ class NilClass - def rails_to_json(options = nil) #:nodoc: - 'null' - end - - alias to_json rails_to_json + private + def rails_to_json(*) + 'null' + end end diff --git a/activesupport/lib/active_support/json/encoders/numeric.rb b/activesupport/lib/active_support/json/encoders/numeric.rb index 491b330c39..c7cd0df1d7 100644 --- a/activesupport/lib/active_support/json/encoders/numeric.rb +++ b/activesupport/lib/active_support/json/encoders/numeric.rb @@ -1,7 +1,6 @@ class Numeric - def rails_to_json(options = nil) #:nodoc: - to_s - end - - alias to_json rails_to_json + private + def rails_to_json(*) + to_s + end end diff --git a/activesupport/lib/active_support/json/encoders/object.rb b/activesupport/lib/active_support/json/encoders/object.rb index d68f50562e..9cc12d91ac 100644 --- a/activesupport/lib/active_support/json/encoders/object.rb +++ b/activesupport/lib/active_support/json/encoders/object.rb @@ -2,9 +2,12 @@ require 'active_support/core_ext/object/instance_variables' class Object # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. - def rails_to_json(options = {}) - ActiveSupport::JSON.encode(instance_values, options) + def to_json(options = nil) + ActiveSupport::JSON.encode(self, options) end - alias to_json rails_to_json + private + def rails_to_json(*args) + ActiveSupport::JSON.encode(instance_values, *args) + end end diff --git a/activesupport/lib/active_support/json/encoders/regexp.rb b/activesupport/lib/active_support/json/encoders/regexp.rb index 63ccd7c490..ee42db4d02 100644 --- a/activesupport/lib/active_support/json/encoders/regexp.rb +++ b/activesupport/lib/active_support/json/encoders/regexp.rb @@ -1,7 +1,6 @@ class Regexp - def rails_to_json(options = nil) #:nodoc: - inspect - end - - alias to_json rails_to_json + private + def rails_to_json(*) + inspect + end end diff --git a/activesupport/lib/active_support/json/encoders/string.rb b/activesupport/lib/active_support/json/encoders/string.rb index 27bef3b9cc..4a6b21c1c0 100644 --- a/activesupport/lib/active_support/json/encoders/string.rb +++ b/activesupport/lib/active_support/json/encoders/string.rb @@ -1,38 +1,6 @@ -module ActiveSupport - module JSON - module Encoding - mattr_accessor :escape_regex - - ESCAPED_CHARS = { - "\010" => '\b', - "\f" => '\f', - "\n" => '\n', - "\r" => '\r', - "\t" => '\t', - '"' => '\"', - '\\' => '\\\\', - '>' => '\u003E', - '<' => '\u003C', - '&' => '\u0026' - } - end - end -end - -ActiveSupport.escape_html_entities_in_json = true - class String - def rails_to_json(options = nil) #:nodoc: - json = '"' + gsub(ActiveSupport::JSON::Encoding.escape_regex) { |s| - ActiveSupport::JSON::Encoding::ESCAPED_CHARS[s] - } - json.force_encoding('ascii-8bit') if respond_to?(:force_encoding) - json.gsub(/([\xC0-\xDF][\x80-\xBF]| - [\xE0-\xEF][\x80-\xBF]{2}| - [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s| - s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&') - } + '"' - end - - alias to_json rails_to_json + private + def rails_to_json(*) + ActiveSupport::JSON::Encoding.escape(self) + end end diff --git a/activesupport/lib/active_support/json/encoders/symbol.rb b/activesupport/lib/active_support/json/encoders/symbol.rb index 6487bf8cb7..d575350a4e 100644 --- a/activesupport/lib/active_support/json/encoders/symbol.rb +++ b/activesupport/lib/active_support/json/encoders/symbol.rb @@ -1,7 +1,6 @@ class Symbol - def rails_to_json(options = {}) #:nodoc: - ActiveSupport::JSON.encode(to_s, options) - end - - alias to_json rails_to_json + private + def rails_to_json(*args) + ActiveSupport::JSON.encode(to_s, *args) + end end diff --git a/activesupport/lib/active_support/json/encoders/time.rb b/activesupport/lib/active_support/json/encoders/time.rb index 2699672949..d434b9aace 100644 --- a/activesupport/lib/active_support/json/encoders/time.rb +++ b/activesupport/lib/active_support/json/encoders/time.rb @@ -1,25 +1,24 @@ require 'active_support/core_ext/time/conversions' class Time - # Returns a JSON string representing the time. If ActiveSupport.use_standard_json_time_format is set to true, the - # ISO 8601 format is used. - # - # ==== Examples - # - # # With ActiveSupport.use_standard_json_time_format = true - # Time.utc(2005,2,1,15,15,10).to_json - # # => "2005-02-01T15:15:10Z" - # - # # With ActiveSupport.use_standard_json_time_format = false - # Time.utc(2005,2,1,15,15,10).to_json - # # => "2005/02/01 15:15:10 +0000" - def rails_to_json(options = nil) - if ActiveSupport.use_standard_json_time_format - xmlschema.inspect - else - %("#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}") + private + # Returns a JSON string representing the time. If ActiveSupport.use_standard_json_time_format is set to true, the + # ISO 8601 format is used. + # + # ==== Examples + # + # # With ActiveSupport.use_standard_json_time_format = true + # Time.utc(2005,2,1,15,15,10).to_json + # # => "2005-02-01T15:15:10Z" + # + # # With ActiveSupport.use_standard_json_time_format = false + # Time.utc(2005,2,1,15,15,10).to_json + # # => "2005/02/01 15:15:10 +0000" + def rails_to_json(*) + if ActiveSupport.use_standard_json_time_format + xmlschema.inspect + else + %("#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}") + end end - end - - alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/true_class.rb b/activesupport/lib/active_support/json/encoders/true_class.rb index ac7c7d1e87..bc25a6db78 100644 --- a/activesupport/lib/active_support/json/encoders/true_class.rb +++ b/activesupport/lib/active_support/json/encoders/true_class.rb @@ -1,7 +1,6 @@ class TrueClass - def rails_to_json(options = nil) #:nodoc: - 'true' - end - - alias to_json rails_to_json + private + def rails_to_json(*) + 'true' + end end diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 42a217cedc..5fefe5b88b 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -4,11 +4,13 @@ module ActiveSupport end # Converts a Ruby object into a JSON string. - def self.encode(value, options = {}) - seen = (options[:seen] ||= []) - raise CircularReferenceError, 'object references itself' if seen.include?(value) + def self.encode(value, options = nil, seen = nil) + seen ||= [] + if seen.any? { |object| object.equal?(value) } + raise CircularReferenceError, 'object references itself' + end seen << value - value.send(:rails_to_json, options) + value.__send__(:rails_to_json, options, seen) ensure seen.pop end diff --git a/activesupport/lib/active_support/json/variable.rb b/activesupport/lib/active_support/json/variable.rb index 3ee152ee3c..daa7449b71 100644 --- a/activesupport/lib/active_support/json/variable.rb +++ b/activesupport/lib/active_support/json/variable.rb @@ -2,9 +2,10 @@ module ActiveSupport module JSON # A string that returns itself as its JSON-encoded form. class Variable < String - def rails_to_json(options=nil) - self - end + private + def rails_to_json(*) + self + end end end end diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 1949ce0ad3..069842c6c3 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -118,28 +118,6 @@ module ActiveSupport end alias_method :iso8601, :xmlschema - # Returns a JSON string representing the TimeWithZone. If ActiveSupport.use_standard_json_time_format is set to - # true, the ISO 8601 format is used. - # - # ==== Examples - # - # # With ActiveSupport.use_standard_json_time_format = true - # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json - # # => "2005-02-01T15:15:10Z" - # - # # With ActiveSupport.use_standard_json_time_format = false - # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json - # # => "2005/02/01 15:15:10 +0000" - def rails_to_json(options = nil) - if !ActiveSupport.respond_to?(:use_standard_json_time_format) || ActiveSupport.use_standard_json_time_format - xmlschema.inspect - else - %("#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}") - end - end - - alias to_json rails_to_json - def to_yaml(options = {}) if options.kind_of?(YAML::Emitter) utc.to_yaml(options) @@ -323,6 +301,26 @@ module ActiveSupport end private + # Returns a JSON string representing the TimeWithZone. If ActiveSupport.use_standard_json_time_format is set to + # true, the ISO 8601 format is used. + # + # ==== Examples + # + # # With ActiveSupport.use_standard_json_time_format = true + # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json + # # => "2005-02-01T15:15:10Z" + # + # # With ActiveSupport.use_standard_json_time_format = false + # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json + # # => "2005/02/01 15:15:10 +0000" + def rails_to_json(*) + if !ActiveSupport.respond_to?(:use_standard_json_time_format) || ActiveSupport.use_standard_json_time_format + xmlschema.inspect + else + %("#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}") + end + end + def get_period_and_ensure_valid_local_time # we don't want a Time.local instance enforcing its own DST rules as well, # so transfer time values to a utc constructor if necessary diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb new file mode 100644 index 0000000000..d795d55690 --- /dev/null +++ b/activesupport/lib/active_support/xml_mini/jdom.rb @@ -0,0 +1,162 @@ +raise "JRuby is required to use the JDOM backend for XmlMini" unless RUBY_PLATFORM =~ /java/ + +require 'jruby' +include Java + +import javax.xml.parsers.DocumentBuilder unless defined? DocumentBuilder +import javax.xml.parsers.DocumentBuilderFactory unless defined? DocumentBuilderFactory +import java.io.StringReader unless defined? StringReader +import org.xml.sax.InputSource unless defined? InputSource +import org.xml.sax.Attributes unless defined? Attributes +import org.w3c.dom.Node unless defined? Node + +# = XmlMini JRuby JDOM implementation +module ActiveSupport + module XmlMini_JDOM #:nodoc: + extend self + + CONTENT_KEY = '__content__'.freeze + + NODE_TYPE_NAMES = %w{ATTRIBUTE_NODE CDATA_SECTION_NODE COMMENT_NODE DOCUMENT_FRAGMENT_NODE + DOCUMENT_NODE DOCUMENT_TYPE_NODE ELEMENT_NODE ENTITY_NODE ENTITY_REFERENCE_NODE NOTATION_NODE + PROCESSING_INSTRUCTION_NODE TEXT_NODE} + + node_type_map = {} + NODE_TYPE_NAMES.each { |type| node_type_map[Node.send(type)] = type } + + # Parse an XML Document string into a simple hash using Java's jdom. + # string:: + # XML Document string to parse + def parse(string) + if string.blank? + {} + else + @dbf = DocumentBuilderFactory.new_instance + xml_string_reader = StringReader.new(string) + xml_input_source = InputSource.new(xml_string_reader) + doc = @dbf.new_document_builder.parse(xml_input_source) + merge_element!({}, doc.document_element) + end + 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.tag_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) + + child_nodes = element.child_nodes + if child_nodes.length > 0 + for i in 0...child_nodes.length + child = child_nodes.item(i) + merge_element!(hash, child) unless child.node_type == Node.TEXT_NODE + end + 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) + text_children = texts(element) + if text_children.join.empty? + hash + else + # must use value to prevent double-escaping + merge!(hash, CONTENT_KEY, text_children.join) + 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) + attribute_hash = {} + attributes = element.attributes + for i in 0...attributes.length + attribute_hash[attributes.item(i).name] = attributes.item(i).value + end + attribute_hash + end + + # Determines if a document element has text content + # + # element:: + # XML element to be checked. + def texts(element) + texts = [] + child_nodes = element.child_nodes + for i in 0...child_nodes.length + item = child_nodes.item(i) + if item.node_type == Node.TEXT_NODE + texts << item.get_data + end + end + texts + end + + # Determines if a document element has text content + # + # element:: + # XML element to be checked. + def empty_content?(element) + text = '' + child_nodes = element.child_nodes + for i in 0...child_nodes.length + item = child_nodes.item(i) + if item.node_type == Node.TEXT_NODE + text << item.get_data.strip + end + end + text.strip.length == 0 + end + end +end |