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

#stackObject

Returns the value of attribute stack.



463
464
465
# File 'lib/rfm/utilities/sax_parser.rb', line 463

def stack
  @stack
end

#templateObject

Returns the value of attribute template.



463
464
465
# File 'lib/rfm/utilities/sax_parser.rb', line 463

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)



471
472
473
474
475
476
# File 'lib/rfm/utilities/sax_parser.rb', line 471

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.



500
501
502
503
504
505
# File 'lib/rfm/utilities/sax_parser.rb', line 500

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.



488
489
490
491
492
493
494
495
496
497
# File 'lib/rfm/utilities/sax_parser.rb', line 488

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.



625
626
627
628
629
630
# File 'lib/rfm/utilities/sax_parser.rb', line 625

def _attribute(name, value, *args)
	#puts "Receiving attribute '#{name}' with value '#{value}'"
	name = transform name
	new_att = default_class.new.tap{|att| att[name]=value}
	@element_buffer[:attributes].merge!(new_att)
end

#_doctype(*args) ⇒ Object



647
648
649
650
651
# File 'lib/rfm/utilities/sax_parser.rb', line 647

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.



640
641
642
643
644
645
# File 'lib/rfm/utilities/sax_parser.rb', line 640

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

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

Add a node to an existing element.



609
610
611
612
613
614
615
616
617
618
619
620
621
622
# File 'lib/rfm/utilities/sax_parser.rb', line 609

def _start_element(tag, attributes=nil, *args)
	#puts ["_START_ELEMENT", tag, attributes, args].to_yaml # if tag.to_s.downcase=='fmrestulset'
	tag = transform tag
	send_element_buffer if element_buffer?
	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
	@element_buffer.merge!({:tag=>tag, :attributes => attributes || default_class.new})
end

#_text(value, *args) ⇒ Object

Add ‘content’ attribute to existing element.



633
634
635
636
637
# File 'lib/rfm/utilities/sax_parser.rb', line 633

def _text(value, *args)
	#puts "Receiving text '#{value}'"
	return unless value.to_s[/[^\s]/]
  @element_buffer[:text] << value
end

#cursorObject



562
563
564
# File 'lib/rfm/utilities/sax_parser.rb', line 562

def cursor
	stack.last
end

#dump_cursorObject



577
578
579
# File 'lib/rfm/utilities/sax_parser.rb', line 577

def dump_cursor
	stack.pop
end

#element_buffer?Boolean

Returns:

  • (Boolean)


603
604
605
# File 'lib/rfm/utilities/sax_parser.rb', line 603

def element_buffer?
  @element_buffer[:tag] && !@element_buffer[:tag].empty?
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.



532
533
534
535
536
537
538
539
540
541
542
# File 'lib/rfm/utilities/sax_parser.rb', line 532

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

#init_element_bufferObject



591
592
593
# File 'lib/rfm/utilities/sax_parser.rb', line 591

def init_element_buffer
   @element_buffer = {:tag=>nil, :attributes=>default_class.new, :text=>''}
end

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

Instance Methods ###



511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
# File 'lib/rfm/utilities/sax_parser.rb', line 511

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
	#initial_object = initial_object || default_class.new || {}
	@stack = []
	@template = get_template(_template)
	@tag_translation = tag_translation
	#(@template = @template.values[0]) if @template.size == 1
	#y @template
	init_element_buffer
  set_cursor Cursor.new(@template, initial_object, 'top', self)
end

#load_template(dat) ⇒ Object

Does the heavy-lifting of template retrieval.



545
546
547
548
549
550
551
552
553
554
555
556
# File 'lib/rfm/utilities/sax_parser.rb', line 545

def load_template(dat)
	prefix = defined?(template_prefix) ? template_prefix : ''
 	rslt = case
 		when dat.is_a?(Hash); 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
end

#resultObject



558
559
560
# File 'lib/rfm/utilities/sax_parser.rb', line 558

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

#send_element_bufferObject



595
596
597
598
599
600
601
# File 'lib/rfm/utilities/sax_parser.rb', line 595

def send_element_buffer
 if element_buffer?
 	(@element_buffer[:attributes][text_label] = @element_buffer[:text]) if @element_buffer[:text].to_s[/[^\s]/]
     set_cursor cursor.receive_start_element(@element_buffer[:tag], @element_buffer[:attributes])
     init_element_buffer
  end
end

#set_cursor(args) ⇒ Object

cursor_object



566
567
568
569
570
571
572
573
574
575
# File 'lib/rfm/utilities/sax_parser.rb', line 566

def set_cursor(args) # cursor_object
	if args.is_a? Cursor
		stack.push(args)
		cursor.parent = stack[-2] || stack[0] #_stack[0] so methods called on parent won't bomb.
		# Cursor is no longer storing top or stack, it is delegating those mehthods to main handler.
		#cursor.top = stack[0]
		#cursor.stack = stack
	end
	cursor
end

#topObject



581
582
583
# File 'lib/rfm/utilities/sax_parser.rb', line 581

def top
	stack[0]
end

#transform(name) ⇒ Object



585
586
587
588
589
# File 'lib/rfm/utilities/sax_parser.rb', line 585

def transform(name)
	return name unless @tag_translation.is_a?(Proc)
	#name.to_s.gsub(*@tag_translation)
	@tag_translation.call(name.to_s)
end