rubysl-rexml 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +25 -0
  6. data/README.md +29 -0
  7. data/Rakefile +1 -0
  8. data/lib/rexml/attlistdecl.rb +62 -0
  9. data/lib/rexml/attribute.rb +185 -0
  10. data/lib/rexml/cdata.rb +67 -0
  11. data/lib/rexml/child.rb +96 -0
  12. data/lib/rexml/comment.rb +80 -0
  13. data/lib/rexml/doctype.rb +271 -0
  14. data/lib/rexml/document.rb +230 -0
  15. data/lib/rexml/dtd/attlistdecl.rb +10 -0
  16. data/lib/rexml/dtd/dtd.rb +51 -0
  17. data/lib/rexml/dtd/elementdecl.rb +17 -0
  18. data/lib/rexml/dtd/entitydecl.rb +56 -0
  19. data/lib/rexml/dtd/notationdecl.rb +39 -0
  20. data/lib/rexml/element.rb +1227 -0
  21. data/lib/rexml/encoding.rb +71 -0
  22. data/lib/rexml/encodings/CP-1252.rb +103 -0
  23. data/lib/rexml/encodings/EUC-JP.rb +35 -0
  24. data/lib/rexml/encodings/ICONV.rb +22 -0
  25. data/lib/rexml/encodings/ISO-8859-1.rb +7 -0
  26. data/lib/rexml/encodings/ISO-8859-15.rb +72 -0
  27. data/lib/rexml/encodings/SHIFT-JIS.rb +37 -0
  28. data/lib/rexml/encodings/SHIFT_JIS.rb +1 -0
  29. data/lib/rexml/encodings/UNILE.rb +34 -0
  30. data/lib/rexml/encodings/US-ASCII.rb +30 -0
  31. data/lib/rexml/encodings/UTF-16.rb +35 -0
  32. data/lib/rexml/encodings/UTF-8.rb +18 -0
  33. data/lib/rexml/entity.rb +166 -0
  34. data/lib/rexml/formatters/default.rb +109 -0
  35. data/lib/rexml/formatters/pretty.rb +138 -0
  36. data/lib/rexml/formatters/transitive.rb +56 -0
  37. data/lib/rexml/functions.rb +382 -0
  38. data/lib/rexml/instruction.rb +70 -0
  39. data/lib/rexml/light/node.rb +196 -0
  40. data/lib/rexml/namespace.rb +47 -0
  41. data/lib/rexml/node.rb +75 -0
  42. data/lib/rexml/output.rb +24 -0
  43. data/lib/rexml/parent.rb +166 -0
  44. data/lib/rexml/parseexception.rb +51 -0
  45. data/lib/rexml/parsers/baseparser.rb +503 -0
  46. data/lib/rexml/parsers/lightparser.rb +60 -0
  47. data/lib/rexml/parsers/pullparser.rb +196 -0
  48. data/lib/rexml/parsers/sax2parser.rb +238 -0
  49. data/lib/rexml/parsers/streamparser.rb +46 -0
  50. data/lib/rexml/parsers/treeparser.rb +97 -0
  51. data/lib/rexml/parsers/ultralightparser.rb +56 -0
  52. data/lib/rexml/parsers/xpathparser.rb +698 -0
  53. data/lib/rexml/quickpath.rb +266 -0
  54. data/lib/rexml/rexml.rb +32 -0
  55. data/lib/rexml/sax2listener.rb +97 -0
  56. data/lib/rexml/source.rb +251 -0
  57. data/lib/rexml/streamlistener.rb +92 -0
  58. data/lib/rexml/syncenumerator.rb +33 -0
  59. data/lib/rexml/text.rb +344 -0
  60. data/lib/rexml/undefinednamespaceexception.rb +8 -0
  61. data/lib/rexml/validation/relaxng.rb +559 -0
  62. data/lib/rexml/validation/validation.rb +155 -0
  63. data/lib/rexml/validation/validationexception.rb +9 -0
  64. data/lib/rexml/xmldecl.rb +119 -0
  65. data/lib/rexml/xmltokens.rb +18 -0
  66. data/lib/rexml/xpath.rb +66 -0
  67. data/lib/rexml/xpath_parser.rb +792 -0
  68. data/lib/rubysl/rexml.rb +1 -0
  69. data/lib/rubysl/rexml/version.rb +5 -0
  70. data/rubysl-rexml.gemspec +23 -0
  71. data/spec/attribute/clone_spec.rb +10 -0
  72. data/spec/attribute/element_spec.rb +22 -0
  73. data/spec/attribute/equal_value_spec.rb +17 -0
  74. data/spec/attribute/hash_spec.rb +12 -0
  75. data/spec/attribute/initialize_spec.rb +28 -0
  76. data/spec/attribute/inspect_spec.rb +19 -0
  77. data/spec/attribute/namespace_spec.rb +23 -0
  78. data/spec/attribute/node_type_spec.rb +9 -0
  79. data/spec/attribute/prefix_spec.rb +17 -0
  80. data/spec/attribute/remove_spec.rb +19 -0
  81. data/spec/attribute/to_s_spec.rb +13 -0
  82. data/spec/attribute/to_string_spec.rb +14 -0
  83. data/spec/attribute/value_spec.rb +14 -0
  84. data/spec/attribute/write_spec.rb +22 -0
  85. data/spec/attribute/xpath_spec.rb +19 -0
  86. data/spec/attributes/add_spec.rb +6 -0
  87. data/spec/attributes/append_spec.rb +6 -0
  88. data/spec/attributes/delete_all_spec.rb +30 -0
  89. data/spec/attributes/delete_spec.rb +26 -0
  90. data/spec/attributes/each_attribute_spec.rb +24 -0
  91. data/spec/attributes/each_spec.rb +24 -0
  92. data/spec/attributes/element_reference_spec.rb +18 -0
  93. data/spec/attributes/element_set_spec.rb +25 -0
  94. data/spec/attributes/get_attribute_ns_spec.rb +13 -0
  95. data/spec/attributes/get_attribute_spec.rb +28 -0
  96. data/spec/attributes/initialize_spec.rb +18 -0
  97. data/spec/attributes/length_spec.rb +6 -0
  98. data/spec/attributes/namespaces_spec.rb +5 -0
  99. data/spec/attributes/prefixes_spec.rb +23 -0
  100. data/spec/attributes/shared/add.rb +17 -0
  101. data/spec/attributes/shared/length.rb +12 -0
  102. data/spec/attributes/size_spec.rb +6 -0
  103. data/spec/attributes/to_a_spec.rb +20 -0
  104. data/spec/cdata/clone_spec.rb +9 -0
  105. data/spec/cdata/initialize_spec.rb +24 -0
  106. data/spec/cdata/shared/to_s.rb +11 -0
  107. data/spec/cdata/to_s_spec.rb +6 -0
  108. data/spec/cdata/value_spec.rb +6 -0
  109. data/spec/document/add_element_spec.rb +30 -0
  110. data/spec/document/add_spec.rb +60 -0
  111. data/spec/document/clone_spec.rb +19 -0
  112. data/spec/document/doctype_spec.rb +14 -0
  113. data/spec/document/encoding_spec.rb +21 -0
  114. data/spec/document/expanded_name_spec.rb +15 -0
  115. data/spec/document/new_spec.rb +37 -0
  116. data/spec/document/node_type_spec.rb +7 -0
  117. data/spec/document/root_spec.rb +11 -0
  118. data/spec/document/stand_alone_spec.rb +18 -0
  119. data/spec/document/version_spec.rb +13 -0
  120. data/spec/document/write_spec.rb +38 -0
  121. data/spec/document/xml_decl_spec.rb +14 -0
  122. data/spec/element/add_attribute_spec.rb +40 -0
  123. data/spec/element/add_attributes_spec.rb +21 -0
  124. data/spec/element/add_element_spec.rb +38 -0
  125. data/spec/element/add_namespace_spec.rb +23 -0
  126. data/spec/element/add_text_spec.rb +23 -0
  127. data/spec/element/attribute_spec.rb +16 -0
  128. data/spec/element/attributes_spec.rb +18 -0
  129. data/spec/element/cdatas_spec.rb +23 -0
  130. data/spec/element/clone_spec.rb +28 -0
  131. data/spec/element/comments_spec.rb +20 -0
  132. data/spec/element/delete_attribute_spec.rb +38 -0
  133. data/spec/element/delete_element_spec.rb +50 -0
  134. data/spec/element/delete_namespace_spec.rb +24 -0
  135. data/spec/element/document_spec.rb +17 -0
  136. data/spec/element/each_element_with_attribute_spec.rb +34 -0
  137. data/spec/element/each_element_with_text_spec.rb +30 -0
  138. data/spec/element/get_text_spec.rb +17 -0
  139. data/spec/element/has_attributes_spec.rb +16 -0
  140. data/spec/element/has_elements_spec.rb +17 -0
  141. data/spec/element/has_text_spec.rb +15 -0
  142. data/spec/element/inspect_spec.rb +26 -0
  143. data/spec/element/instructions_spec.rb +20 -0
  144. data/spec/element/namespace_spec.rb +26 -0
  145. data/spec/element/namespaces_spec.rb +31 -0
  146. data/spec/element/new_spec.rb +34 -0
  147. data/spec/element/next_element_spec.rb +18 -0
  148. data/spec/element/node_type_spec.rb +7 -0
  149. data/spec/element/prefixes_spec.rb +22 -0
  150. data/spec/element/previous_element_spec.rb +19 -0
  151. data/spec/element/raw_spec.rb +23 -0
  152. data/spec/element/root_spec.rb +27 -0
  153. data/spec/element/text_spec.rb +45 -0
  154. data/spec/element/texts_spec.rb +15 -0
  155. data/spec/element/whitespace_spec.rb +22 -0
  156. data/spec/node/each_recursive_spec.rb +20 -0
  157. data/spec/node/find_first_recursive_spec.rb +24 -0
  158. data/spec/node/index_in_parent_spec.rb +14 -0
  159. data/spec/node/next_sibling_node_spec.rb +20 -0
  160. data/spec/node/parent_spec.rb +20 -0
  161. data/spec/node/previous_sibling_node_spec.rb +20 -0
  162. data/spec/shared/each_element.rb +35 -0
  163. data/spec/shared/elements_to_a.rb +35 -0
  164. data/spec/text/append_spec.rb +9 -0
  165. data/spec/text/clone_spec.rb +9 -0
  166. data/spec/text/comparison_spec.rb +24 -0
  167. data/spec/text/empty_spec.rb +11 -0
  168. data/spec/text/indent_text_spec.rb +23 -0
  169. data/spec/text/inspect_spec.rb +7 -0
  170. data/spec/text/new_spec.rb +48 -0
  171. data/spec/text/node_type_spec.rb +7 -0
  172. data/spec/text/normalize_spec.rb +7 -0
  173. data/spec/text/read_with_substitution_spec.rb +12 -0
  174. data/spec/text/to_s_spec.rb +17 -0
  175. data/spec/text/unnormalize_spec.rb +7 -0
  176. data/spec/text/value_spec.rb +36 -0
  177. data/spec/text/wrap_spec.rb +20 -0
  178. data/spec/text/write_with_substitution_spec.rb +32 -0
  179. metadata +385 -0
