xml-mapping 0.8.1 → 0.9.1

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 (71) hide show
  1. data/ChangeLog +64 -3
  2. data/README +871 -173
  3. data/README_XPATH +40 -13
  4. data/Rakefile +37 -26
  5. data/TODO.txt +39 -8
  6. data/examples/README +5 -0
  7. data/examples/company_usage.intout +34 -22
  8. data/examples/documents_folders.rb +31 -0
  9. data/examples/documents_folders.xml +16 -0
  10. data/examples/documents_folders_usage.intin.rb +18 -0
  11. data/examples/documents_folders_usage.intout +46 -0
  12. data/examples/order_signature_enhanced_usage.intout +21 -11
  13. data/examples/order_usage.intin.rb +52 -5
  14. data/examples/order_usage.intout +154 -80
  15. data/examples/person.intin.rb +44 -0
  16. data/examples/person.intout +27 -0
  17. data/examples/person_mm.intin.rb +119 -0
  18. data/examples/person_mm.intout +114 -0
  19. data/examples/publication.intin.rb +44 -0
  20. data/examples/publication.intout +20 -0
  21. data/examples/reader.intin.rb +33 -0
  22. data/examples/reader.intout +19 -0
  23. data/examples/stringarray.rb +5 -0
  24. data/examples/stringarray.xml +10 -0
  25. data/examples/stringarray_usage.intin.rb +11 -0
  26. data/examples/stringarray_usage.intout +31 -0
  27. data/examples/time_augm.intout +19 -7
  28. data/examples/time_augm_loading.intin.rb +44 -0
  29. data/examples/time_augm_loading.intout +12 -0
  30. data/examples/time_node.intin.rb +79 -0
  31. data/examples/time_node.rb +3 -2
  32. data/examples/time_node_w_marshallers.intin.rb +48 -0
  33. data/examples/time_node_w_marshallers.intout +25 -0
  34. data/examples/time_node_w_marshallers.xml +9 -0
  35. data/examples/xpath_create_new.intout +132 -114
  36. data/examples/xpath_ensure_created.intout +86 -65
  37. data/examples/xpath_pathological.intout +16 -16
  38. data/examples/xpath_usage.intout +1 -1
  39. data/install.rb +1 -0
  40. data/lib/xml/mapping.rb +3 -1
  41. data/lib/xml/mapping/base.rb +442 -272
  42. data/lib/xml/mapping/core_classes_mapping.rb +32 -0
  43. data/lib/xml/mapping/standard_nodes.rb +176 -86
  44. data/lib/xml/mapping/version.rb +2 -2
  45. data/lib/xml/rexml_ext.rb +186 -0
  46. data/lib/xml/xxpath.rb +28 -265
  47. data/lib/xml/xxpath/steps.rb +345 -0
  48. data/lib/xml/xxpath_methods.rb +96 -0
  49. data/test/all_tests.rb +4 -1
  50. data/test/benchmark_fixtures.rb +14 -0
  51. data/test/{multiple_mappings.rb → bookmarks.rb} +0 -0
  52. data/test/company.rb +47 -0
  53. data/test/documents_folders.rb +11 -1
  54. data/test/examples_test.rb +29 -0
  55. data/test/fixtures/benchmark.xml +77 -0
  56. data/test/fixtures/company1.xml +9 -0
  57. data/test/fixtures/documents_folders.xml +0 -8
  58. data/test/fixtures/documents_folders2.xml +13 -19
  59. data/test/fixtures/triangle_m1.xml +17 -0
  60. data/test/fixtures/triangle_m2.xml +19 -0
  61. data/test/inheritance_test.rb +50 -0
  62. data/test/multiple_mappings_test.rb +155 -0
  63. data/test/rexml_xpath_benchmark.rb +29 -0
  64. data/test/triangle_mm.rb +57 -0
  65. data/test/xml_mapping_adv_test.rb +36 -1
  66. data/test/xml_mapping_test.rb +136 -7
  67. data/test/xpath_test.rb +154 -0
  68. data/test/xxpath_benchmark.rb +36 -0
  69. data/test/xxpath_benchmark.result1.txt +17 -0
  70. data/test/xxpath_methods_test.rb +61 -0
  71. metadata +139 -90
