duxml 0.8.8 → 0.8.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/bin/validate_xml +30 -30
  3. data/lib/duxml.rb +76 -76
  4. data/lib/duxml/doc.rb +108 -91
  5. data/lib/duxml/doc/element.rb +250 -250
  6. data/lib/duxml/doc/lazy_ox.rb +167 -167
  7. data/lib/duxml/doc/node_set.rb +38 -38
  8. data/lib/duxml/meta.rb +72 -72
  9. data/lib/duxml/meta/grammar.rb +133 -133
  10. data/lib/duxml/meta/grammar/pattern.rb +111 -111
  11. data/lib/duxml/meta/grammar/pattern/attr_name_pattern.rb +36 -36
  12. data/lib/duxml/meta/grammar/pattern/attr_val_pattern.rb +36 -36
  13. data/lib/duxml/meta/grammar/pattern/child_pattern.rb +60 -60
  14. data/lib/duxml/meta/grammar/pattern/text_pattern.rb +31 -31
  15. data/lib/duxml/meta/grammar/pattern_maker.rb +68 -68
  16. data/lib/duxml/meta/grammar/relax_ng.rb +39 -39
  17. data/lib/duxml/meta/grammar/relax_ng/attrs_rule.rb +58 -58
  18. data/lib/duxml/meta/grammar/relax_ng/children_rule.rb +82 -82
  19. data/lib/duxml/meta/grammar/relax_ng/value_rule.rb +44 -44
  20. data/lib/duxml/meta/grammar/rule.rb +58 -58
  21. data/lib/duxml/meta/grammar/rule/attrs_rule.rb +77 -77
  22. data/lib/duxml/meta/grammar/rule/children_rule.rb +135 -135
  23. data/lib/duxml/meta/grammar/rule/text_rule.rb +39 -39
  24. data/lib/duxml/meta/grammar/rule/value_rule.rb +110 -110
  25. data/lib/duxml/meta/grammar/spreadsheet.rb +34 -34
  26. data/lib/duxml/meta/history.rb +88 -88
  27. data/lib/duxml/meta/history/add.rb +30 -30
  28. data/lib/duxml/meta/history/change.rb +70 -70
  29. data/lib/duxml/meta/history/change_attr.rb +33 -33
  30. data/lib/duxml/meta/history/change_text.rb +32 -32
  31. data/lib/duxml/meta/history/error.rb +24 -24
  32. data/lib/duxml/meta/history/new_attr.rb +32 -32
  33. data/lib/duxml/meta/history/new_text.rb +36 -36
  34. data/lib/duxml/meta/history/qualify_error.rb +21 -21
  35. data/lib/duxml/meta/history/remove.rb +27 -27
  36. data/lib/duxml/meta/history/undo.rb +23 -23
  37. data/lib/duxml/meta/history/validate_error.rb +20 -20
  38. data/lib/duxml/reportable.rb +28 -28
  39. data/lib/duxml/ruby_ext/fixnum.rb +55 -55
  40. data/lib/duxml/ruby_ext/module.rb +11 -11
  41. data/lib/duxml/ruby_ext/object.rb +13 -13
  42. data/lib/duxml/ruby_ext/regexp.rb +19 -19
  43. data/lib/duxml/ruby_ext/string.rb +37 -37
  44. data/lib/duxml/saxer.rb +75 -75
  45. metadata +12 -12
