# class REXML::Parsers::SAX2Parser

SAX2Parser

### Public Class Methods

new(source) click to toggle source

    # File lib/rexml/parsers/sax2parser.rb, line 10
    def initialize source
      @parser = BaseParser.new(source)
      @listeners = []
      @procs = []
      @namespace_stack = []
      @has_listeners = false
      @tag_stack = []
      @entities = {}
    end

### Public Instance Methods

add_listener( listener ) click to toggle source

    # File lib/rexml/parsers/sax2parser.rb, line 24
    def add_listener( listener )
      @parser.add_listener( listener )
    end

deafen( listener=nil, &blok ) click to toggle source

    # File lib/rexml/parsers/sax2parser.rb, line 76
    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

listen( *args, &blok ) click to toggle source

Listen arguments:

Symbol, Array, Block

    Listen to Symbol events on Array elements

Symbol, Block

    Listen to Symbol events

Array, Listener

    Listen to all events on Array elements

Array, Block

    Listen to :start_element events on Array elements

Listener

    Listen to All events

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

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

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

Listener must implement the methods in SAX2Listener

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

    # File lib/rexml/parsers/sax2parser.rb, line 58
    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

parse() click to toggle source

    # File lib/rexml/parsers/sax2parser.rb, line 85
    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
          #normalized = @parser.normalize( event[1] )
          #handle( :characters, normalized )
          copy = event[1].clone

          esub = proc { |match|
            if @entities.has_key?($1)
              @entities[$1].gsub(Text::REFERENCE, &esub)
            else
              match
            end
          }

          copy.gsub!( Text::REFERENCE, &esub )
          copy.gsub!( Text::NUMERICENTITY ) {|m|
            m=$1
            m = "0#{m}" if m[0] == ?x
            [Integer(m)].pack('U*')
          }
          handle( :characters, copy )
        when :entitydecl
          handle_entitydecl( event )
        when :processing_instruction, :comment, :attlistdecl,
          :elementdecl, :cdata, :notationdecl, :xmldecl
          handle( *event )
        end
        handle( :progress, @parser.position )
      end
    end

source() click to toggle source

    # File lib/rexml/parsers/sax2parser.rb, line 20
    def source
      @parser.source
    end

### Private Instance Methods

add( pair ) click to toggle source

    # File lib/rexml/parsers/sax2parser.rb, line 256
    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

get_listeners( symbol, name ) click to toggle source

    # File lib/rexml/parsers/sax2parser.rb, line 242
    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

get_namespace( prefix ) click to toggle source

    # File lib/rexml/parsers/sax2parser.rb, line 265
    def get_namespace( prefix )
      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

get_procs( symbol, name ) click to toggle source

The following methods are duplicates, but it is faster than using a helper

    # File lib/rexml/parsers/sax2parser.rb, line 229
    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

handle( symbol, *arguments ) click to toggle source

    # File lib/rexml/parsers/sax2parser.rb, line 189
    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

handle_entitydecl( event ) click to toggle source

    # File lib/rexml/parsers/sax2parser.rb, line 200
    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