@@ -1,7 +1,9 @@
1
- # xpath.rb -- XPath implementation for Ruby, including write access
2
- # Copyright (C) 2004,2005 Olaf Klischat
1
+ # xxpath -- XPath implementation for Ruby, including write access
2
+ # Copyright (C) 2004-2006 Olaf Klischat
3
3
 
4
4
  require 'rexml/document'
5
+ require 'xml/rexml_ext'
6
+ require 'xml/xxpath/steps'
5
7
 
6
8
  module XML
7
9
 
@@ -19,91 +21,34 @@ module XML
19
21
  def initialize(xpathstr)
20
22
  @xpathstr = xpathstr # for error messages
21
23
 
22
- xpathstr=xpathstr[1..-1] if xpathstr[0]==?/
24
+ # TODO: write a real XPath parser sometime
23
25
 
24
- # TODO: avoid code duplications
25
- # maybe: build & create the procs using eval
26
+ xpathstr='/'+xpathstr if xpathstr[0] != ?/
26
27
 
27
28
  @creator_procs = [ proc{|node,create_new| node} ]
28
29
  @reader_proc = proc {|nodes| nodes}
29
- xpathstr.split('/').reverse.each do |part|
30
- prev_creator = @creator_procs[-1]
31
- prev_reader = @reader_proc
32
- case part
33
- when /^(.*?)\[@(.*?)='(.*?)'\]$/
34
- name,attr_name,attr_value = [$1,$2,$3]
35
- @creator_procs << curr_creator = proc {|node,create_new|
36
- prev_creator.call(Accessors.create_subnode_by_name_and_attr(node,create_new,
37
- name,attr_name,attr_value),
38
- create_new)
39
- }
40
- @reader_proc = proc {|nodes|
41
- next_nodes = Accessors.subnodes_by_name_and_attr(nodes,
42
- name,attr_name,attr_value)
43
- if (next_nodes == [])
44
- throw :not_found, [nodes,curr_creator]
45
- else
46
- prev_reader.call(next_nodes)
47
- end
48
- }
49
- when /^(.*?)\[(.*?)\]$/
50
- name,index = [$1,$2.to_i]
51
- @creator_procs << curr_creator = proc {|node,create_new|
52
- prev_creator.call(Accessors.create_subnode_by_name_and_index(node,create_new,
53
- name,index),
54
- create_new)
55
- }
56
- @reader_proc = proc {|nodes|
57
- next_nodes = Accessors.subnodes_by_name_and_index(nodes,
58
- name,index)
59
- if (next_nodes == [])
60
- throw :not_found, [nodes,curr_creator]
61
- else
62
- prev_reader.call(next_nodes)
63
- end
64
- }
65
- when /^@(.*)$/
66
- name = $1
67
- @creator_procs << curr_creator = proc {|node,create_new|
68
- prev_creator.call(Accessors.create_subnode_by_attr_name(node,create_new,name),
69
- create_new)
70
- }
71
- @reader_proc = proc {|nodes|
72
- next_nodes = Accessors.subnodes_by_attr_name(nodes,name)
73
- if (next_nodes == [])
74
- throw :not_found, [nodes,curr_creator]
75
- else
76
- prev_reader.call(next_nodes)
77
- end
78
- }
79
- when '*'
80
- @creator_procs << curr_creator = proc {|node,create_new|
81
- prev_creator.call(Accessors.create_subnode_by_all(node,create_new),
82
- create_new)
83
- }
84
- @reader_proc = proc {|nodes|
85
- next_nodes = Accessors.subnodes_by_all(nodes)
86
- if (next_nodes == [])
87
- throw :not_found, [nodes,curr_creator]
88
- else
89
- prev_reader.call(next_nodes)
90
- end
91
- }
92
- else
93
- name = part
94
- @creator_procs << curr_creator = proc {|node,create_new|
95
- prev_creator.call(Accessors.create_subnode_by_name(node,create_new,name),
96
- create_new)
97
- }
98
- @reader_proc = proc {|nodes|
99
- next_nodes = Accessors.subnodes_by_name(nodes,name)
100
- if (next_nodes == [])
101
- throw :not_found, [nodes,curr_creator]
102
- else
103
- prev_reader.call(next_nodes)
104
- end
105
- }
106
- end
30
+
31
+ part=nil; part_expected=true
32
+ xpathstr.split(/(\/+)/)[1..-1].reverse.each do |x|
33
+ if part_expected
34
+ part=x
35
+ part_expected = false
36
+ next
37
+ end
38
+ part_expected = true
39
+ axis = case x
40
+ when '/'
41
+ :child
42
+ when '//'
43
+ :descendant
44
+ else
45
+ raise XXPathError, "XPath (#{xpathstr}): unknown axis: #{x}"
46
+ end
47
+ axis=:self if axis==:child and (part[0]==?. or part=~/^self::/) # yuck
48
+
49
+ step = Step.compile(axis,part)
50
+ @creator_procs << step.creator(@creator_procs[-1])
51
+ @reader_proc = step.reader(@reader_proc, @creator_procs[-1])
107
52
  end
