1# frozen_string_literal: false
2require_relative 'validationexception'
3
4module REXML
5  module Validation
6    module Validator
7      NILEVENT = [ nil ]
8      def reset
9        @current = @root
10        @root.reset
11        @root.previous = true
12        @attr_stack = []
13        self
14      end
15      def dump
16        puts @root.inspect
17      end
18      def validate( event )
19        @attr_stack = [] unless defined? @attr_stack
20        match = @current.next(event)
21        raise ValidationException.new( "Validation error.  Expected: "+
22          @current.expected.join( " or " )+" from #{@current.inspect} "+
23          " but got #{Event.new( event[0], event[1] ).inspect}" ) unless match
24        @current = match
25
26        # Check for attributes
27        case event[0]
28        when :start_element
29          @attr_stack << event[2]
30          begin
31            sattr = [:start_attribute, nil]
32            eattr = [:end_attribute]
33            text = [:text, nil]
34            k, = event[2].find { |key,value|
35              sattr[1] = key
36              m = @current.next( sattr )
37              if m
38                # If the state has text children...
39                if m.matches?( eattr )
40                  @current = m
41                else
42                  text[1] = value
43                  m = m.next( text )
44                  text[1] = nil
45                  return false unless m
46                  @current = m if m
47                end
48                m = @current.next( eattr )
49                if m
50                  @current = m
51                  true
52                else
53                  false
54                end
55              else
56                false
57              end
58            }
59            event[2].delete(k) if k
60          end while k
61        when :end_element
62          attrs = @attr_stack.pop
63          raise ValidationException.new( "Validation error.  Illegal "+
64            " attributes: #{attrs.inspect}") if attrs.length > 0
65        end
66      end
67    end
68
69    class Event
70      def initialize(event_type, event_arg=nil )
71        @event_type = event_type
72        @event_arg = event_arg
73      end
74
75      attr_reader :event_type
76      attr_accessor :event_arg
77
78      def done?
79        @done
80      end
81
82      def single?
83        return (@event_type != :start_element and @event_type != :start_attribute)
84      end
85
86      def matches?( event )
87        return false unless event[0] == @event_type
88        case event[0]
89        when nil
90          return true
91        when :start_element
92          return true if event[1] == @event_arg
93        when :end_element
94          return true
95        when :start_attribute
96          return true if event[1] == @event_arg
97        when :end_attribute
98          return true
99        when :end_document
100          return true
101        when :text
102          return (@event_arg.nil? or @event_arg == event[1])
103=begin
104        when :processing_instruction
105          false
106        when :xmldecl
107          false
108        when :start_doctype
109          false
110        when :end_doctype
111          false
112        when :externalentity
113          false
114        when :elementdecl
115          false
116        when :entity
117          false
118        when :attlistdecl
119          false
120        when :notationdecl
121          false
122        when :end_doctype
123          false
124=end
125        else
126          false
127        end
128      end
129
130      def ==( other )
131        return false unless other.kind_of? Event
132        @event_type == other.event_type and @event_arg == other.event_arg
133      end
134
135      def to_s
136        inspect
137      end
138
139      def inspect
140        "#{@event_type.inspect}( #@event_arg )"
141      end
142    end
143  end
144end
145