@@ -1,59 +1,59 @@
1
- # Copyright (c) 2016 Freescale Semiconductor Inc.
2
-
3
- require File.expand_path(File.dirname(__FILE__) + '/pattern')
4
- require File.expand_path(File.dirname(__FILE__) + '/../../reportable')
5
-
6
- module Duxml
7
- # scans DTD statements for XML rule operators
8
- Struct.new 'Scanner', :match, :operator
9
-
10
- # contains methods to apply Duxml::RuleClass to XML edit or validate events
11
- module Rule
12
- include Reportable
13
- end
14
-
15
- # as an object, Rules consist of a subject, representing the XML Element, and a statement representing the Rule's logic
16
- # this class cannot be used alone and is rather subclassed, one for each type of possible XML changes
17
- class RuleClass < PatternClass
18
- include Rule
19
-
20
- # @param subj [String] NMTOKEN name of element this rule applies to
21
- # @param _statement [String, Regexp] string statement of rule in DTD declaration form or Regexp
22
- def initialize(subj, _statement)
23
- @statement = _statement
24
- @object = nil
25
- super subj
26
- end
27
-
28
- attr_reader :statement, :object
29
- end
30
-
31
- module Rule
32
- # Duxml::Rule's #qualify is only used to report errors found by its subclasses' #qualify methods
33
- # @param change_or_pattern [Duxml::Pattern, Duxml::Change] Change or Pattern to be reported for Rule violation
34
- # @return [Boolean] always false; this method should always be subclassed to apply that specific rule type's #qualify
35
- def qualify(change_or_pattern)
36
- type = (change_or_pattern.respond_to?(:time_stamp)) ? :QualifyError : :ValidateError
37
- report(type, change_or_pattern)
38
- false
39
- end
40
-
41
- # @return [HistoryClass] history to which this rule will report errors
42
- def history
43
- @observer_peers.first.first if @observer_peers.any? and @observer_peers.first.any?
44
- end
45
-
46
- # @param change_or_pattern [Duxml::Change, Duxml::Pattern] change or pattern that rule may apply to
47
- # @return [Boolean] whether this rule does in fact apply
48
- def applies_to?(change_or_pattern)
49
- pattern_type = change_or_pattern.subject.name
50
- subject == pattern_type
51
- end
52
-
53
- # @return [String] default description for a Rule
54
- def description
55
- statement_str = (statement.is_a?(String) ? statement : statement.inspect).gsub('\b','')
56
- %(#{relationship.capitalize} Rule that <#{subject}>'s #{relationship} must match '#{statement_str}')
57
- end
58
- end # module Rule
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/pattern')
4
+ require File.expand_path(File.dirname(__FILE__) + '/../../reportable')
5
+
6
+ module Duxml
7
+ # scans DTD statements for XML rule operators
8
+ Struct.new 'Scanner', :match, :operator
9
+
10
+ # contains methods to apply Duxml::RuleClass to XML edit or validate events
11
+ module Rule
12
+ include Reportable
13
+ end
14
+
15
+ # as an object, Rules consist of a subject, representing the XML Element, and a statement representing the Rule's logic
16
+ # this class cannot be used alone and is rather subclassed, one for each type of possible XML changes
17
+ class RuleClass < PatternClass
18
+ include Rule
19
+
20
+ # @param subj [String] NMTOKEN name of element this rule applies to
21
+ # @param _statement [String, Regexp] string statement of rule in DTD declaration form or Regexp
22
+ def initialize(subj, _statement)
23
+ @statement = _statement
24
+ @object = nil
25
+ super subj
26
+ end
27
+
28
+ attr_reader :statement, :object
29
+ end
30
+
31
+ module Rule
32
+ # Duxml::Rule's #qualify is only used to report errors found by its subclasses' #qualify methods
33
+ # @param change_or_pattern [Duxml::Pattern, Duxml::Change] Change or Pattern to be reported for Rule violation
34
+ # @return [Boolean] always false; this method should always be subclassed to apply that specific rule type's #qualify
35
+ def qualify(change_or_pattern)
36
+ type = (change_or_pattern.respond_to?(:time_stamp)) ? :QualifyError : :ValidateError
37
+ report(type, change_or_pattern)
38
+ false
39
+ end
40
+
41
+ # @return [HistoryClass] history to which this rule will report errors
42
+ def history
43
+ @observer_peers.first.first if @observer_peers.any? and @observer_peers.first.any?
44
+ end
45
+
46
+ # @param change_or_pattern [Duxml::Change, Duxml::Pattern] change or pattern that rule may apply to
47
+ # @return [Boolean] whether this rule does in fact apply
48
+ def applies_to?(change_or_pattern)
49
+ pattern_type = change_or_pattern.subject.name
50
+ subject == pattern_type
51
+ end
52
+
53
+ # @return [String] default description for a Rule
54
+ def description
55
+ statement_str = (statement.is_a?(String) ? statement : statement.inspect).gsub('\b','')
56
+ %(#{relationship.capitalize} Rule that <#{subject}>'s #{relationship} must match '#{statement_str}')
57
+ end
58
+ end # module Rule
59
59
  end # module Duxml
@@ -1,78 +1,78 @@
1
- # Copyright (c) 2016 Freescale Semiconductor Inc.
2
-
3
- require File.expand_path(File.dirname(__FILE__) + '/../rule')
4
-
5
- module Duxml
6
- # methods for applying a rule on what attribute names a given Element is allowed to have
7
- module AttrsRule; end
8
-
9
- # do not subclass this! as object, consists of subject and a Regexp describing allowable attribute names
10
- # as well as a String indicating with DTD symbols for whether the given attributes are allowed or required
11
- class AttrsRuleClass < RuleClass
12
- include AttrsRule
13
-
14
- # @param _subject [String] name of the Element
15
- # @param attrs [String] the attribute name pattern in Regexp form
16
- # @param required [String] the requirement level - optional i.e. #IMPLIED by default
17
- def initialize(_subject, attrs, required='#IMPLIED')
18
- formatted_statement = attrs.gsub('-', '__dash__').gsub(/\b/, '\b').gsub('-', '__dash__')
19
- @requirement = required
20
- super(_subject, formatted_statement)
21
- end
22
-
23
- attr_reader :requirement
24
- end
25
-
26
- module AttrsRule
27
- # @param change_or_pattern [Duxml::Pattern] checks an doc of type change_or_pattern.subject against change_or_pattern
28
- # @return [Boolean] whether or not given pattern passed this test
29
- def qualify(change_or_pattern)
30
- @object = change_or_pattern
31
- result = pass change_or_pattern
32
- super change_or_pattern unless result
33
- @object = nil
34
- result
35
- end
36
-
37
- # @param change_or_pattern [Duxml::Change, Duxml::Pattern] change or pattern that rule may apply to
38
- # @return [Boolean] whether this rule does in fact apply
39
- def applies_to?(change_or_pattern)
40
- return false unless change_or_pattern.respond_to?(:attr_name)
41
- return false unless super(change_or_pattern)
42
- return false if change_or_pattern.respond_to?(:value) && !change_or_pattern.respond_to?(:time_stamp)
43
- statement.include?(change_or_pattern.attr_name.to_s)
44
- end
45
-
46
- # @return [Boolean] whether or not this attribute is required
47
- def required?
48
- requirement == '#REQUIRED'
49
- end
50
-
51
- # @return [String] name of attribute to which this rule applies
52
- def attr_name
53
- statement.gsub('\b','')
54
- end
55
-
56
- # @return [String] describes relationship in a word
57
- def relationship
58
- 'attributes'
59
- end
60
-
61
- # @return [String] description of self; overrides super to account for cases of missing, required attributes
62
- def description
63
- %(#{relationship.capitalize} Rule that <#{subject}>'s #{relationship} #{required? ? 'must':'can'} include '#{attr_name}')
64
- end
65
-
66
- # @param change_or_pattern [Duxml::Change, Duxml::Pattern] change or pattern to be evaluated
67
- # @return [Boolean] true if this rule does not apply to param; false if pattern is for a missing required attribute
68
- # otherwise returns whether or not any illegal attributes exist
69
- def pass(change_or_pattern)
70
- if change_or_pattern.respond_to?(:time_stamp)
71
- an = change_or_pattern.attr_name.to_s
72
- attr_name.include?(an) && !change_or_pattern.abstract?
73
- else
74
- !change_or_pattern.abstract?
75
- end
76
- end # def pass
77
- end # class AttributesRule
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/../rule')
4
+
5
+ module Duxml
6
+ # methods for applying a rule on what attribute names a given Element is allowed to have
7
+ module AttrsRule; end
8
+
9
+ # do not subclass this! as object, consists of subject and a Regexp describing allowable attribute names
10
+ # as well as a String indicating with DTD symbols for whether the given attributes are allowed or required
11
+ class AttrsRuleClass < RuleClass
12
+ include AttrsRule
13
+
14
+ # @param _subject [String] name of the Element
15
+ # @param attrs [String] the attribute name pattern in Regexp form
16
+ # @param required [String] the requirement level - optional i.e. #IMPLIED by default
17
+ def initialize(_subject, attrs, required='#IMPLIED')
18
+ formatted_statement = attrs.gsub('-', '__dash__').gsub(/\b/, '\b').gsub('-', '__dash__')
19
+ @requirement = required
20
+ super(_subject, formatted_statement)
21
+ end
22
+
23
+ attr_reader :requirement
24
+ end
25
+
26
+ module AttrsRule
27
+ # @param change_or_pattern [Duxml::Pattern] checks an doc of type change_or_pattern.subject against change_or_pattern
28
+ # @return [Boolean] whether or not given pattern passed this test
29
+ def qualify(change_or_pattern)
30
+ @object = change_or_pattern
31
+ result = pass change_or_pattern
32
+ super change_or_pattern unless result
33
+ @object = nil
34
+ result
35
+ end
36
+
37
+ # @param change_or_pattern [Duxml::Change, Duxml::Pattern] change or pattern that rule may apply to
38
+ # @return [Boolean] whether this rule does in fact apply
39
+ def applies_to?(change_or_pattern)
40
+ return false unless change_or_pattern.respond_to?(:attr_name)
41
+ return false unless super(change_or_pattern)
42
+ return false if change_or_pattern.respond_to?(:value) && !change_or_pattern.respond_to?(:time_stamp)
43
+ statement.include?(change_or_pattern.attr_name.to_s)
44
+ end
45
+
46
+ # @return [Boolean] whether or not this attribute is required
47
+ def required?
48
+ requirement == '#REQUIRED'
49
+ end
50
+
51
+ # @return [String] name of attribute to which this rule applies
52
+ def attr_name
53
+ statement.gsub('\b','')
54
+ end
55
+
56
+ # @return [String] describes relationship in a word
57
+ def relationship
58
+ 'attributes'
59
+ end
60
+
61
+ # @return [String] description of self; overrides super to account for cases of missing, required attributes
62
+ def description
63
+ %(#{relationship.capitalize} Rule that <#{subject}>'s #{relationship} #{required? ? 'must':'can'} include '#{attr_name}')
64
+ end
65
+
66
+ # @param change_or_pattern [Duxml::Change, Duxml::Pattern] change or pattern to be evaluated
67
+ # @return [Boolean] true if this rule does not apply to param; false if pattern is for a missing required attribute
68
+ # otherwise returns whether or not any illegal attributes exist
69
+ def pass(change_or_pattern)
70
+ if change_or_pattern.respond_to?(:time_stamp)
71
+ an = change_or_pattern.attr_name.to_s
72
+ attr_name.include?(an) && !change_or_pattern.abstract?
73
+ else
74
+ !change_or_pattern.abstract?
75
+ end
76
+ end # def pass
77
+ end # class AttributesRule
78
78
  end # module Duxml
@@ -1,136 +1,136 @@
1
- # Copyright (c) 2016 Freescale Semiconductor Inc.
2
-
3
- require File.expand_path(File.dirname(__FILE__) + '/../rule')
4
- require File.expand_path(File.dirname(__FILE__) + '/../../../doc/element')
5
-
6
- module Duxml
7
- # methods to apply rule on what children an Element may have
8
- module ChildrenRule; end
9
-
10
- # do not subclass! rule that states what children and how many a given element is allowed to have where @statement
11
- # is a DTD/Regexp of what children are allowed, how many, in what order and whether they are required
12
- class ChildrenRuleClass < RuleClass
13
- include ChildrenRule
14
- # child rules are initialized from DTD doc declarations e.g. (zeroOrMore|other-first-child)*,second-child-optional?,third-child-gt1+
15
- #
16
- # @param _subject [String] name of the doc the rule applies
17
- # @param _statement [String] DTD-style statement of the rule
18
- def initialize(_subject, _statement)
19
- formatted_statement = _statement
20
- .gsub(/[\<>]/, '')
21
- .gsub(/#PCDATA/, 'PCDATA')
22
- .gsub('-','_dash_')
23
- .gsub(/\b/,'\b')
24
- .gsub(/PCDATA/, '#PCDATA')
25
- .gsub('_dash_', '-')
26
- .gsub(/\s/,'')
27
- super(_subject, formatted_statement)
28
- end
29
- end # class ChildrenRuleClass
30
-
31
- module ChildrenRule
32
- # @param change_or_pattern [Duxml::ChildPattern, Duxml::Add, Duxml::Remove] to be evaluated to see if it follows this rule
33
- # @return [Boolean] whether or not change_or_pattern#subject is allowed to have #object as its child
34
- # if false, Error is reported to History
35
- def qualify(change_or_pattern)
36
- @object = change_or_pattern
37
- result = pass
38
- super change_or_pattern unless result
39
- @object = nil
40
- result
41
- end
42
-
43
- # @return [Array[String]] in order, array of child doc types required by this rule
44
- def required_children
45
- req_scans = get_scanners.select do |scanner| scanner[:operator].match(/[\*\?]/).nil? end
46
- req_scans.collect do |req_scan|
47
- get_child_name req_scan unless get_child_name(req_scan) == 'EMPTY'
48
- end.compact
49
- end
50
-
51
- # @param change_or_pattern [Duxml::Pattern, Duxml::Change] change or pattern to be evaluated
52
- # @return [Boolean] whether subjects agree, and change or pattern is not a Rule and responds to #affected_parent
53
- def applies_to?(change_or_pattern)
54
- case
55
- when change_or_pattern.is_a?(Duxml::Rule) then false
56
- when super(change_or_pattern) && change_or_pattern.respond_to?(:parent)
57
- true
58
- else
59
- false
60
- end
61
- end
62
-
63
- private
64
-
65
- # @param req_scan [Struct::Scanner] scanner with which to identify a child name within @statement
66
- # @return [String] name of child that is currently being checked for by this rule
67
- def get_child_name(req_scan)
68
- req_scan[:match].inspect.split(/[\/(\\b)]/).select do |word| !word.empty? end.first
69
- end
70
-
71
- # @return [[Struct::Scanner]] array of scanners for each logical component of @statement to be applied to target Element's children
72
- def get_scanners
73
- statement.split(',').collect do |rule|
74
- r = rule.gsub(/[\(\)]/, '')
75
- operator = r[-1].match(/[\?\+\*]/) ? r[-1] : ''
76
- r = r[0..-2] unless operator.empty?
77
- Struct::Scanner.new Regexp.new(r), operator
78
- end
79
- end
80
-
81
- attr_reader :child_stack
82
-
83
- # @return [Boolean] whether or not this rule has been passed
84
- def pass
85
- @child_stack = object.child.nil? ? [] : object.parent.nodes.clone
86
- scanners = get_scanners
87
- scanner = scanners.shift
88
- result = false
89
- loop do
90
- child = @child_stack.shift
91
- case
92
- when child.nil?, scanner.nil? then break
93
- when child.is_a?(String)
94
- result = scanner[:match].inspect.include?('#PCDATA')
95
- when child.name.match(scanner[:match]) # scanner matches this child
96
- if scanner[:operator]=='?' or scanner[:operator]=='' # shift scanners if we only need one child of this type
97
- scanner = scanners.shift
98
- result = previous_child.nil? || (previous_child.name != child.name)
99
- else
100
- result = true
101
- end
102
- # scanner does not match this child...
103
- when %w(? *).include?(scanner[:operator]) # optional scanner so try next scanner on same child
104
- scanner = scanners.shift
105
- @child_stack.unshift child
106
- result = !scanner.nil?
107
- else
108
- result = false # else, this scanner will report false
109
- end # case
110
- return result unless child.is_a?(String) or !matching_index?# don't need to keep looping because we've scanned our target
111
- end # loop do
112
-
113
- # checking to see if any required children were not present
114
- result = false if child_stack.empty? && scanners.any? do |scanner|
115
- scanner[:operator] != '*' or scanner[:operator] != '?'
116
- end
117
- result
118
- end # def pass
119
-
120
- # @return [Boolean] whether the child currently being scanned is the one in the pattern we are testing now
121
- def matching_index?
122
- child_index == object.index
123
- end
124
-
125
- # @return [Fixnum] index of child currently being scanned
126
- def child_index
127
- object.parent.nodes.size-child_stack.size-1
128
- end
129
-
130
- # @return [Element] previous element to the one being scanned
131
- def previous_child
132
- index = child_index - 1
133
- index < 0 ? nil : object.parent.nodes[index]
134
- end
135
- end # module ChildrenRule
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/../rule')
4
+ require File.expand_path(File.dirname(__FILE__) + '/../../../doc/element')
5
+
6
+ module Duxml
7
+ # methods to apply rule on what children an Element may have
8
+ module ChildrenRule; end
9
+
10
+ # do not subclass! rule that states what children and how many a given element is allowed to have where @statement
11
+ # is a DTD/Regexp of what children are allowed, how many, in what order and whether they are required
12
+ class ChildrenRuleClass < RuleClass
13
+ include ChildrenRule
14
+ # child rules are initialized from DTD doc declarations e.g. (zeroOrMore|other-first-child)*,second-child-optional?,third-child-gt1+
15
+ #
16
+ # @param _subject [String] name of the doc the rule applies
17
+ # @param _statement [String] DTD-style statement of the rule
18
+ def initialize(_subject, _statement)
19
+ formatted_statement = _statement
20
+ .gsub(/[\<>]/, '')
21
+ .gsub(/#PCDATA/, 'PCDATA')
22
+ .gsub('-','_dash_')
23
+ .gsub(/\b/,'\b')
24
+ .gsub(/PCDATA/, '#PCDATA')
25
+ .gsub('_dash_', '-')
26
+ .gsub(/\s/,'')
27
+ super(_subject, formatted_statement)
28
+ end
29
+ end # class ChildrenRuleClass
30
+
31
+ module ChildrenRule
32
+ # @param change_or_pattern [Duxml::ChildPattern, Duxml::Add, Duxml::Remove] to be evaluated to see if it follows this rule
33
+ # @return [Boolean] whether or not change_or_pattern#subject is allowed to have #object as its child
34
+ # if false, Error is reported to History
35
+ def qualify(change_or_pattern)
36
+ @object = change_or_pattern
37
+ result = pass
38
+ super change_or_pattern unless result
39
+ @object = nil
40
+ result
41
+ end
42
+
43
+ # @return [Array[String]] in order, array of child doc types required by this rule
44
+ def required_children
45
+ req_scans = get_scanners.select do |scanner| scanner[:operator].match(/[\*\?]/).nil? end
46
+ req_scans.collect do |req_scan|
47
+ get_child_name req_scan unless get_child_name(req_scan) == 'EMPTY'
48
+ end.compact
49
+ end
50
+
51
+ # @param change_or_pattern [Duxml::Pattern, Duxml::Change] change or pattern to be evaluated
52
+ # @return [Boolean] whether subjects agree, and change or pattern is not a Rule and responds to #affected_parent
53
+ def applies_to?(change_or_pattern)
54
+ case
55
+ when change_or_pattern.is_a?(Duxml::Rule) then false
56
+ when super(change_or_pattern) && change_or_pattern.respond_to?(:parent)
57
+ true
58
+ else
59
+ false
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ # @param req_scan [Struct::Scanner] scanner with which to identify a child name within @statement
66
+ # @return [String] name of child that is currently being checked for by this rule
67
+ def get_child_name(req_scan)
68
+ req_scan[:match].inspect.split(/[\/(\\b)]/).select do |word| !word.empty? end.first
69
+ end
70
+
71
+ # @return [[Struct::Scanner]] array of scanners for each logical component of @statement to be applied to target Element's children
72
+ def get_scanners
73
+ statement.split(',').collect do |rule|
74
+ r = rule.gsub(/[\(\)]/, '')
75
+ operator = r[-1].match(/[\?\+\*]/) ? r[-1] : ''
76
+ r = r[0..-2] unless operator.empty?
77
+ Struct::Scanner.new Regexp.new(r), operator
78
+ end
79
+ end
80
+
81
+ attr_reader :child_stack
82
+
83
+ # @return [Boolean] whether or not this rule has been passed
84
+ def pass
85
+ @child_stack = object.child.nil? ? [] : object.parent.nodes.clone
86
+ scanners = get_scanners
87
+ scanner = scanners.shift
88
+ result = false
89
+ loop do
90
+ child = @child_stack.shift
91
+ case
92
+ when child.nil?, scanner.nil? then break
93
+ when child.is_a?(String)
94
+ result = scanner[:match].inspect.include?('#PCDATA')
95
+ when child.name.match(scanner[:match]) # scanner matches this child
96
+ if scanner[:operator]=='?' or scanner[:operator]=='' # shift scanners if we only need one child of this type
97
+ scanner = scanners.shift
98
+ result = previous_child.nil? || (previous_child.name != child.name)
99
+ else
100
+ result = true
101
+ end
102
+ # scanner does not match this child...
103
+ when %w(? *).include?(scanner[:operator]) # optional scanner so try next scanner on same child
104
+ scanner = scanners.shift
105
+ @child_stack.unshift child
106
+ result = !scanner.nil?
107
+ else
108
+ result = false # else, this scanner will report false
109
+ end # case
110
+ return result unless child.is_a?(String) or !matching_index?# don't need to keep looping because we've scanned our target
111
+ end # loop do
112
+
113
+ # checking to see if any required children were not present
114
+ result = false if child_stack.empty? && scanners.any? do |scanner|
115
+ scanner[:operator] != '*' or scanner[:operator] != '?'
116
+ end
117
+ result
118
+ end # def pass
119
+
120
+ # @return [Boolean] whether the child currently being scanned is the one in the pattern we are testing now
121
+ def matching_index?
122
+ child_index == object.index
123
+ end
124
+
125
+ # @return [Fixnum] index of child currently being scanned
126
+ def child_index
127
+ object.parent.nodes.size-child_stack.size-1
128
+ end
129
+
130
+ # @return [Element] previous element to the one being scanned
131
+ def previous_child
132
+ index = child_index - 1
133
+ index < 0 ? nil : object.parent.nodes[index]
134
+ end
135
+ end # module ChildrenRule
136
136
  end # module Duxml