108
53
  end
109
54
 
@@ -167,188 +112,6 @@ module XML
167
112
  first(base_node,:create_new=>true)
168
113
  end
169
114
 
170
-
171
- module Accessors #:nodoc:
172
-
173
- # we need a boolean "unspecified?" attribute for XML nodes --
174
- # paths like "*" oder (somewhen) "foo|bar" create "unspecified"
175
- # nodes that the user must then "specify" by setting their text
176
- # etc. (or manually setting unspecified=false)
177
- #
178
- # This is mixed into the REXML::Element and
179
- # XML::XXPath::Accessors::Attribute classes.
180
- module UnspecifiednessSupport
181
-
182
- def unspecified?
183
- @xml_xpath_unspecified ||= false
184
- end
185
-
186
- def unspecified=(x)
187
- @xml_xpath_unspecified = x
188
- end
189
-
190
- def self.included(mod)
191
- mod.module_eval <<-EOS
192
- alias_method :_text_orig, :text
193
- alias_method :_textis_orig, :text=
194
- def text
195
- # we're suffering from the "fragile base class"
196
- # phenomenon here -- we don't know whether the
197
- # implementation of the class we get mixed into always
198
- # calls text (instead of just accessing @text or so)
199
- if unspecified?
200
- "[UNSPECIFIED]"
201
- else
202
- _text_orig
203
- end
204
- end
205
- def text=(x)
206
- _textis_orig(x)
207
- self.unspecified=false
208
- end
209
-
210
- alias_method :_nameis_orig, :name=
211
- def name=(x)
212
- _nameis_orig(x)
213
- self.unspecified=false
214
- end
215
- EOS
216
- end
217
-
218
- end
219
-
220
- class REXML::Element #:nodoc:
221
- include UnspecifiednessSupport
222
- end
223
-
224
- # attribute node, half-way compatible
225
- # with REXML's Element.
226
- # REXML doesn't provide one...
227
- #
228
- # The all/first calls return instances of this class if they
229
- # matched an attribute node.
230
- class Attribute
231
- attr_reader :parent, :name
232
- attr_writer :name
233
-
234
- def initialize(parent,name)
235
- @parent,@name = parent,name
236
- end
237
-
238
- def self.new(parent,name,create)
239
- if parent.attributes[name]
240
- super(parent,name)
241
- else
242
- if create
243
- parent.attributes[name] = "[unset]"
244
- super(parent,name)
245
- else
246
- nil
247
- end
248
- end
249
- end
250
-
251
- # the value of the attribute.
252
- def text
253
- parent.attributes[@name]
254
- end
255
-
256
- def text=(x)
257
- parent.attributes[@name] = x
258
- end
259
-
260
- def ==(other)
261
- other.kind_of?(Attribute) and other.parent==parent and other.name==name
262
- end
263
-
264
- include UnspecifiednessSupport
265
- end
266
-
267
- # read accessors
268
-
269
- for things in %w{name name_and_attr name_and_index attr_name all} do
270
- self.module_eval <<-EOS
271
- def self.subnodes_by_#{things}(nodes, *args)
272
- nodes.map{|node| subnodes_by_#{things}_singlesrc(node,*args)}.flatten
273
- end
274
- EOS
275
- end
276
-
277
- def self.subnodes_by_name_singlesrc(node,name)
278
- node.elements.select{|elt| elt.name==name}
279
- end
280
-
281
- def self.subnodes_by_name_and_attr_singlesrc(node,name,attr_name,attr_value)
282
- node.elements.select{|elt| elt.name==name and elt.attributes[attr_name]==attr_value}
283
- end
284
-
285
- def self.subnodes_by_name_and_index_singlesrc(node,name,index)
286
- index-=1
287
- byname=subnodes_by_name_singlesrc(node,name)
288
- if index>=byname.size
289
- []
290
- else
291
- [byname[index]]
292
- end
293
- end
294
-
295
- def self.subnodes_by_attr_name_singlesrc(node,name)
296
- attr=Attribute.new(node,name,false)
297
- if attr then [attr] else [] end
298
- end
299
-
300
- def self.subnodes_by_all_singlesrc(node)
301
- node.elements.to_a
302
- end
303
-
304
-
305
- # write accessors
306
-
307
- # precondition: unless create_new, we know that a node with
308
- # exactly the requested attributes doesn't exist yet (else we
309
- # wouldn't have been called)
310
- def self.create_subnode_by_name(node,create_new,name)
311
- node.elements.add name
312
- end
313
-
314
- def self.create_subnode_by_name_and_attr(node,create_new,name,attr_name,attr_value)
315
- if create_new
316
- newnode = node.elements.add(name)
317
- else
318
- newnode = subnodes_by_name_singlesrc(node,name)[0]
319
- if not(newnode) or newnode.attributes[attr_name]
320
- newnode = node.elements.add(name)
321
- end
322
- end
323
- newnode.attributes[attr_name]=attr_value
324
- newnode
325
- end
326
-
327
- def self.create_subnode_by_name_and_index(node,create_new,name,index)
328
- name_matches = subnodes_by_name_singlesrc(node,name)
329
- if create_new and (name_matches.size >= index)
330
- raise XXPathError, "XPath (#{@xpathstr}): #{name}[#{index}]: create_new and element already exists"
331
- end
332
- newnode = name_matches[0]
333
- (index-name_matches.size).times do
334
- newnode = node.elements.add name
335
- end
336
- newnode
337
- end
338
-
339
- def self.create_subnode_by_attr_name(node,create_new,name)
340
- if create_new and node.attributes[name]
341
- raise XXPathError, "XPath (#{@xpathstr}): @#{name}: create_new and attribute already exists"
342
- end
343
- Attribute.new(node,name,true)
344
- end
345
-
346
- def self.create_subnode_by_all(node,create_new)
347
- node = node.elements.add
348
- node.unspecified = true
349
- node
350
- end
351
- end
352
115
  end
353
116
 
354
117
  end
@@ -0,0 +1,345 @@
1
+ # xxpath -- XPath implementation for Ruby, including write access
2
+ # Copyright (C) 2004-2006 Olaf Klischat
3
+
4
+ module XML
5
+ class XXPath
6
+
7
+ # base class for XPath "steps". Steps contain an "axis" (e.g.
8
+ # "/", "//", i.e. the "child" resp. "descendants" axis), and
9
+ # a "node matcher" like "foo" or "@bar" or "foo[@bar='baz']", i.e. they
10
+ # match some XML nodes and don't match others (e.g. "foo[@bar='baz']"
11
+ # macthes all XML element nodes named "foo" that contain an attribute
12
+ # with name "bar" and value "baz").
13
+ #
14
+ # Steps can find out whether they match a given XML node
15
+ # (Step#matches?(node)), and they know how to create a matchingnode
16
+ # on a given base node (Step#create_on(node,create_new)).
17
+ class Step #:nodoc:
18
+ def self.inherited(subclass)
19
+ (@subclasses||=[]) << subclass
20
+ end
21
+
22
+ # create and return an instance of the right Step subclass for
23
+ # axis _axis_ (:child or :descendant atm.) and node matcher _string_
24
+ def self.compile axis, string
25
+ (@subclasses||=[]).each do |sc|
26
+ obj = sc.compile axis, string
27
+ return obj if obj
28
+ end
29
+ raise XXPathError, "can't compile XPath step: #{string}"
30
+ end
31
+
32
+ def initialize axis
33
+ @axis = axis
34
+ end
35
+
36
+ # return a proc that takes a list of nodes, finds all sub-nodes
37
+ # that are reachable from one of those nodes via _self_'s axis
38
+ # and match (see below) _self_, and calls _prev_reader_ on them
39
+ # (and returns the result). When the proc doesn't find any such
40
+ # nodes, it throws <tt>:not_found,
41
+ # [nodes,creator_from_here]</tt>.
42
+ #
43
+ # Needed for compiling whole XPath expressions for reading.
44
+ #
45
+ # <tt>Step</tt> itself provides a generic default implementation
46
+ # which checks whether _self_ matches a given node by calling
47
+ # self.matches?(node). Subclasses must either implement such a
48
+ # _matches?_ method or override _reader_ to provide more
49
+ # specialized implementations for better performance.
50
+ def reader(prev_reader,creator_from_here)
51
+ proc {|nodes|
52
+ next_nodes = []
53
+ nodes.each do |node|
54
+ if node.respond_to? :each_on_axis
55
+ node.each_on_axis(@axis) do |subnode|
56
+ next_nodes << subnode if self.matches?(subnode)
57
+ end
58
+ end
59
+ end
60
+ if (next_nodes.empty?)
61
+ throw :not_found, [nodes,creator_from_here]
62
+ else
63
+ prev_reader.call(next_nodes)
64
+ end
65
+ }
66
+ end
67
+
68
+ # return a proc that takes a node, creates a sub-node matching
69
+ # _self_ on it, and then calls _prev_creator_ on that and
70
+ # returns the result.
71
+ #
72
+ # Needed for compiling whole XPath expressions for writing.
73
+ #
74
+ # <tt>Step</tt> itself provides a generic default
75
+ # implementation, subclasses may provide specialized
76
+ # implementations for better performance.
77
+ def creator(prev_creator)
78
+ if @axis==:child or @axis==:self
79
+ proc {|node,create_new|
80
+ prev_creator.call(self.create_on(node,create_new),
81
+ create_new)
82
+ }
83
+ else
84
+ proc {|node,create_new|
85
+ raise XXPathError, "can't create axis: #{@axis}"
86
+ }
87
+ end
88
+ end
89
+ end
90
+
91
+
92
+ class AttrStep < Step #:nodoc:
93
+ def self.compile axis, string
94
+ /^(?:\.|self::\*)\[@(.*?)='(.*?)'\]$/ === string or return nil
95
+ self.new axis,$1,$2
96
+ end
97
+
98
+ def initialize(axis,attr_name,attr_value)
99
+ super(axis)
100
+ @attr_name,@attr_value = attr_name,attr_value
101
+ end
102
+
103
+ def matches? node
104
+ node.is_a?(REXML::Element) and node.attributes[@attr_name]==@attr_value
105
+ end
106
+
107
+ def create_on(node,create_new)
108
+ if create_new
109
+ raise XXPathError, "XPath: .[@'#{@attr_name}'='#{@attr_value}']: create_new but context node already exists"
110
+ end
111
+ # TODO: raise if node.attributes[@attr_name] already exists?
112
+ node.attributes[@attr_name]=@attr_value
113
+ node
114
+ end
115
+ end
116
+
117
+
118
+ class NameAndAttrStep < Step #:nodoc:
119
+ def self.compile axis, string
120
+ /^(.*?)\[@(.*?)='(.*?)'\]$/ === string or return nil
121
+ self.new axis,$1,$2,$3
122
+ end
123
+
124
+ def initialize(axis,name,attr_name,attr_value)
125
+ super(axis)
126
+ @name,@attr_name,@attr_value = name,attr_name,attr_value
127
+ end
128
+
129
+ def matches? node
130
+ node.is_a?(REXML::Element) and node.name==@name and node.attributes[@attr_name]==@attr_value
131
+ end
132
+
133
+ def create_on(node,create_new)
134
+ if create_new
135
+ newnode = node.elements.add(@name)
136
+ else
137
+ newnode = node.elements.select{|elt| elt.name==@name and not(elt.attributes[@attr_name])}[0]
138
+ if not(newnode)
139
+ newnode = node.elements.add(@name)
140
+ end
141
+ end
142
+ newnode.attributes[@attr_name]=@attr_value
143
+ newnode
144
+ end
145
+ end
146
+
147
+
148
+ class NameAndIndexStep < Step #:nodoc:
149
+ def self.compile axis, string
150
+ /^(.*?)\[(\d+)\]$/ === string or return nil
151
+ self.new axis,$1,$2.to_i
152
+ end
153
+
154
+ def initialize(axis,name,index)
155
+ super(axis)
156
+ @name,@index = name,index
157
+ end
158
+
159
+ def matches? node
160
+ raise XXPathError, "can't use #{@name}[#{@index}] on root node" if node.parent.nil?
161
+ node == node.parent.elements.select{|elt| elt.name==@name}[@index-1]
162
+ end
163
+
164
+ def create_on(node,create_new)
165
+ name_matches = node.elements.select{|elt| elt.name==@name}
166
+ if create_new and (name_matches.size >= @index)
167
+ raise XXPathError, "XPath: #{@name}[#{@index}]: create_new and element already exists"
168
+ end
169
+ newnode = name_matches[0]
170
+ (@index-name_matches.size).times do
171
+ newnode = node.elements.add @name
172
+ end
173
+ newnode
174
+ end
175
+
176
+ def reader(prev_reader,creator_from_here)
177
+ if @axis==:child
178
+ index = @index - 1
179
+ proc {|nodes|
180
+ next_nodes = []
181
+ nodes.each do |node|
182
+ byname=node.elements.select{|elt| elt.name==@name}
183
+ next_nodes << byname[index] if index<byname.size
184
+ end
185
+ if (next_nodes.empty?)
186
+ throw :not_found, [nodes,creator_from_here]
187
+ else
188
+ prev_reader.call(next_nodes)
189
+ end
190
+ }
191
+ else
192
+ super(prev_reader,creator_from_here)
193
+ end
194
+ end
195
+ end
196
+
197
+
198
+ class AttrNameStep < Step #:nodoc:
199
+ def self.compile axis, string
200
+ /^@(.*)$/ === string or return nil
201
+ self.new axis,$1
202
+ end
203
+
204
+ def initialize(axis,attr_name)
205
+ super(axis)
206
+ @attr_name = attr_name
207
+ end
208
+
209
+ def matches? node
210
+ node.class==XML::XXPath::Accessors::Attribute and node.name==@attr_name
211
+ end
212
+
213
+ def create_on(node,create_new)
214
+ if create_new and node.attributes[@attr_name]
215
+ raise XXPathError, "XPath (@#{@attr_name}): create_new and attribute already exists"
216
+ end
217
+ XML::XXPath::Accessors::Attribute.new(node,@attr_name,true)
218
+ end
219
+
220
+ def reader(prev_reader,creator_from_here)
221
+ if @axis==:child
222
+ proc {|nodes|
223
+ next_nodes = []
224
+ nodes.each do |node|
225
+ attr=XML::XXPath::Accessors::Attribute.new(node,@attr_name,false)
226
+ next_nodes << attr if attr
227
+ end
228
+ if (next_nodes.empty?)
229
+ throw :not_found, [nodes,creator_from_here]
230
+ else
231
+ prev_reader.call(next_nodes)
232
+ end
233
+ }
234
+ else
235
+ super(prev_reader,creator_from_here)
236
+ end
237
+ end
238
+ end
239
+
240
+
241
+ class AllElementsStep < Step #:nodoc:
242
+ def self.compile axis, string
243
+ '*'==string or return nil
244
+ self.new axis
245
+ end
246
+
247
+ def matches? node
248
+ node.is_a? REXML::Element
249
+ end
250
+
251
+ def create_on(node,create_new)
252
+ newnode = node.elements.add
253
+ newnode.unspecified = true
254
+ newnode
255
+ end
256
+ end
257
+
258
+
259
+ class ThisNodeStep < Step #:nodoc:
260
+ def self.compile axis, string
261
+ '.'==string or return nil
262
+ self.new axis
263
+ end
264
+
265
+ def matches? node
266
+ true
267
+ end
268
+
269
+ def create_on(node,create_new)
270
+ if create_new
271
+ raise XXPathError, "XPath: .: create_new and attribute already exists"
272
+ end
273
+ node
274
+ end
275
+ end
276
+
277
+
278
+ class AlternativeNamesStep < Step #:nodoc:
279
+ def self.compile axis, string
280
+ if string=~/\|/
281
+ self.new axis, string.split('|')
282
+ else
283
+ nil
284
+ end
285
+ end
286
+
287
+ def initialize(axis,names)
288
+ super(axis)
289
+ @names = names
290
+ end
291
+
292
+ def matches? node
293
+ node.is_a?(REXML::Element) and @names.inject(false){|prev,name| prev or node.name==name}
294
+ end
295
+
296
+ def create_on(node,create_new)
297
+ newnode = node.elements.add
298
+ newnode.unspecified = true
299
+ newnode
300
+ end
301
+ end
302
+
303
+
304
+ class TextNodesStep < Step #:nodoc:
305
+ def self.compile axis, string
306
+ 'text()' == string or return nil
307
+ self.new axis
308
+ end
309
+
310
+ def matches? node
311
+ node.is_a? REXML::Text
312
+ end
313
+
314
+ def create_on(node,create_new)
315
+ node.add(REXML::Text.new(""))
316
+ end
317
+ end
318
+
319
+ class REXML::Text
320
+ # call-compatibility w/ REXML::Element
321
+ alias_method :text, :value
322
+ alias_method :text=, :value=
323
+ end
324
+
325
+
326
+ class NameStep < Step #:nodoc:
327
+ def self.compile axis, string
328
+ self.new axis,string
329
+ end
330
+
331
+ def initialize(axis,name)
332
+ super(axis)
333
+ @name = name
334
+ end
335
+
336
+ def matches? node
337
+ node.is_a?(REXML::Element) and node.name==@name
338
+ end
339
+
340
+ def create_on(node,create_new)
341
+ node.elements.add @name
342
+ end
343
+ end
344
+ end
345
+ end