class REXML::Parsers::SAX2Parser

SAX2Parser

def add( pair )

def add( pair )
  if pair[-1].respond_to? :call
    @procs << pair unless @procs.include? pair
  else
    @listeners << pair unless @listeners.include? pair
    @has_listeners = true
  end
end

def add_listener( listener )

def add_listener( listener )
  @parser.add_listener( listener )
end

def deafen( listener=nil, &blok )

def deafen( listener=nil, &blok )
  if listener
    @listeners.delete_if {|item| item[-1] == listener }
    @has_listeners = false if @listeners.size == 0
  else
    @procs.delete_if {|item| item[-1] == blok }
  end
end

def entity_expansion_count

def entity_expansion_count
  @parser.entity_expansion_count
end

def entity_expansion_limit=( limit )

def entity_expansion_limit=( limit )
  @parser.entity_expansion_limit = limit
end

def entity_expansion_text_limit=( limit )

def entity_expansion_text_limit=( limit )
  @parser.entity_expansion_text_limit = limit
end

def get_listeners( symbol, name )

def get_listeners( symbol, name )
  return nil if @listeners.size == 0
  @listeners.find_all do |sym, match, block|
    (
      (sym.nil? or symbol == sym) and
      ((name.nil? and match.nil?) or match.nil? or (
        (name == match) or
        (match.kind_of? Regexp and name =~ match)
        )
      )
    )
  end.collect{|x| x[-1]}
end

def get_namespace( prefix )

def get_namespace( prefix )
  return nil if @namespace_stack.empty?
  uris = (@namespace_stack.find_all { |ns| not ns[prefix].nil? }) ||
    (@namespace_stack.find { |ns| not ns[nil].nil? })
  uris[-1][prefix] unless uris.nil? or 0 == uris.size
end

def get_procs( symbol, name )

a helper
The following methods are duplicates, but it is faster than using
def get_procs( symbol, name )
  return nil if @procs.size == 0
  @procs.find_all do |sym, match, block|
    (
      (sym.nil? or symbol == sym) and
      ((name.nil? and match.nil?) or match.nil? or (
        (name == match) or
        (match.kind_of? Regexp and name =~ match)
        )
      )
    )
  end.collect{|x| x[-1]}
end

def handle( symbol, *arguments )

def handle( symbol, *arguments )
  tag = @tag_stack[-1]
  procs = get_procs( symbol, tag )
  listeners = get_listeners( symbol, tag )
  # notify observers
  procs.each { |ob| ob.call( *arguments ) } if procs
  listeners.each { |l|
    l.send( symbol.to_s, *arguments )
  } if listeners
end

def handle_entitydecl( event )

def handle_entitydecl( event )
  @entities[ event[1] ] = event[2] if event.size == 3
  parameter_reference_p = false
  case event[2]
  when "SYSTEM"
    if event.size == 5
      if event.last == "%"
        parameter_reference_p = true
      else
        event[4, 0] = "NDATA"
      end
    end
  when "PUBLIC"
    if event.size == 6
      if event.last == "%"
        parameter_reference_p = true
      else
        event[5, 0] = "NDATA"
      end
    end
  else
    parameter_reference_p = (event.size == 4)
  end
  event[1, 0] = event.pop if parameter_reference_p
  handle( event[0], event[1..-1] )
end

def initialize source

def initialize source
  @parser = BaseParser.new(source)
  @listeners = []
  @procs = []
  @namespace_stack = []
  @has_listeners = false
  @tag_stack = []
  @entities = {}
end

def listen( *args, &blok )

See the SAX2Listener for more information.
be, where the method name is the same as the matched Symbol.
Block will be passed the same arguments as a SAX2Listener method would

Listener must implement the methods in SAX2Listener

against fully qualified element names.
Array contains regular expressions or strings which will be matched

stream position.
This will be called for every event generated, passing in the current
There is an additional symbol that can be listened for: :progress.

:entitydecl, :notationdecl, :cdata, :xmldecl, :comment
:processing_instruction, :doctype, :attlistdecl, :elementdecl,
:start_prefix_mapping, :end_prefix_mapping, :characters,
Symbol can be one of: :start_element, :end_element,

