File: rexml/validation/relaxng.rb

Overview
Module Structure
Class Hierarchy
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: REXML#4
  module: Validation#5
  class: RelaxNG#34
includes
  Validator ( REXML::Validation )
inherits from
  Object ( Builtin-Module )
has properties
constant: INFINITY #37
constant: EMPTY #38
constant: TEXT #39
attribute: current [RW] #40
attribute: count [RW] #41
attribute: references [R] #42
method: initialize #45
method: receive #121
  class: State#126
inherits from
  Object ( Builtin-Module )
has properties
method: initialize / 1 #127
method: reset #136
method: previous= / 1 #142
method: next / 1 #146
method: to_s #177
method: inspect #185
method: expected #192
method: << / 1 #196
method: expand_ref_in / 2 #202
method: add_event_to_arry / 2 #210
method: generate_event / 1 #220
  class: Sequence#256
inherits from
  State ( REXML::Validation )
has properties
method: matches? / 1 #257
  class: Optional#263
inherits from
  State ( REXML::Validation )
has properties
method: next / 1 #264
method: matches? / 1 #274
method: expected #279
  class: ZeroOrMore#286
inherits from
  Optional ( REXML::Validation )
has properties
method: next / 1 #287
method: expected #308
  class: OneOrMore#315
inherits from
  State ( REXML::Validation )
has properties
method: initialize #316
method: reset #321
method: next / 1 #326
method: matches? / 1 #347
method: expected #352
  class: Choice#362
inherits from
  State ( REXML::Validation )
has properties
method: initialize #363
method: reset #368
method: << / 1 #374
method: next / 1 #378
method: matches? / 1 #406
method: expected #411
method: inspect #424
method: add_event_to_arry / 2 #429
  class: Interleave#448
inherits from
  Choice ( REXML::Validation )
has properties
method: initialize #449
method: reset #454
method: next_current / 1 #458
method: next / 1 #488
method: matches? / 1 #524
method: expected #529
method: inspect #542
  class: Ref#547
inherits from
  Object ( Builtin-Module )
has properties
method: initialize #548
method: to_s #551
method: inspect #554

Class Hierarchy

Code

   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