~ubuntu-branches/ubuntu/hardy/ruby1.8/hardy-updates

« back to all changes in this revision

Viewing changes to lib/rexml/validation/relaxng.rb

  • Committer: Bazaar Package Importer
  • Author(s): akira yamada
  • Date: 2007-03-13 22:11:58 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20070313221158-h3oql37brlaf2go2
Tags: 1.8.6-1
* new upstream version, 1.8.6.
* libruby1.8 conflicts with libopenssl-ruby1.8 (< 1.8.6) (closes: #410018)
* changed packaging style to cdbs from dbs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
require "rexml/validation/validation"
 
2
require "rexml/parsers/baseparser"
 
3
 
 
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
 
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
 
559
end