Listen to All events
Listener
Listen to :start_element events on Array elements
Array, Block
Listen to all events on Array elements
Array, Listener
Listen to Symbol events
Symbol, Block
Listen to Symbol events on Array elements
Symbol, Array, Block

Listen arguments:
def listen( *args, &blok )
  if args[0].kind_of? Symbol
    if args.size == 2
      args[1].each { |match| @procs << [args[0], match, blok] }
    else
      add( [args[0], nil, blok] )
    end
  elsif args[0].kind_of? Array
    if args.size == 2
      args[0].each { |match| add( [nil, match, args[1]] ) }
    else
      args[0].each { |match| add( [ :start_element, match, blok ] ) }
    end
  else
    add([nil, nil, args[0]])
  end
end

def parse

def parse
  @procs.each { |sym,match,block| block.call if sym == :start_document }
  @listeners.each { |sym,match,block|
    block.start_document if sym == :start_document or sym.nil?
  }
  context = []
  while true
    event = @parser.pull
    case event[0]
    when :end_document
      handle( :end_document )
      break
    when :start_doctype
      handle( :doctype, *event[1..-1])
    when :end_doctype
      context = context[1]
    when :start_element
      @tag_stack.push(event[1])
      # find the observers for namespaces
      procs = get_procs( :start_prefix_mapping, event[1] )
      listeners = get_listeners( :start_prefix_mapping, event[1] )
      if procs or listeners
        # break out the namespace declarations
        # The attributes live in event[2]
        event[2].each {|n, v| event[2][n] = @parser.normalize(v)}
        nsdecl = event[2].find_all { |n, value| n =~ /^xmlns(:|$)/ }
        nsdecl.collect! { |n, value| [ n[6..-1], value ] }
        @namespace_stack.push({})
        nsdecl.each do |n,v|
          @namespace_stack[-1][n] = v
          # notify observers of namespaces
          procs.each { |ob| ob.call( n, v ) } if procs
          listeners.each { |ob| ob.start_prefix_mapping(n, v) } if listeners
        end
      end
      event[1] =~ Namespace::NAMESPLIT
      prefix = $1
      local = $2
      uri = get_namespace(prefix)
      # find the observers for start_element
      procs = get_procs( :start_element, event[1] )
      listeners = get_listeners( :start_element, event[1] )
      # notify observers
      procs.each { |ob| ob.call( uri, local, event[1], event[2] ) } if procs
      listeners.each { |ob|
        ob.start_element( uri, local, event[1], event[2] )
      } if listeners
    when :end_element
      @tag_stack.pop
      event[1] =~ Namespace::NAMESPLIT
      prefix = $1
      local = $2
      uri = get_namespace(prefix)
      # find the observers for start_element
      procs = get_procs( :end_element, event[1] )
      listeners = get_listeners( :end_element, event[1] )
      # notify observers
      procs.each { |ob| ob.call( uri, local, event[1] ) } if procs
      listeners.each { |ob|
        ob.end_element( uri, local, event[1] )
      } if listeners
      namespace_mapping = @namespace_stack.pop
      # find the observers for namespaces
      procs = get_procs( :end_prefix_mapping, event[1] )
      listeners = get_listeners( :end_prefix_mapping, event[1] )
      if procs or listeners
        namespace_mapping.each do |ns_prefix, ns_uri|
          # notify observers of namespaces
          procs.each { |ob| ob.call( ns_prefix ) } if procs
          listeners.each { |ob| ob.end_prefix_mapping(ns_prefix) } if listeners
        end
      end
    when :text
      unnormalized = @parser.unnormalize( event[1], @entities )
      handle( :characters, unnormalized )
    when :entitydecl
      handle_entitydecl( event )
    when :processing_instruction, :comment, :attlistdecl,
      :elementdecl, :cdata, :notationdecl, :xmldecl
      handle( *event )
    end
    handle( :progress, @parser.position )
  end
end

def source

def source
  @parser.source
end