Module: Nori::Parser::REXML

Defined in:
lib/nori/parser/rexml.rb

Overview

Nori::Parser::REXML

REXML pull parser.

Class Method Summary collapse

Class Method Details

.parse(xml, options) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/nori/parser/rexml.rb', line 13

def self.parse(xml, options)
  stack = []
  # Tracks the xml:space value in effect for each open element so
  # whitespace-only text can be kept under xml:space="preserve".
  space_stack = []
  parser = ::REXML::Parsers::BaseParser.new(xml)

  while true
    raw_data = parser.pull
    event = unnormalize(raw_data)
    case event[0]
    when :end_document
      break
    when :end_doctype, :start_doctype
      # do nothing
    when :start_element
      space_stack.push xml_space(event[2], space_stack)
      stack.push Nori::XMLUtilityNode.new(options, event[1], event[2])
    when :end_element
      space_stack.pop
      if stack.size > 1
        temp = stack.pop
        stack.last.add_node(temp)
      end
    when :text
      # Whitespace-only text is insignificant and stripped, unless
      # xml:space="preserve" is in effect under the :standards profile.
      keep = preserve_whitespace?(options, space_stack) || event[1].strip.length > 0
      stack.last.add_node(event[1]) if keep && !stack.empty?
    when :cdata
      # CDATA is the author's explicit literal-data marker (XML 1.0 §2.7),
      # so whitespace-only CDATA is kept rather than stripped like text.
      stack.last.add_node(raw_data[1]) unless stack.empty?
    end
  end
  stack.length > 0 ? stack.pop.to_hash : {}
end

.preserve_whitespace?(options, space_stack) ⇒ Boolean

Whether whitespace-only text must be kept for the current element. Only the :standards profile honors xml:space.

Returns:

  • (Boolean)


66
67
68
# File 'lib/nori/parser/rexml.rb', line 66

def self.preserve_whitespace?(options, space_stack)
  options[:standards] && space_stack.last == "preserve"
end

.unnormalize(event) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
# File 'lib/nori/parser/rexml.rb', line 70

def self.unnormalize(event)
  event.map do |el|
    if el.is_a?(String)
      ::REXML::Text.unnormalize(el)
    elsif el.is_a?(Hash)
      el.each {|k,v| el[k] = ::REXML::Text.unnormalize(v)}
    else
      el
    end
  end
end

.xml_space(attributes, space_stack) ⇒ String

The xml:space value for an element: its own declaration if it has one, otherwise the value inherited from the nearest ancestor. Defaults to "default". See XML 1.0 §2.10.

Parameters:

  • attributes (Hash{String => String})

    the element's attributes

  • space_stack (Array<String>)

    the ancestor xml:space values

Returns:

  • (String)

    "preserve", "default", or an inherited value



58
59
60
# File 'lib/nori/parser/rexml.rb', line 58

def self.xml_space(attributes, space_stack)
  (attributes && attributes["xml:space"]) || space_stack.last || "default"
end