| Class | REXML::Parsers::SAX2Parser |
| In: |
lib/rexml/parsers/sax2parser.rb
|
| Parent: | Object |
# File lib/rexml/parsers/sax2parser.rb, line 10
10: def initialize source
11: @parser = BaseParser.new(source)
12: @listeners = []
13: @procs = []
14: @namespace_stack = []
15: @has_listeners = false
16: @tag_stack = []
17: @entities = {}
18: end
# File lib/rexml/parsers/sax2parser.rb, line 24
24: def add_listener( listener )
25: @parser.add_listener( listener )
26: end
# File lib/rexml/parsers/sax2parser.rb, line 76
76: def deafen( listener=nil, &blok )
77: if listener
78: @listeners.delete_if {|item| item[-1] == listener }
79: @has_listeners = false if @listeners.size == 0
80: else
81: @procs.delete_if {|item| item[-1] == blok }
82: end
83: end
Listen arguments:
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
58: def listen( *args, &blok )
59: if args[0].kind_of? Symbol
60: if args.size == 2
61: args[1].each { |match| @procs << [args[0], match, blok] }
62: else
63: add( [args[0], nil, blok] )
64: end
65: elsif args[0].kind_of? Array
66: if args.size == 2
67: args[0].each { |match| add( [nil, match, args[1]] ) }
68: else
69: args[0].each { |match| add( [ :start_element, match, blok ] ) }
70: end
71: else
72: add([nil, nil, args[0]])
73: end
74: end
# File lib/rexml/parsers/sax2parser.rb, line 85
85: def parse
86: @procs.each { |sym,match,block| block.call if sym == :start_document }
87: @listeners.each { |sym,match,block|
88: block.start_document if sym == :start_document or sym.nil?
89: }
90: root = context = []
91: while true
92: event = @parser.pull
93: case event[0]
94: when :end_document
95: handle( :end_document )
96: break
97: when :start_doctype
98: handle( :doctype, *event[1..-1])
99: when :end_doctype
100: context = context[1]
101: when :start_element
102: @tag_stack.push(event[1])
103: # find the observers for namespaces
104: procs = get_procs( :start_prefix_mapping, event[1] )
105: listeners = get_listeners( :start_prefix_mapping, event[1] )
106: if procs or listeners
107: # break out the namespace declarations
108: # The attributes live in event[2]
109: event[2].each {|n, v| event[2][n] = @parser.normalize(v)}
110: nsdecl = event[2].find_all { |n, value| n =~ /^xmlns(:|$)/ }
111: nsdecl.collect! { |n, value| [ n[6..-1], value ] }
112: @namespace_stack.push({})
113: nsdecl.each do |n,v|
114: @namespace_stack[-1][n] = v
115: # notify observers of namespaces
116: procs.each { |ob| ob.call( n, v ) } if procs
117: listeners.each { |ob| ob.start_prefix_mapping(n, v) } if listeners
118: end
119: end
120: event[1] =~ Namespace::NAMESPLIT
121: prefix = $1
122: local = $2
123: uri = get_namespace(prefix)
124: # find the observers for start_element
125: procs = get_procs( :start_element, event[1] )
126: listeners = get_listeners( :start_element, event[1] )
127: # notify observers
128: procs.each { |ob| ob.call( uri, local, event[1], event[2] ) } if procs
129: listeners.each { |ob|
130: ob.start_element( uri, local, event[1], event[2] )
131: } if listeners
132: when :end_element
133: @tag_stack.pop
134: event[1] =~ Namespace::NAMESPLIT
135: prefix = $1
136: local = $2
137: uri = get_namespace(prefix)
138: # find the observers for start_element
139: procs = get_procs( :end_element, event[1] )
140: listeners = get_listeners( :end_element, event[1] )
141: # notify observers
142: procs.each { |ob| ob.call( uri, local, event[1] ) } if procs
143: listeners.each { |ob|
144: ob.end_element( uri, local, event[1] )
145: } if listeners
146:
147: namespace_mapping = @namespace_stack.pop
148: # find the observers for namespaces
149: procs = get_procs( :end_prefix_mapping, event[1] )
150: listeners = get_listeners( :end_prefix_mapping, event[1] )
151: if procs or listeners
152: namespace_mapping.each do |prefix, uri|
153: # notify observers of namespaces
154: procs.each { |ob| ob.call( prefix ) } if procs
155: listeners.each { |ob| ob.end_prefix_mapping(prefix) } if listeners
156: end
157: end
158: when :text
159: #normalized = @parser.normalize( event[1] )
160: #handle( :characters, normalized )
161: copy = event[1].clone
162: @entities.each { |key, value| copy = copy.gsub("&#{key};", value) }
163: copy.gsub!( Text::NUMERICENTITY ) {|m|
164: m=$1
165: m = "0#{m}" if m[0] == ?x
166: [Integer(m)].pack('U*')
167: }
168: handle( :characters, copy )
169: when :entitydecl
170: @entities[ event[1] ] = event[2] if event.size == 3
171: handle( *event )
172: when :processing_instruction, :comment, :attlistdecl,
173: :elementdecl, :cdata, :notationdecl, :xmldecl
174: handle( *event )
175: end
176: handle( :progress, @parser.position )
177: end
178: end
# File lib/rexml/parsers/sax2parser.rb, line 222
222: def add( pair )
223: if pair[-1].respond_to? :call
224: @procs << pair unless @procs.include? pair
225: else
226: @listeners << pair unless @listeners.include? pair
227: @has_listeners = true
228: end
229: end
# File lib/rexml/parsers/sax2parser.rb, line 208
208: def get_listeners( symbol, name )
209: return nil if @listeners.size == 0
210: @listeners.find_all do |sym, match, block|
211: (
212: (sym.nil? or symbol == sym) and
213: ((name.nil? and match.nil?) or match.nil? or (
214: (name == match) or
215: (match.kind_of? Regexp and name =~ match)
216: )
217: )
218: )
219: end.collect{|x| x[-1]}
220: end
# File lib/rexml/parsers/sax2parser.rb, line 231
231: def get_namespace( prefix )
232: uris = (@namespace_stack.find_all { |ns| not ns[prefix].nil? }) ||
233: (@namespace_stack.find { |ns| not ns[nil].nil? })
234: uris[-1][prefix] unless uris.nil? or 0 == uris.size
235: end
The following methods are duplicates, but it is faster than using a helper
# File lib/rexml/parsers/sax2parser.rb, line 194
194: def get_procs( symbol, name )
195: return nil if @procs.size == 0
196: @procs.find_all do |sym, match, block|
197: #puts sym.inspect+"=="+symbol.inspect+ "\t"+match.inspect+"=="+name.inspect+ "\t"+( (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)))).to_s
198: (
199: (sym.nil? or symbol == sym) and
200: ((name.nil? and match.nil?) or match.nil? or (
201: (name == match) or
202: (match.kind_of? Regexp and name =~ match)
203: )
204: )
205: )
206: end.collect{|x| x[-1]}
207: end
# File lib/rexml/parsers/sax2parser.rb, line 181
181: def handle( symbol, *arguments )
182: tag = @tag_stack[-1]
183: procs = get_procs( symbol, tag )
184: listeners = get_listeners( symbol, tag )
185: # notify observers
186: procs.each { |ob| ob.call( *arguments ) } if procs
187: listeners.each { |l|
188: l.send( symbol.to_s, *arguments )
189: } if listeners
190: end