Module: Rfm::SaxParser::Handler

Defined in:
lib/rfm/utilities/sax_parser.rb

Overview

A handler instance is created for each parsing run. The handler has several important functions:

  1. Receive callbacks from the sax/stream parsing engine (start_element, end_element, attribute…).

  2. Maintain a stack of cursors, growing & shrinking, throughout the parsing run.

  3. Maintain a Cursor instance throughout the parsing run.

  4. Hand over parser callbacks & data to the Cursor instance for refined processing.

The handler instance is unique to each different parsing gem but inherits generic methods from this Handler module. During each parsing run, the Hander module creates a new instance of the spcified parer’s handler class and runs the handler’s main parsing method. At the end of the parsing run the handler instance, along with it’s newly parsed object, is returned to the object that originally called for the parsing run (your script/app/whatever).

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#initial_objectObject

Returns the value of attribute initial_object.



622
623
624
# File 'lib/rfm/utilities/sax_parser.rb', line 622

def initial_object
  @initial_object
end

#stackObject

Returns the value of attribute stack.



622
623
624
# File 'lib/rfm/utilities/sax_parser.rb', line 622

def stack
  @stack
end

#stack_debugObject

Returns the value of attribute stack_debug.



622
623
624
# File 'lib/rfm/utilities/sax_parser.rb', line 622

def stack_debug
  @stack_debug
end

#templateObject

Returns the value of attribute template.



622
623
624
# File 'lib/rfm/utilities/sax_parser.rb', line 622

def template
  @template
end

Class Method Details

.build(io, template = nil, initial_object = nil, parser = nil, options = {}) ⇒ Object

Main parsing interface (also aliased at SaxParser.parse)



630
631
632
633
634
635
# File 'lib/rfm/utilities/sax_parser.rb', line 630

def self.build(io, template=nil, initial_object=nil, parser=nil, options={})
  parser = parser || options[:parser] || BACKEND
  parser = get_backend(parser)
  (Rfm.log.info "Using backend parser: #{parser}, with template: #{template}") if options[:log_parser]
  parser.build(io, template, initial_object)
end

.decide_backendObject

Finds a loadable backend and returns its symbol.



659
660
661
662
663
664
# File 'lib/rfm/utilities/sax_parser.rb', line 659

def self.decide_backend
  #BACKENDS.find{|b| !Gem::Specification::find_all_by_name(b[1]).empty? || b[0]==:rexml}[0]
  PARSERS.find{|k,v| !Gem::Specification::find_all_by_name(v[:file]).empty? || k == :rexml}[0]
rescue
  raise "The xml parser could not find a loadable backend library: #{$!}"
end

.get_backend(parser = BACKEND) ⇒ Object

Takes backend symbol and returns custom Handler class for specified backend.



647
648
649
650
651
652
653
654
655
656
# File 'lib/rfm/utilities/sax_parser.rb', line 647

def self.get_backend(parser=BACKEND)
  (parser = decide_backend) unless parser
  if parser.is_a?(String) || parser.is_a?(Symbol)
    parser_proc = PARSERS[parser.to_sym][:proc]
    parser_proc.call unless parser_proc.nil? || const_defined?((parser.to_s.capitalize + 'Handler').to_sym)
    SaxParser.const_get(parser.to_s.capitalize + "Handler")
  end
rescue
  raise "Could not load the backend parser '#{parser}': #{$!}"
end

Instance Method Details

#_attribute(name, value, *args) ⇒ Object

Add attribute to existing element.



763
764
765
766
767
# File 'lib/rfm/utilities/sax_parser.rb', line 763

def _attribute(name, value, *args)
  #puts "Receiving attribute '#{name}' with value '#{value}'"
  name = transform name
  cursor.receive_attribute(name, value)
end

#_doctype(*args) ⇒ Object



789
790
791
792
793
# File 'lib/rfm/utilities/sax_parser.rb', line 789