@@ -0,0 +1,46 @@
1
+ module REXML
2
+ module Parsers
3
+ class StreamParser
4
+ def initialize source, listener
5
+ @listener = listener
6
+ @parser = BaseParser.new( source )
7
+ end
8
+
9
+ def add_listener( listener )
10
+ @parser.add_listener( listener )
11
+ end
12
+
13
+ def parse
14
+ # entity string
15
+ while true
16
+ event = @parser.pull
17
+ case event[0]
18
+ when :end_document
19
+ return
20
+ when :start_element
21
+ attrs = event[2].each do |n, v|
22
+ event[2][n] = @parser.unnormalize( v )
23
+ end
24
+ @listener.tag_start( event[1], attrs )
25
+ when :end_element
26
+ @listener.tag_end( event[1] )
27
+ when :text
28
+ normalized = @parser.unnormalize( event[1] )
29
+ @listener.text( normalized )
30
+ when :processing_instruction
31
+ @listener.instruction( *event[1,2] )
32
+ when :start_doctype
33
+ @listener.doctype( *event[1..-1] )
34
+ when :end_doctype
35
+ # FIXME: remove this condition for milestone:3.2
36
+ @listener.doctype_end if @listener.respond_to? :doctype_end
37
+ when :comment, :attlistdecl, :cdata, :xmldecl, :elementdecl
38
+ @listener.send( event[0].to_s, *event[1..-1] )
39
+ when :entitydecl, :notationdecl
40
+ @listener.send( event[0].to_s, event[1..-1] )
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,97 @@
1
+ require 'rexml/validation/validationexception'
2
+ require 'rexml/undefinednamespaceexception'
3
+
4
+ module REXML
5
+ module Parsers
6
+ class TreeParser
7
+ def initialize( source, build_context = Document.new )
8
+ @build_context = build_context
9
+ @parser = Parsers::BaseParser.new( source )
10
+ end
11
+
12
+ def add_listener( listener )
13
+ @parser.add_listener( listener )
14
+ end
15
+
16
+ def parse
17
+ tag_stack = []
18
+ in_doctype = false
19
+ entities = nil
20
+ begin
21
+ while true
22
+ event = @parser.pull
23
+ #STDERR.puts "TREEPARSER GOT #{event.inspect}"
24
+ case event[0]
25
+ when :end_document
26
+ unless tag_stack.empty?
27
+ #raise ParseException.new("No close tag for #{tag_stack.inspect}")
28
+ raise ParseException.new("No close tag for #{@build_context.xpath}")
29
+ end
30
+ return
31
+ when :start_element
32
+ tag_stack.push(event[1])
33
+ el = @build_context = @build_context.add_element( event[1], event[2] )
34
+ when :end_element
35
+ tag_stack.pop
36
+ @build_context = @build_context.parent
37
+ when :text
38
+ if not in_doctype
39
+ if @build_context[-1].instance_of? Text
40
+ @build_context[-1] << event[1]
41
+ else
42
+ @build_context.add(
43
+ Text.new(event[1], @build_context.whitespace, nil, true)
44
+ ) unless (
45
+ @build_context.ignore_whitespace_nodes and
46
+ event[1].strip.size==0
47
+ )
48
+ end
49
+ end
50
+ when :comment
51
+ c = Comment.new( event[1] )
52
+ @build_context.add( c )
53
+ when :cdata
54
+ c = CData.new( event[1] )
55
+ @build_context.add( c )
56
+ when :processing_instruction
57
+ @build_context.add( Instruction.new( event[1], event[2] ) )
58
+ when :end_doctype
59
+ in_doctype = false
60
+ entities.each { |k,v| entities[k] = @build_context.entities[k].value }
61
+ @build_context = @build_context.parent
62
+ when :start_doctype
63
+ doctype = DocType.new( event[1..-1], @build_context )
64
+ @build_context = doctype
65
+ entities = {}
66
+ in_doctype = true
67
+ when :attlistdecl
68
+ n = AttlistDecl.new( event[1..-1] )
69
+ @build_context.add( n )
70
+ when :externalentity
71
+ n = ExternalEntity.new( event[1] )
72
+ @build_context.add( n )
73
+ when :elementdecl
74
+ n = ElementDecl.new( event[1] )
75
+ @build_context.add(n)
76
+ when :entitydecl
77
+ entities[ event[1] ] = event[2] unless event[2] =~ /PUBLIC|SYSTEM/
78
+ @build_context.add(Entity.new(event))
79
+ when :notationdecl
80
+ n = NotationDecl.new( *event[1..-1] )
81
+ @build_context.add( n )
82
+ when :xmldecl
83
+ x = XMLDecl.new( event[1], event[2], event[3] )
84
+ @build_context.add( x )
85
+ end
86
+ end
87
+ rescue REXML::Validation::ValidationException
88
+ raise
89
+ rescue REXML::UndefinedNamespaceException
90
+ raise
91
+ rescue
92
+ raise ParseException.new( $!.message, @parser.source, @parser, $! )
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,56 @@
1
+ require 'rexml/parsers/streamparser'
2
+ require 'rexml/parsers/baseparser'
3
+
4
+ module REXML
5
+ module Parsers
6
+ class UltraLightParser
7
+ def initialize stream
8
+ @stream = stream
9
+ @parser = REXML::Parsers::BaseParser.new( stream )
10
+ end
11
+
12
+ def add_listener( listener )
13
+ @parser.add_listener( listener )
14
+ end
15
+
16
+ def rewind
17
+ @stream.rewind
18
+ @parser.stream = @stream
19
+ end
20
+
21
+ def parse
22
+ root = context = []
23
+ while true
24
+ event = @parser.pull
25
+ case event[0]
26
+ when :end_document
27
+ break
28
+ when :end_doctype
29
+ context = context[1]
30
+ when :start_element, :doctype
31
+ context << event
32
+ event[1,0] = [context]
33
+ context = event
34
+ when :end_element
35
+ context = context[1]
36
+ else
37
+ context << event
38
+ end
39
+ end
40
+ root
41
+ end
42
+ end
43
+
44
+ # An element is an array. The array contains:
45
+ # 0 The parent element
46
+ # 1 The tag name
47
+ # 2 A hash of attributes
48
+ # 3..-1 The child elements
49
+ # An element is an array of size > 3
50
+ # Text is a String
51
+ # PIs are [ :processing_instruction, target, data ]
52
+ # Comments are [ :comment, data ]
53
+ # DocTypes are DocType structs
54
+ # The root is an array with XMLDecls, Text, DocType, Array, Text
55
+ end
56
+ end
@@ -0,0 +1,698 @@
1
+ require 'rexml/namespace'
2
+ require 'rexml/xmltokens'
3
+
4
+ module REXML
5
+ module Parsers
6
+ # You don't want to use this class. Really. Use XPath, which is a wrapper
7
+ # for this class. Believe me. You don't want to poke around in here.
8
+ # There is strange, dark magic at work in this code. Beware. Go back! Go
9
+ # back while you still can!
10
+ class XPathParser
11
+ include XMLTokens
12
+ LITERAL = /^'([^']*)'|^"([^"]*)"/u
13
+
14
+ def namespaces=( namespaces )
15
+ Functions::namespace_context = namespaces
16
+ @namespaces = namespaces
17
+ end
18
+
19
+ def parse path
20
+ path.gsub!(/([\(\[])\s+/, '\1') # Strip ignorable spaces
21
+ path.gsub!( /\s+([\]\)])/, '\1' )
22
+ parsed = []
23
+ path = OrExpr(path, parsed)
24
+ parsed
25
+ end
26
+
27
+ def predicate path
28
+ parsed = []
29
+ Predicate( "[#{path}]", parsed )
30
+ parsed
31
+ end
32
+
33
+ def abbreviate( path )
34
+ path = path.kind_of?(String) ? parse( path ) : path
35
+ string = ""
36
+ document = false
37
+ while path.size > 0
38
+ op = path.shift
39
+ case op
40
+ when :node
41
+ when :attribute
42
+ string << "/" if string.size > 0
43
+ string << "@"
44
+ when :child
45
+ string << "/" if string.size > 0
46
+ when :descendant_or_self
47
+ string << "/"
48
+ when :self
49
+ string << "."
50
+ when :parent
51
+ string << ".."
52
+ when :any
53
+ string << "*"
54
+ when :text
55
+ string << "text()"
56
+ when :following, :following_sibling,
57
+ :ancestor, :ancestor_or_self, :descendant,
58
+ :namespace, :preceding, :preceding_sibling
59
+ string << "/" unless string.size == 0
60
+ string << op.to_s.tr("_", "-")
61
+ string << "::"
62
+ when :qname
63
+ prefix = path.shift
64
+ name = path.shift
65
+ string << prefix+":" if prefix.size > 0
66
+ string << name
67
+ when :predicate
68
+ string << '['
69
+ string << predicate_to_string( path.shift ) {|x| abbreviate( x ) }
70
+ string << ']'
71
+ when :document
72
+ document = true
73
+ when :function
74
+ string << path.shift
75
+ string << "( "
76
+ string << predicate_to_string( path.shift[0] ) {|x| abbreviate( x )}
77
+ string << " )"
78
+ when :literal
79
+ string << %Q{ "#{path.shift}" }
80
+ else
81
+ string << "/" unless string.size == 0
82
+ string << "UNKNOWN("
83
+ string << op.inspect
84
+ string << ")"
85
+ end
86
+ end
87
+ string = "/"+string if document
88
+ return string
89
+ end
90
+
91
+ def expand( path )
92
+ path = path.kind_of?(String) ? parse( path ) : path
93
+ string = ""
94
+ document = false
95
+ while path.size > 0
96
+ op = path.shift
97
+ case op
98
+ when :node
99
+ string << "node()"
100
+ when :attribute, :child, :following, :following_sibling,
101
+ :ancestor, :ancestor_or_self, :descendant, :descendant_or_self,
102
+ :namespace, :preceding, :preceding_sibling, :self, :parent
103
+ string << "/" unless string.size == 0
104
+ string << op.to_s.tr("_", "-")
105
+ string << "::"
106
+ when :any
107
+ string << "*"
108
+ when :qname
109
+ prefix = path.shift
110
+ name = path.shift
111
+ string << prefix+":" if prefix.size > 0
112
+ string << name
113
+ when :predicate
114
+ string << '['
115
+ string << predicate_to_string( path.shift ) { |x| expand(x) }
116
+ string << ']'
117
+ when :document
118
+ document = true
119
+ else
120
+ string << "/" unless string.size == 0
121
+ string << "UNKNOWN("
122
+ string << op.inspect
123
+ string << ")"
124
+ end
125
+ end
126
+ string = "/"+string if document
127
+ return string
128
+ end
129
+
130
+ def predicate_to_string( path, &block )
131
+ string = ""
132
+ case path[0]
133
+ when :and, :or, :mult, :plus, :minus, :neq, :eq, :lt, :gt, :lteq, :gteq, :div, :mod, :union
134
+ op = path.shift
135
+ case op
136
+ when :eq
137
+ op = "="
138
+ when :lt
139
+ op = "<"
140
+ when :gt
141
+ op = ">"
142
+ when :lteq
143
+ op = "<="
144
+ when :gteq
145
+ op = ">="
146
+ when :neq
147
+ op = "!="
148
+ when :union
149
+ op = "|"
150
+ end
151
+ left = predicate_to_string( path.shift, &block )
152
+ right = predicate_to_string( path.shift, &block )
153
+ string << " "
154
+ string << left
155
+ string << " "
156
+ string << op.to_s
157
+ string << " "
158
+ string << right
159
+ string << " "
160
+ when :function
161
+ path.shift
162
+ name = path.shift
163
+ string << name
164
+ string << "( "
165
+ string << predicate_to_string( path.shift, &block )
166
+ string << " )"
167
+ when :literal
168
+ path.shift
169
+ string << " "
170
+ string << path.shift.inspect
171
+ string << " "
172
+ else
173
+ string << " "
174
+ string << yield( path )
175
+ string << " "
176
+ end
177
+ return string.squeeze(" ")
178
+ end
179
+
180
+ private
181
+ #LocationPath
182
+ # | RelativeLocationPath
183
+ # | '/' RelativeLocationPath?
184
+ # | '//' RelativeLocationPath
185
+ def LocationPath path, parsed
186
+ #puts "LocationPath '#{path}'"
187
+ path = path.strip
188
+ if path[0] == ?/
189
+ parsed << :document
190
+ if path[1] == ?/
191
+ parsed << :descendant_or_self
192
+ parsed << :node
193
+ path = path[2..-1]
194
+ else
195
+ path = path[1..-1]
196
+ end
197
+ end
198
+ #puts parsed.inspect
199
+ return RelativeLocationPath( path, parsed ) if path.size > 0
200
+ end
201
+
202
+ #RelativeLocationPath
203
+ # | Step
204
+ # | (AXIS_NAME '::' | '@' | '') AxisSpecifier
205
+ # NodeTest
206
+ # Predicate
207
+ # | '.' | '..' AbbreviatedStep
208
+ # | RelativeLocationPath '/' Step
209
+ # | RelativeLocationPath '//' Step
210
+ AXIS = /^(ancestor|ancestor-or-self|attribute|child|descendant|descendant-or-self|following|following-sibling|namespace|parent|preceding|preceding-sibling|self)::/
211
+ def RelativeLocationPath path, parsed
212
+ #puts "RelativeLocationPath #{path}"
213
+ while path.size > 0
214
+ # (axis or @ or <child::>) nodetest predicate >
215
+ # OR > / Step
216
+ # (. or ..) >
217
+ if path[0] == ?.
218
+ if path[1] == ?.
219
+ parsed << :parent
220
+ parsed << :node
221
+ path = path[2..-1]
222
+ else
223
+ parsed << :self
224
+ parsed << :node
225
+ path = path[1..-1]
226
+ end
227
+ else
228
+ if path[0] == ?@
229
+ #puts "ATTRIBUTE"
230
+ parsed << :attribute
231
+ path = path[1..-1]
232
+ # Goto Nodetest
233
+ elsif path =~ AXIS
234
+ parsed << $1.tr('-','_').intern
235
+ path = $'
236
+ # Goto Nodetest
237
+ else
238
+ parsed << :child
239
+ end
240
+
241
+ #puts "NODETESTING '#{path}'"
242
+ n = []
243
+ path = NodeTest( path, n)
244
+ #puts "NODETEST RETURNED '#{path}'"
245
+
246
+ if path[0] == ?[
247
+ path = Predicate( path, n )
248
+ end
249
+
250
+ parsed.concat(n)
251
+ end
252
+
253
+ if path.size > 0
254
+ if path[0] == ?/
255
+ if path[1] == ?/
256
+ parsed << :descendant_or_self
257
+ parsed << :node
258
+ path = path[2..-1]
259
+ else
260
+ path = path[1..-1]
261
+ end
262
+ else
263
+ return path
264
+ end
265
+ end
266
+ end
267
+ return path
268
+ end
269
+
270
+ # Returns a 1-1 map of the nodeset
271
+ # The contents of the resulting array are either:
272
+ # true/false, if a positive match
273
+ # String, if a name match
274
+ #NodeTest
275
+ # | ('*' | NCNAME ':' '*' | QNAME) NameTest
276
+ # | NODE_TYPE '(' ')' NodeType
277
+ # | PI '(' LITERAL ')' PI
278
+ # | '[' expr ']' Predicate
279
+ NCNAMETEST= /^(#{NCNAME_STR}):\*/u
280
+ QNAME = Namespace::NAMESPLIT
281
+ NODE_TYPE = /^(comment|text|node)\(\s*\)/m
282
+ PI = /^processing-instruction\(/
283
+ def NodeTest path, parsed
284
+ #puts "NodeTest with #{path}"
285
+ res = nil
286
+ case path
287
+ when /^\*/
288
+ path = $'
289
+ parsed << :any
290
+ when NODE_TYPE
291
+ type = $1
292
+ path = $'
293
+ parsed << type.tr('-', '_').intern
294
+ when PI
295
+ path = $'
296
+ literal = nil
297
+ if path !~ /^\s*\)/
298
+ path =~ LITERAL
299
+ literal = $1
300
+ path = $'
301
+ raise ParseException.new("Missing ')' after processing instruction") if path[0] != ?)
302
+ path = path[1..-1]
303
+ end
304
+ parsed << :processing_instruction
305
+ parsed << (literal || '')
306
+ when NCNAMETEST
307
+ #puts "NCNAMETEST"
308
+ prefix = $1
309
+ path = $'
310
+ parsed << :namespace
311
+ parsed << prefix
312
+ when QNAME
313
+ #puts "QNAME"
314
+ prefix = $1
315
+ name = $2
316
+ path = $'
317
+ prefix = "" unless prefix
318
+ parsed << :qname
319
+ parsed << prefix
320
+ parsed << name
321
+ end
322
+ return path
323
+ end
324
+
325
+ # Filters the supplied nodeset on the predicate(s)
326
+ def Predicate path, parsed
327
+ #puts "PREDICATE with #{path}"
328
+ return nil unless path[0] == ?[
329
+ predicates = []
330
+ while path[0] == ?[
331
+ path, expr = get_group(path)
332
+ predicates << expr[1..-2] if expr
333
+ end
334
+ #puts "PREDICATES = #{predicates.inspect}"
335
+ predicates.each{ |expr|
336
+ #puts "ORING #{expr}"
337
+ preds = []
338
+ parsed << :predicate
339
+ parsed << preds
340
+ OrExpr(expr, preds)
341
+ }
342
+ #puts "PREDICATES = #{predicates.inspect}"
343
+ path
344
+ end
345
+
346
+ # The following return arrays of true/false, a 1-1 mapping of the
347
+ # supplied nodeset, except for axe(), which returns a filtered
348
+ # nodeset
349
+
350
+ #| OrExpr S 'or' S AndExpr
351
+ #| AndExpr
352
+ def OrExpr path, parsed
353
+ #puts "OR >>> #{path}"
354
+ n = []
355
+ rest = AndExpr( path, n )
356
+ #puts "OR <<< #{rest}"
357
+ if rest != path
358
+ while rest =~ /^\s*( or )/
359
+ n = [ :or, n, [] ]
360
+ rest = AndExpr( $', n[-1] )
361
+ end
362
+ end
363
+ if parsed.size == 0 and n.size != 0
364
+ parsed.replace(n)
365
+ elsif n.size > 0
366
+ parsed << n
367
+ end
368
+ rest
369
+ end
370
+
371
+ #| AndExpr S 'and' S EqualityExpr
372
+ #| EqualityExpr
373
+ def AndExpr path, parsed
374
+ #puts "AND >>> #{path}"
375
+ n = []
376
+ rest = EqualityExpr( path, n )
377
+ #puts "AND <<< #{rest}"
378
+ if rest != path
379
+ while rest =~ /^\s*( and )/
380
+ n = [ :and, n, [] ]
381
+ #puts "AND >>> #{rest}"
382
+ rest = EqualityExpr( $', n[-1] )
383
+ #puts "AND <<< #{rest}"
384
+ end
385
+ end
386
+ if parsed.size == 0 and n.size != 0
387
+ parsed.replace(n)
388
+ elsif n.size > 0
389
+ parsed << n
390
+ end
391
+ rest
392
+ end
393
+
394
+ #| EqualityExpr ('=' | '!=') RelationalExpr
395
+ #| RelationalExpr
396
+ def EqualityExpr path, parsed
397
+ #puts "EQUALITY >>> #{path}"
398
+ n = []
399
+ rest = RelationalExpr( path, n )
400
+ #puts "EQUALITY <<< #{rest}"
401
+ if rest != path
402
+ while rest =~ /^\s*(!?=)\s*/
403
+ if $1[0] == ?!
404
+ n = [ :neq, n, [] ]
405
+ else
406
+ n = [ :eq, n, [] ]
407
+ end
408
+ rest = RelationalExpr( $', n[-1] )
409
+ end
410
+ end
411
+ if parsed.size == 0 and n.size != 0
412
+ parsed.replace(n)
413
+ elsif n.size > 0
414
+ parsed << n
415
+ end
416
+ rest
417
+ end
418
+
419
+ #| RelationalExpr ('<' | '>' | '<=' | '>=') AdditiveExpr
420
+ #| AdditiveExpr
421
+ def RelationalExpr path, parsed
422
+ #puts "RELATION >>> #{path}"
423
+ n = []
424
+ rest = AdditiveExpr( path, n )
425
+ #puts "RELATION <<< #{rest}"
426
+ if rest != path
427
+ while rest =~ /^\s*([<>]=?)\s*/
428
+ if $1[0] == ?<
429
+ sym = "lt"
430
+ else
431
+ sym = "gt"
432
+ end
433
+ sym << "eq" if $1[-1] == ?=
434
+ n = [ sym.intern, n, [] ]
435
+ rest = AdditiveExpr( $', n[-1] )
436
+ end
437
+ end
438
+ if parsed.size == 0 and n.size != 0
439
+ parsed.replace(n)
440
+ elsif n.size > 0
441
+ parsed << n
442
+ end
443
+ rest
444
+ end
445
+
446
+ #| AdditiveExpr ('+' | S '-') MultiplicativeExpr
447
+ #| MultiplicativeExpr
448
+ def AdditiveExpr path, parsed
449
+ #puts "ADDITIVE >>> #{path}"
450
+ n = []
451
+ rest = MultiplicativeExpr( path, n )
452
+ #puts "ADDITIVE <<< #{rest}"
453
+ if rest != path
454
+ while rest =~ /^\s*(\+| -)\s*/
455
+ if $1[0] == ?+
456
+ n = [ :plus, n, [] ]
457
+ else
458
+ n = [ :minus, n, [] ]
459
+ end
460
+ rest = MultiplicativeExpr( $', n[-1] )
461
+ end
462
+ end
463
+ if parsed.size == 0 and n.size != 0
464
+ parsed.replace(n)
465
+ elsif n.size > 0
466
+ parsed << n
467
+ end
468
+ rest
469
+ end
470
+
471
+ #| MultiplicativeExpr ('*' | S ('div' | 'mod') S) UnaryExpr
472
+ #| UnaryExpr
473
+ def MultiplicativeExpr path, parsed
474
+ #puts "MULT >>> #{path}"
475
+ n = []
476
+ rest = UnaryExpr( path, n )
477
+ #puts "MULT <<< #{rest}"
478
+ if rest != path
479
+ while rest =~ /^\s*(\*| div | mod )\s*/
480
+ if $1[0] == ?*
481
+ n = [ :mult, n, [] ]
482
+ elsif $1.include?( "div" )
483
+ n = [ :div, n, [] ]
484
+ else
485
+ n = [ :mod, n, [] ]
486
+ end
487
+ rest = UnaryExpr( $', n[-1] )
488
+ end
489
+ end
490
+ if parsed.size == 0 and n.size != 0
491
+ parsed.replace(n)
492
+ elsif n.size > 0
493
+ parsed << n
494
+ end
495
+ rest
496
+ end
497
+
498
+ #| '-' UnaryExpr
499
+ #| UnionExpr
500
+ def UnaryExpr path, parsed
501
+ path =~ /^(\-*)/
502
+ path = $'
503
+ if $1 and (($1.size % 2) != 0)
504
+ mult = -1
505
+ else
506
+ mult = 1
507
+ end
508
+ parsed << :neg if mult < 0
509
+
510
+ #puts "UNARY >>> #{path}"
511
+ n = []
512
+ path = UnionExpr( path, n )
513
+ #puts "UNARY <<< #{path}"
514
+ parsed.concat( n )
515
+ path
516
+ end
517
+
518
+ #| UnionExpr '|' PathExpr
519
+ #| PathExpr
520
+ def UnionExpr path, parsed
521
+ #puts "UNION >>> #{path}"
522
+ n = []
523
+ rest = PathExpr( path, n )
524
+ #puts "UNION <<< #{rest}"
525
+ if rest != path
526
+ while rest =~ /^\s*(\|)\s*/
527
+ n = [ :union, n, [] ]
528
+ rest = PathExpr( $', n[-1] )
529
+ end
530
+ end
531
+ if parsed.size == 0 and n.size != 0
532
+ parsed.replace( n )
533
+ elsif n.size > 0
534
+ parsed << n
535
+ end
536
+ rest
537
+ end
538
+
539
+ #| LocationPath
540
+ #| FilterExpr ('/' | '//') RelativeLocationPath
541
+ def PathExpr path, parsed
542
+ path =~ /^\s*/
543
+ path = $'
544
+ #puts "PATH >>> #{path}"
545
+ n = []
546
+ rest = FilterExpr( path, n )
547
+ #puts "PATH <<< '#{rest}'"
548
+ if rest != path
549
+ if rest and rest[0] == ?/
550
+ return RelativeLocationPath(rest, n)
551
+ end
552
+ end
553
+ #puts "BEFORE WITH '#{rest}'"
554
+ rest = LocationPath(rest, n) if rest =~ /\A[\/\.\@\[\w_*]/
555
+ parsed.concat(n)
556
+ return rest
557
+ end
558
+
559
+ #| FilterExpr Predicate
560
+ #| PrimaryExpr
561
+ def FilterExpr path, parsed
562
+ #puts "FILTER >>> #{path}"
563
+ n = []
564
+ path = PrimaryExpr( path, n )
565
+ #puts "FILTER <<< #{path}"
566
+ path = Predicate(path, n) if path and path[0] == ?[
567
+ #puts "FILTER <<< #{path}"
568
+ parsed.concat(n)
569
+ path
570
+ end
571
+
572
+ #| VARIABLE_REFERENCE
573
+ #| '(' expr ')'
574
+ #| LITERAL
575
+ #| NUMBER
576
+ #| FunctionCall
577
+ VARIABLE_REFERENCE = /^\$(#{NAME_STR})/u
578
+ NUMBER = /^(\d*\.?\d+)/
579
+ NT = /^comment|text|processing-instruction|node$/
580
+ def PrimaryExpr path, parsed
581
+ arry = []
582
+ case path
583
+ when VARIABLE_REFERENCE
584
+ varname = $1
585
+ path = $'
586
+ parsed << :variable
587
+ parsed << varname
588
+ #arry << @variables[ varname ]
589
+ when /^(\w[-\w]*)(?:\()/
590
+ #puts "PrimaryExpr :: Function >>> #$1 -- '#$''"
591
+ fname = $1
592
+ tmp = $'
593
+ #puts "#{fname} =~ #{NT.inspect}"
594
+ return path if fname =~ NT
595
+ path = tmp
596
+ parsed << :function
597
+ parsed << fname
598
+ path = FunctionCall(path, parsed)
599
+ when NUMBER
600
+ #puts "LITERAL or NUMBER: #$1"
601
+ varname = $1.nil? ? $2 : $1
602
+ path = $'
603
+ parsed << :literal
604
+ parsed << (varname.include?('.') ? varname.to_f : varname.to_i)
605
+ when LITERAL
606
+ #puts "LITERAL or NUMBER: #$1"
607
+ varname = $1.nil? ? $2 : $1
608
+ path = $'
609
+ parsed << :literal
610
+ parsed << varname
611
+ when /^\(/ #/
612
+ path, contents = get_group(path)
613
+ contents = contents[1..-2]
614
+ n = []
615
+ OrExpr( contents, n )
616
+ parsed.concat(n)
617
+ end
618
+ path
619
+ end
620
+
621
+ #| FUNCTION_NAME '(' ( expr ( ',' expr )* )? ')'
622
+ def FunctionCall rest, parsed
623
+ path, arguments = parse_args(rest)
624
+ argset = []
625
+ for argument in arguments
626
+ args = []
627
+ OrExpr( argument, args )
628
+ argset << args
629
+ end
630
+ parsed << argset
631
+ path
632
+ end
633
+
634
+ # get_group( '[foo]bar' ) -> ['bar', '[foo]']
635
+ def get_group string
636
+ ind = 0
637
+ depth = 0
638
+ st = string[0,1]
639
+ en = (st == "(" ? ")" : "]")
640
+ begin
641
+ case string[ind,1]
642
+ when st
643
+ depth += 1
644
+ when en
645
+ depth -= 1
646
+ end
647
+ ind += 1
648
+ end while depth > 0 and ind < string.length
649
+ return nil unless depth==0
650
+ [string[ind..-1], string[0..ind-1]]
651
+ end
652
+
653
+ def parse_args( string )
654
+ arguments = []
655
+ ind = 0
656
+ inquot = false
657
+ inapos = false
658
+ depth = 1
659
+ begin
660
+ case string[ind]
661
+ when ?"
662
+ inquot = !inquot unless inapos
663
+ when ?'
664
+ inapos = !inapos unless inquot
665
+ else
666
+ unless inquot or inapos
667
+ case string[ind]
668
+ when ?(
669
+ depth += 1
670
+ if depth == 1
671
+ string = string[1..-1]
672
+ ind -= 1
673
+ end
674
+ when ?)
675
+ depth -= 1
676
+ if depth == 0
677
+ s = string[0,ind].strip
678
+ arguments << s unless s == ""
679
+ string = string[ind+1..-1]
680
+ end
681
+ when ?,
682
+ if depth == 1
683
+ s = string[0,ind].strip
684
+ arguments << s unless s == ""
685
+ string = string[ind+1..-1]
686
+ ind = -1
687
+ end
688
+ end
689
+ end
690
+ end
691
+ ind += 1
692
+ end while depth > 0 and ind < string.length
693
+ return nil unless depth==0
694
+ [string,arguments]
695
+ end
696
+ end
697
+ end
698
+ end