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