xml-mapping 0.8.1 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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