1require "rexml/validation/validation"
2require "rexml/parsers/baseparser"
3
4module 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
36
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
43
44      # FIXME: Namespaces
45      def initialize source
46        parser = REXML::Parsers::BaseParser.new( source )
47
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
120
121      def receive event
122        validate( event )
123      end
124    end
125
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
135
136      def reset
137        return if @current == 0
138        @current = 0
139        @events.each {|s| s.reset if s.kind_of? State }
140      end
141
142      def previous=( previous )
143        @previous << previous
144      end
145
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
176
177      def to_s
178        # Abbreviated:
179        self.class.name =~ /(?:::)(\w)\w+$/
180        # Full:
181        #self.class.name =~ /(?:::)(\w+)$/
182        "#$1.#@count"
183      end
184
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
191
192      def expected
193        return [@events[@current]]
194      end
195
196      def <<( event )
197        add_event_to_arry( @events, event )
198      end
199
200
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
209
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
219
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
254
255
256    class Sequence < State
257      def matches?(event)
258        @events[@current].matches?( event )
259      end
260    end
261
262
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
273
274      def matches?(event)
275        @events[@current].matches?(event) ||
276        (@current == 0 and @previous[-1].matches?(event))
277      end
278
279      def expected
280        return [ @prior.expected, @events[0] ].flatten if @current == 0
281        return [@events[@current]]
282      end
283    end
284
285
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
307
308      def expected
309        return [ @prior.expected, @events[0] ].flatten if @current == 0
310        return [@events[@current]]
311      end
312    end
313
314
315    class OneOrMore < State
316      def initialize context
317        super
318        @ord = 0
319      end
320
321      def reset
322        super
323        @ord = 0
324      end
325
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
346
347      def matches?( event )
348        @events[@current].matches?(event) ||
349        (@current == 0 and @ord > 0 and @previous[-1].matches?(event))
350      end
351
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
360
361
362    class Choice < State
363      def initialize context
364        super
365        @choices = []
366      end
367
368      def reset
369        super
370        @events = []
371        @choices.each { |c| c.each { |s| s.reset if s.kind_of? State } }
372      end
373
374      def <<( event )
375        add_event_to_arry( @choices, event )
376      end
377
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
405
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
410
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
423
424      def inspect
425        "< #{to_s} #{@choices.collect{|e| e.collect{|f|f.to_s}.join(', ')}.join(' or ')} >"
426      end
427
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
436
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
446
447
448    class Interleave < Choice
449      def initialize context
450        super
451        @choice = 0
452      end
453
454      def reset
455        @choice = 0
456      end
457
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
486
487
488      def next( event )
489        # Find the next series
490        next_current(event) unless @events[@current]
491        return nil unless @events[@current]
492
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
523
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
528
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
541
542      def inspect
543        "< #{to_s} #{@choices.collect{|e| e.collect{|f|f.to_s}.join(', ')}.join(' and ')} >"
544      end
545    end
546
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
559end
560