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