def _doctype(*args)
  (args = args[0].gsub(/"/, '').split) if args.size ==1
  _start_element('doctype', :value=>args)
  _end_element('doctype')
end

#_end_element(tag, *args) ⇒ Object

Close out an existing element.



783
784
785
786
787
# File 'lib/rfm/utilities/sax_parser.rb', line 783

def _end_element(tag, *args)
  tag = transform tag
  #puts "Receiving end_element '#{tag}'"
  cursor.receive_end_element(tag) and dump_cursor
end

#_start_element(tag, attributes = nil, *args) ⇒ Object

Add a node to an existing element.



748
749
750
751
752
753
754
755
756
757
758
759
760
# File 'lib/rfm/utilities/sax_parser.rb', line 748

def _start_element(tag, attributes=nil, *args)
  #puts ["_START_ELEMENT", tag, attributes, args].to_yaml # if tag.to_s.downcase=='fmrestulset'
  tag = transform tag
  if attributes
    # This crazy thing transforms attribute keys to underscore (or whatever).
    #attributes = default_class[*attributes.collect{|k,v| [transform(k),v] }.flatten]
    # This works but downcases all attribute names - not good.
    attributes = DEFAULT_CLASS.new.tap {|hash| attributes.each {|k, v| hash[transform(k)] = v}}
    # This doesn't work yet, but at least it wont downcase hash keys.
    #attributes = Hash.new.tap {|hash| attributes.each {|k, v| hash[transform(k)] = v}}
  end
  set_cursor cursor.receive_start_element(tag, attributes)
end

#_text(value, *args) ⇒ Object

Add ‘content’ attribute to existing element.



770
771
772
773
774
775
776
777
778
779
780
# File 'lib/rfm/utilities/sax_parser.rb', line 770

def _text(value, *args)
  #puts "Receiving text '#{value}'"
  #puts RUBY_VERSION_NUM
  if RUBY_VERSION_NUM > 1.8 && value.is_a?(String)
    #puts "Forcing utf-8"
    value.force_encoding('UTF-8')
  end
  # I think the reason this was here is no longer relevant, so I'm disabeling.
  return unless value[/[^\s]/]
  cursor.receive_attribute(TEXT_LABEL, value)
end

#cursorObject



722
723
724
# File 'lib/rfm/utilities/sax_parser.rb', line 722

def cursor
  stack.last
end

#dump_cursorObject



734
735
736
# File 'lib/rfm/utilities/sax_parser.rb', line 734

def dump_cursor
  stack.pop
end

#get_template(name) ⇒ Object

Takes string, symbol, hash, and returns a (possibly cached) parsing template. String can be a file name, yaml, xml. Symbol is a name of a template stored in SaxParser@templates (you would set the templates when your app or gem loads). Templates stored in the SaxParser@templates var can be strings of code, file specs, or hashes.



687
688
689
690
691
692
693
694
695
696
697
# File 'lib/rfm/utilities/sax_parser.rb', line 687

def get_template(name)
  #   dat = templates[name]
  #   if dat
  #     rslt = load_template(dat)
  #   else
  #     rslt = load_template(name)
  #   end
  #   (templates[name] = rslt) #unless dat == rslt
  # The above works, but this is cleaner.
  TEMPLATES[name] = TEMPLATES[name] && load_template(TEMPLATES[name]) || load_template(name)
end

#initialize(_template = nil, _initial_object = nil) ⇒ Object

Instance Methods ###



670
671
672
673
674
675
676
677
678
679
680
681
# File 'lib/rfm/utilities/sax_parser.rb', line 670

def initialize(_template=nil, _initial_object=nil)
  @initial_object = case
                    when _initial_object.nil?; DEFAULT_CLASS.new
                    when _initial_object.is_a?(Class); _initial_object.new
                    when _initial_object.is_a?(String) || _initial_object.is_a?(Symbol); SaxParser.get_constant(_initial_object).new
                    else _initial_object
                    end
  @stack = []
  @stack_debug=[]
  @template = get_template(_template)
  set_cursor Cursor.new('__TOP__', self).process_new_element
end

#load_template(dat) ⇒ Object

Does the heavy-lifting of template retrieval.



700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
# File 'lib/rfm/utilities/sax_parser.rb', line 700

def load_template(dat)
  #puts "DAT: #{dat}, class #{dat.class}"
  prefix = defined?(TEMPLATE_PREFIX) ? TEMPLATE_PREFIX : ''
  #puts "SaxParser::Handler#load_template... 'prefix' is #{prefix}"
  rslt = case
         when dat.is_a?(Hash); dat
         when (dat.is_a?(String) && dat[/^\//]); YAML.load_file dat
         when dat.to_s[/\.y.?ml$/i]; (YAML.load_file(File.join(*[prefix, dat].compact)))
           # This line might cause an infinite loop.
         when dat.to_s[/\.xml$/i]; self.class.build(File.join(*[prefix, dat].compact), nil, {'compact'=>true})
         when dat.to_s[/^<.*>/i]; "Convert from xml to Hash - under construction"
         when dat.is_a?(String); YAML.load dat
         else DEFAULT_CLASS.new
         end
  #puts rslt
  rslt
end

#resultObject



718
719
720
# File 'lib/rfm/utilities/sax_parser.rb', line 718

def result
  stack[0].object if stack[0].is_a? Cursor
end

#set_cursor(args) ⇒ Object

cursor_object



726
727
728
729
730
731
732
# File 'lib/rfm/utilities/sax_parser.rb', line 726

def set_cursor(args) # cursor_object
  if args.is_a? Cursor
    stack.push(args)
    #@stack_debug.push(args.dup.tap(){|c| c.handler = c.handler.object_id; c.parent = c.parent.tag})
  end
  cursor
end

#topObject



738
739
740
# File 'lib/rfm/utilities/sax_parser.rb', line 738

def top
  stack[0]
end

#transform(name) ⇒ Object



742
743
744
745
# File 'lib/rfm/utilities/sax_parser.rb', line 742

def transform(name)
  return name unless TAG_TRANSLATION.is_a?(Proc)
  TAG_TRANSLATION.call(name.to_s)
end