source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/rexml/validation/relaxng.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 14.6 KB
Line 
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 [email protected]{|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
Note: See TracBrowser for help on using the repository browser.