class REXML::Parsers::SAX2Parser - rexml: Ruby Standard Library Documentation

# class REXML::Parsers::SAX2Parser

`SAX2Parser`

### Public Class Methods

new(source) click to toggle source

    # File rexml-3.4.0/lib/rexml/parsers/sax2parser.rb, line 11
    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 rexml-3.4.0/lib/rexml/parsers/sax2parser.rb, line 37
    def add_listener( listener )
      @parser.add_listener( listener )
    end

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

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

entity_expansion_count() click to toggle source

    # File rexml-3.4.0/lib/rexml/parsers/sax2parser.rb, line 25
    def entity_expansion_count
      @parser.entity_expansion_count
    end

entity_expansion_limit=( limit ) click to toggle source

    # File rexml-3.4.0/lib/rexml/parsers/sax2parser.rb, line 29
    def entity_expansion_limit=( limit )
      @parser.entity_expansion_limit = limit
    end

entity_expansion_text_limit=( limit ) click to toggle source

    # File rexml-3.4.0/lib/rexml/parsers/sax2parser.rb, line 33
    def entity_expansion_text_limit=( limit )
      @parser.entity_expansion_text_limit = limit
    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 rexml-3.4.0/lib/rexml/parsers/sax2parser.rb, line 71
    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 rexml-3.4.0/lib/rexml/parsers/sax2parser.rb, line 98
    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

source() click to toggle source

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

### Private Instance Methods

add( pair ) click to toggle source

    # File rexml-3.4.0/lib/rexml/parsers/sax2parser.rb, line 252
    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 rexml-3.4.0/lib/rexml/parsers/sax2parser.rb, line 238
    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 rexml-3.4.0/lib/rexml/parsers/sax2parser.rb, line 261
    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

get_procs( symbol, name ) click to toggle source

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

    # File rexml-3.4.0/lib/rexml/parsers/sax2parser.rb, line 225
    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 rexml-3.4.0/lib/rexml/parsers/sax2parser.rb, line 185
    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 rexml-3.4.0/lib/rexml/parsers/sax2parser.rb, line 196
    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