1 require "rexml/validation/validation"
2 require "rexml/parsers/baseparser"
38 EMPTY = Event.new( nil )
39 TEXT = [:start_element, "text"]
40 attr_accessor :current
42 attr_reader :references
46 parser = REXML::Parsers::BaseParser.new( source )
50 @root = @current = Sequence.new(self)
59 when "element", "attribute", "text", "value"
62 states << Optional.new( self )
63 states[-2] << states[-1]
65 states << Choice.new( self )
66 states[-2] << states[-1]
68 states << OneOrMore.new( self )
69 states[-2] << states[-1]
71 states << ZeroOrMore.new( self )
72 states[-2] << states[-1]
74 states << Sequence.new( self )
75 states[-2] << states[-1]
77 states << Interleave.new( self )
78 states[-2] << states[-1]
80 states << Interleave.new( self )
81 states[-2] << states[-1]
84 states << [ event[2]["name"] ]
86 states[-1] << Ref.new( event[2]["name"] )
88 states << AnyName.new( self )
89 states[-2] << states[-1]
103 when "element", "attribute"
105 when "zeroOrMore", "oneOrMore", "choice", "optional",
106 "interleave", "group", "mixed"
110 @references[ ref.shift ] = ref
118 end while event[0] != :end_document
127 def initialize( context )
131 @count = context.count += 1
132 @references = context.references
137 return if @current == 0
139 @events.each {|s| s.reset if s.kind_of? State }
142 def previous=( previous )
143 @previous << previous
147 #print "In next with #{event.inspect}. "
148 #puts "Next (#@current) is #{@events[@current]}"
150 return @previous.pop.next( event ) if @events[@current].nil?
151 expand_ref_in( @events, @current ) if @events[@current].class == Ref
152 if ( @events[@current].kind_of? State )
154 @events[@current-1].previous = self
155 return @events[@current-1].next( event )
157 #puts "Current isn't a state"
158 if ( @events[@current].matches?(event) )
160 if @events[@current].nil?
161 #puts "#{inspect[0,5]} 1RETURNING #{@previous.inspect[0,5]}"
163 elsif @events[@current].kind_of? State
165 #puts "#{inspect[0,5]} 2RETURNING (#{@current-1}) #{@events[@current-1].inspect[0,5]}; on return, next is #{@events[@current]}"
166 @events[@current-1].previous = self
167 return @events[@current-1]
169 #puts "#{inspect[0,5]} RETURNING self w/ next(#@current) = #{@events[@current]}"
179 self.class.name =~ /(?:::)(\w)\w+$/
181 #self.class.name =~ /(?:::)(\w+)$/
186 "< #{to_s} #{@events.collect{|e|
187 pre = e == @events[@current] ? '#' : ''
188 pre + e.inspect unless self == e
193 return [@events[@current]]
197 add_event_to_arry( @events, event )
202 def expand_ref_in( arry, ind )
204 @references[ arry[ind].to_s ].each{ |evt|
205 add_event_to_arry(new_events,evt)
207 arry[ind,1] = new_events
210 def add_event_to_arry( arry, evt )
211 evt = generate_event( evt )
212 if evt.kind_of? String
213 arry[-1].event_arg = evt if arry[-1].kind_of? Event and @value
220 def generate_event( event )
221 return event if event.kind_of? State or event.class == Ref
229 arg = event[2]["name"]
231 evt = :start_attribute
232 arg = event[2]["name"]
242 return Event.new( event[0] )
243 else # then :end_element
251 return Event.new( evt, arg )
256 class Sequence < State
258 @events[@current].matches?( event )
263 class Optional < State
268 @prior = @previous.pop
269 return @prior.next( event )
275 @events[@current].matches?(event) ||
276 (@current == 0 and @previous[-1].matches?(event))
280 return [ @prior.expected, @events[0] ].flatten if @current == 0
281 return [@events[@current]]
286 class ZeroOrMore < Optional
288 expand_ref_in( @events, @current ) if @events[@current].class == Ref
289 if ( @events[@current].matches?(event) )
291 if @events[@current].nil?
294 elsif @events[@current].kind_of? State
296 @events[@current-1].previous = self
297 return @events[@current-1]
302 @prior = @previous.pop
303 return @prior.next( event ) if @current == 0
309 return [ @prior.expected, @events[0] ].flatten if @current == 0
310 return [@events[@current]]
315 class OneOrMore < State
316 def initialize context
327 expand_ref_in( @events, @current ) if @events[@current].class == Ref
328 if ( @events[@current].matches?(event) )
331 if @events[@current].nil?
334 elsif @events[@current].kind_of? State
336 @events[@current-1].previous = self
337 return @events[@current-1]
342 return @previous.pop.next( event ) if @current == 0 and @ord > 0
347 def matches?( event )
348 @events[@current].matches?(event) ||
349 (@current == 0 and @ord > 0 and @previous[-1].matches?(event))
353 if @current == 0 and @ord > 0
354 return [@previous[-1].expected, @events[0]].flatten
356 return [@events[@current]]
363 def initialize context
371 @choices.each { |c| c.each { |s| s.reset if s.kind_of? State } }
375 add_event_to_arry( @choices, event )
379 # Make the choice if we haven't
381 c = 0 ; max = @choices.size
383 if @choices[c][0].class == Ref
384 expand_ref_in( @choices[c], 0 )
385 @choices += @choices[c]
386 @choices.delete( @choices[c] )
392 @events = @choices.find { |evt| evt[0].matches? event }
393 # Remove the references
396 #puts "In next with #{event.inspect}."
397 #puts "events is #{@events.inspect}"
402 #puts "current = #@current"
406 def matches?( event )
407 return @events[@current].matches?( event ) if @events.size > 0
408 !@choices.find{|evt| evt[0].matches?(event)}.nil?
412 #puts "IN CHOICE EXPECTED"
413 #puts "EVENTS = #{@events.inspect}"
414 return [@events[@current]] if @events.size > 0
415 return @choices.collect do |x|
416 if x[0].kind_of? State
425 "< #{to_s} #{@choices.collect{|e| e.collect{|f|f.to_s}.join(', ')}.join(' or ')} >"
429 def add_event_to_arry( arry, evt )
430 if evt.kind_of? State or evt.class == Ref
432 elsif evt[0] == :text
434 arry[-1][-1].kind_of?( Event ) and
435 arry[-1][-1].event_type == :text and @value
437 arry[-1][-1].event_arg = evt[1]
441 arry << [] if evt[0] == :start_element
442 arry[-1] << generate_event( evt )
448 class Interleave < Choice
449 def initialize context
458 def next_current( event )
460 c = 0 ; max = @choices.size
462 if @choices[c][0].class == Ref
463 expand_ref_in( @choices[c], 0 )
464 @choices += @choices[c]
465 @choices.delete( @choices[c] )
471 @events = @choices[@choice..-1].find { |evt| evt[0].matches? event }
474 # reorder the choices
475 old = @choices[@choice]
476 idx = @choices.index( @events )
477 @choices[@choice] = @events
482 #puts "In next with #{event.inspect}."
483 #puts "events is #{@events.inspect}"
484 @events = [] unless @events
489 # Find the next series
490 next_current(event) unless @events[@current]
491 return nil unless @events[@current]
493 expand_ref_in( @events, @current ) if @events[@current].class == Ref
494 #puts "In next with #{event.inspect}."
495 #puts "Next (#@current) is #{@events[@current]}"
496 if ( @events[@current].kind_of? State )
498 @events[@current-1].previous = self
499 return @events[@current-1].next( event )
501 #puts "Current isn't a state"
502 return @previous.pop.next( event ) if @events[@current].nil?
503 if ( @events[@current].matches?(event) )
505 if @events[@current].nil?
506 #puts "#{inspect[0,5]} 1RETURNING self" unless @choices[@choice].nil?
507 return self unless @choices[@choice].nil?
508 #puts "#{inspect[0,5]} 1RETURNING #{@previous[-1].inspect[0,5]}"
510 elsif @events[@current].kind_of? State
512 #puts "#{inspect[0,5]} 2RETURNING (#{@current-1}) #{@events[@current-1].inspect[0,5]}; on return, next is #{@events[@current]}"
513 @events[@current-1].previous = self
514 return @events[@current-1]
516 #puts "#{inspect[0,5]} RETURNING self w/ next(#@current) = #{@events[@current]}"
524 def matches?( event )
525 return @events[@current].matches?( event ) if @events[@current]
526 !@choices[@choice..-1].find{|evt| evt[0].matches?(event)}.nil?
530 #puts "IN CHOICE EXPECTED"
531 #puts "EVENTS = #{@events.inspect}"
532 return [@events[@current]] if @events[@current]
533 return @choices[@choice..-1].collect do |x|
534 if x[0].kind_of? State
543 "< #{to_s} #{@choices.collect{|e| e.collect{|f|f.to_s}.join(', ')}.join(' and ')} >"