Imported File#ftype spec from rubyspecs.
[rbx.git] / lib / rexml / validation / relaxng.rb
blob969f51bc95e7061851965422704a028093ffa596
1 require "rexml/validation/validation"
2 require "rexml/parsers/baseparser"
4 module REXML
5   module Validation
6     # Implemented:
7     # * empty
8     # * element
9     # * attribute
10     # * text
11     # * optional
12     # * choice
13     # * oneOrMore
14     # * zeroOrMore
15     # * group
16     # * value
17     # * interleave
18     # * mixed
19     # * ref
20     # * grammar
21     # * start
22     # * define
23     #
24     # Not implemented:
25     # * data
26     # * param
27     # * include
28     # * externalRef
29     # * notAllowed
30     # * anyName
31     # * nsName
32     # * except
33     # * name
34     class RelaxNG
35       include Validator
37       INFINITY = 1.0 / 0.0
38       EMPTY = Event.new( nil )
39       TEXT = [:start_element, "text"]
40       attr_accessor :current
41       attr_accessor :count
42       attr_reader :references
44       # FIXME: Namespaces
45       def initialize source
46         parser = REXML::Parsers::BaseParser.new( source )
48         @count = 0
49         @references = {}
50         @root = @current = Sequence.new(self)
51         @root.previous = true
52         states = [ @current ]
53         begin
54           event = parser.pull
55           case event[0]
56           when :start_element
57             case event[1]
58             when "empty"
59             when "element", "attribute", "text", "value"
60               states[-1] << event
61             when "optional"
62               states << Optional.new( self )
63               states[-2] << states[-1]
64             when "choice"
65               states << Choice.new( self )
66               states[-2] << states[-1]
67             when "oneOrMore"
68               states << OneOrMore.new( self )
69               states[-2] << states[-1]
70             when "zeroOrMore"
71               states << ZeroOrMore.new( self )
72               states[-2] << states[-1]
73             when "group"
74               states << Sequence.new( self )
75               states[-2] << states[-1]
76             when "interleave"
77               states << Interleave.new( self )
78               states[-2] << states[-1]
79             when "mixed"
80               states << Interleave.new( self )
81               states[-2] << states[-1]
82               states[-1] << TEXT 
83             when "define"
84               states << [ event[2]["name"] ]
85             when "ref"
86               states[-1] << Ref.new( event[2]["name"] )
87             when "anyName"
88               states << AnyName.new( self )
89               states[-2] << states[-1]
90             when "nsName"
91             when "except"
92             when "name"
93             when "data"
94             when "param"
95             when "include"
96             when "grammar"
97             when "start"
98             when "externalRef"
99             when "notAllowed"
100             end
101           when :end_element
102             case event[1]
103             when "element", "attribute"
104               states[-1] << event
105             when "zeroOrMore", "oneOrMore", "choice", "optional", 
106               "interleave", "group", "mixed"
107               states.pop
108             when "define"
109               ref = states.pop
110               @references[ ref.shift ] = ref
111             #when "empty"
112             end
113           when :end_document
114             states[-1] << event
115           when :text
116             states[-1] << event
117           end
118         end while event[0] != :end_document
119       end
121       def receive event
122         validate( event )
123       end
124     end
126     class State
127       def initialize( context )
128         @previous = []
129         @events = []
130         @current = 0
131         @count = context.count += 1
132         @references = context.references
133         @value = false
134       end
136       def reset
137         return if @current == 0
138         @current = 0
139         @events.each {|s| s.reset if s.kind_of? State }
140       end
142       def previous=( previous ) 
143         @previous << previous
144       end
146       def next( event )
147         #print "In next with #{event.inspect}.  "
148         #puts "Next (#@current) is #{@events[@current]}"
149         #p @previous
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 )
153           @current += 1
154           @events[@current-1].previous = self
155           return @events[@current-1].next( event )
156         end
157         #puts "Current isn't a state"
158         if ( @events[@current].matches?(event) )
159           @current += 1
160           if @events[@current].nil?
161             #puts "#{inspect[0,5]} 1RETURNING #{@previous.inspect[0,5]}"
162             return @previous.pop
163           elsif @events[@current].kind_of? State
164             @current += 1
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]
168           else
169             #puts "#{inspect[0,5]} RETURNING self w/ next(#@current) = #{@events[@current]}"
170             return self
171           end
172         else
173           return nil
174         end
175       end
177       def to_s
178         # Abbreviated:
179         self.class.name =~ /(?:::)(\w)\w+$/
180         # Full:
181         #self.class.name =~ /(?:::)(\w+)$/
182         "#$1.#@count"
183       end
185       def inspect
186         "< #{to_s} #{@events.collect{|e| 
187           pre = e == @events[@current] ? '#' : ''
188           pre + e.inspect unless self == e
189         }.join(', ')} >"
190       end
192       def expected
193         return [@events[@current]]
194       end
196       def <<( event )
197         add_event_to_arry( @events, event )
198       end
201       protected
202       def expand_ref_in( arry, ind )
203         new_events = []
204         @references[ arry[ind].to_s ].each{ |evt| 
205           add_event_to_arry(new_events,evt)
206         }
207         arry[ind,1] = new_events
208       end
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
214           @value = false
215         else
216           arry << evt
217         end
218       end
220       def generate_event( event )
221         return event if event.kind_of? State or event.class == Ref
222         evt = nil
223         arg = nil
224         case event[0]
225         when :start_element
226           case event[1]
227           when "element"
228             evt = :start_element
229             arg = event[2]["name"]
230           when "attribute"
231             evt = :start_attribute
232             arg = event[2]["name"]
233           when "text"
234             evt = :text
235           when "value"
236             evt = :text
237             @value = true
238           end
239         when :text
240           return event[1]
241         when :end_document
242           return Event.new( event[0] )
243         else # then :end_element
244           case event[1]
245           when "element"
246             evt = :end_element
247           when "attribute"
248             evt = :end_attribute
249           end
250         end
251         return Event.new( evt, arg )
252       end
253     end
256     class Sequence < State
257       def matches?(event)
258         @events[@current].matches?( event )
259       end
260     end
263     class Optional < State
264       def next( event )
265         if @current == 0
266           rv = super
267           return rv if rv
268           @prior = @previous.pop
269           return @prior.next( event )
270         end
271         super
272       end
274       def matches?(event)
275         @events[@current].matches?(event) || 
276         (@current == 0 and @previous[-1].matches?(event))
277       end
279       def expected
280         return [ @prior.expected, @events[0] ].flatten if @current == 0
281         return [@events[@current]]
282       end
283     end
286     class ZeroOrMore < Optional
287       def next( event )
288         expand_ref_in( @events, @current ) if @events[@current].class == Ref
289         if ( @events[@current].matches?(event) )
290           @current += 1
291           if @events[@current].nil?
292             @current = 0
293             return self
294           elsif @events[@current].kind_of? State
295             @current += 1
296             @events[@current-1].previous = self
297             return @events[@current-1]
298           else
299             return self
300           end
301         else
302           @prior = @previous.pop
303           return @prior.next( event ) if @current == 0
304           return nil
305         end
306       end
308       def expected
309         return [ @prior.expected, @events[0] ].flatten if @current == 0
310         return [@events[@current]]
311       end
312     end
315     class OneOrMore < State
316       def initialize context
317         super
318         @ord = 0
319       end
321       def reset
322         super 
323         @ord = 0
324       end
326       def next( event )
327         expand_ref_in( @events, @current ) if @events[@current].class == Ref
328         if ( @events[@current].matches?(event) )
329           @current += 1
330           @ord += 1
331           if @events[@current].nil?
332             @current = 0
333             return self
334           elsif @events[@current].kind_of? State
335             @current += 1
336             @events[@current-1].previous = self
337             return @events[@current-1]
338           else
339             return self
340           end
341         else
342           return @previous.pop.next( event ) if @current == 0 and @ord > 0
343           return nil
344         end
345       end
347       def matches?( event )
348         @events[@current].matches?(event) || 
349         (@current == 0 and @ord > 0 and @previous[-1].matches?(event))
350       end
352       def expected
353         if @current == 0 and @ord > 0
354           return [@previous[-1].expected, @events[0]].flatten
355         else
356           return [@events[@current]]
357         end
358       end
359     end
362     class Choice < State
363       def initialize context
364         super
365         @choices = []
366       end
368       def reset
369         super
370         @events = []
371         @choices.each { |c| c.each { |s| s.reset if s.kind_of? State } }
372       end
374       def <<( event )
375         add_event_to_arry( @choices, event )
376       end
378       def next( event )
379         # Make the choice if we haven't
380         if @events.size == 0
381           c = 0 ; max = @choices.size
382           while c < max
383             if @choices[c][0].class == Ref
384               expand_ref_in( @choices[c], 0 )
385               @choices += @choices[c]
386               @choices.delete( @choices[c] )
387               max -= 1
388             else
389               c += 1
390             end
391           end
392           @events = @choices.find { |evt| evt[0].matches? event }
393           # Remove the references
394           # Find the events
395         end
396         #puts "In next with #{event.inspect}."
397         #puts "events is #{@events.inspect}"
398         unless @events
399           @events = []
400           return nil
401         end
402         #puts "current = #@current"
403         super
404       end
406       def matches?( event )
407         return @events[@current].matches?( event ) if @events.size > 0
408         !@choices.find{|evt| evt[0].matches?(event)}.nil?
409       end
411       def expected
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
417             x[0].expected
418           else
419             x[0]
420           end
421         end.flatten
422       end
424       def inspect
425         "< #{to_s} #{@choices.collect{|e| e.collect{|f|f.to_s}.join(', ')}.join(' or ')} >"
426       end
428       protected
429       def add_event_to_arry( arry, evt ) 
430         if evt.kind_of? State or evt.class == Ref
431           arry << [evt]
432         elsif evt[0] == :text 
433          if arry[-1] and
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]
438             @value = false
439           end
440         else
441           arry << [] if evt[0] == :start_element
442           arry[-1] << generate_event( evt )
443         end
444       end
445     end
448     class Interleave < Choice
449       def initialize context
450         super
451         @choice = 0
452       end
454       def reset
455         @choice = 0
456       end
458       def next_current( event )
459         # Expand references
460         c = 0 ; max = @choices.size
461         while c < max
462           if @choices[c][0].class == Ref
463             expand_ref_in( @choices[c], 0 )
464             @choices += @choices[c]
465             @choices.delete( @choices[c] )
466             max -= 1
467           else
468             c += 1
469           end
470         end
471         @events = @choices[@choice..-1].find { |evt| evt[0].matches? event }
472         @current = 0
473         if @events
474           # reorder the choices
475           old = @choices[@choice]
476           idx = @choices.index( @events )
477           @choices[@choice] = @events
478           @choices[idx] = old
479           @choice += 1
480         end
481         
482        #puts "In next with #{event.inspect}."
483        #puts "events is #{@events.inspect}"
484         @events = [] unless @events
485       end
488       def next( event )
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 )
497           @current += 1
498           @events[@current-1].previous = self
499           return @events[@current-1].next( event )
500         end
501        #puts "Current isn't a state"
502         return @previous.pop.next( event ) if @events[@current].nil?
503         if ( @events[@current].matches?(event) )
504           @current += 1
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]}"
509             return @previous.pop
510           elsif @events[@current].kind_of? State
511             @current += 1
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]
515           else
516            #puts "#{inspect[0,5]} RETURNING self w/ next(#@current) = #{@events[@current]}"
517             return self
518           end
519         else
520           return nil
521         end
522       end
524       def matches?( event )
525         return @events[@current].matches?( event ) if @events[@current]
526         !@choices[@choice..-1].find{|evt| evt[0].matches?(event)}.nil?
527       end
529       def expected
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
535             x[0].expected
536           else
537             x[0]
538           end
539         end.flatten
540       end
542       def inspect
543         "< #{to_s} #{@choices.collect{|e| e.collect{|f|f.to_s}.join(', ')}.join(' and ')} >"
544       end
545     end
547     class Ref
548       def initialize value
549         @value = value
550       end
551       def to_s
552         @value
553       end
554       def inspect
555         "{#{to_s}}"
556       end
557     end
558   end