rexml 3.2.3 → 3.3.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rexml might be problematic. Click here for more details.

Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS.md +502 -0
  3. data/README.md +11 -14
  4. data/doc/rexml/context.rdoc +143 -0
  5. data/doc/rexml/tasks/rdoc/child.rdoc +87 -0
  6. data/doc/rexml/tasks/rdoc/document.rdoc +276 -0
  7. data/doc/rexml/tasks/rdoc/element.rdoc +602 -0
  8. data/doc/rexml/tasks/rdoc/node.rdoc +97 -0
  9. data/doc/rexml/tasks/rdoc/parent.rdoc +267 -0
  10. data/doc/rexml/tasks/tocs/child_toc.rdoc +12 -0
  11. data/doc/rexml/tasks/tocs/document_toc.rdoc +30 -0
  12. data/doc/rexml/tasks/tocs/element_toc.rdoc +55 -0
  13. data/doc/rexml/tasks/tocs/master_toc.rdoc +135 -0
  14. data/doc/rexml/tasks/tocs/node_toc.rdoc +16 -0
  15. data/doc/rexml/tasks/tocs/parent_toc.rdoc +25 -0
  16. data/doc/rexml/tutorial.rdoc +1358 -0
  17. data/lib/rexml/attribute.rb +17 -11
  18. data/lib/rexml/doctype.rb +55 -31
  19. data/lib/rexml/document.rb +199 -35
  20. data/lib/rexml/element.rb +1802 -487
  21. data/lib/rexml/entity.rb +10 -39
  22. data/lib/rexml/formatters/pretty.rb +3 -3
  23. data/lib/rexml/functions.rb +1 -2
  24. data/lib/rexml/light/node.rb +0 -8
  25. data/lib/rexml/namespace.rb +8 -4
  26. data/lib/rexml/node.rb +8 -4
  27. data/lib/rexml/parseexception.rb +1 -0
  28. data/lib/rexml/parsers/baseparser.rb +513 -250
  29. data/lib/rexml/parsers/pullparser.rb +12 -0
  30. data/lib/rexml/parsers/sax2parser.rb +16 -19
  31. data/lib/rexml/parsers/streamparser.rb +16 -10
  32. data/lib/rexml/parsers/treeparser.rb +9 -21
  33. data/lib/rexml/parsers/xpathparser.rb +161 -97
  34. data/lib/rexml/rexml.rb +29 -22
  35. data/lib/rexml/source.rb +128 -98
  36. data/lib/rexml/text.rb +46 -22
  37. data/lib/rexml/xpath_parser.rb +43 -33
  38. data/lib/rexml.rb +3 -0
  39. metadata +42 -46
  40. data/.gitignore +0 -9
  41. data/.travis.yml +0 -24
  42. data/Gemfile +0 -6
  43. data/Rakefile +0 -8
  44. data/rexml.gemspec +0 -84
data/lib/rexml/element.rb CHANGED
@@ -7,17 +7,267 @@ require_relative "xpath"
7
7
  require_relative "parseexception"
8
8
 
9
9
  module REXML
10
- # An implementation note about namespaces:
11
- # As we parse, when we find namespaces we put them in a hash and assign
12
- # them a unique ID. We then convert the namespace prefix for the node
13
- # to the unique ID. This makes namespace lookup much faster for the
14
- # cost of extra memory use. We save the namespace prefix for the
15
- # context node and convert it back when we write it.
16
- @@namespaces = {}
17
-
18
- # Represents a tagged XML element. Elements are characterized by
19
- # having children, attributes, and names, and can themselves be
20
- # children.
10
+ # An \REXML::Element object represents an XML element.
11
+ #
12
+ # An element:
13
+ #
14
+ # - Has a name (string).
15
+ # - May have a parent (another element).
16
+ # - Has zero or more children
17
+ # (other elements, text, CDATA, processing instructions, and comments).
18
+ # - Has zero or more siblings
19
+ # (other elements, text, CDATA, processing instructions, and comments).
20
+ # - Has zero or more named attributes.
21
+ #
22
+ # == In a Hurry?
23
+ #
24
+ # If you're somewhat familiar with XML
25
+ # and have a particular task in mind,
26
+ # you may want to see the
27
+ # {tasks pages}[../doc/rexml/tasks/tocs/master_toc_rdoc.html],
28
+ # and in particular, the
29
+ # {tasks page for elements}[../doc/rexml/tasks/tocs/element_toc_rdoc.html].
30
+ #
31
+ # === Name
32
+ #
33
+ # An element has a name, which is initially set when the element is created:
34
+ #
35
+ # e = REXML::Element.new('foo')
36
+ # e.name # => "foo"
37
+ #
38
+ # The name may be changed:
39
+ #
40
+ # e.name = 'bar'
41
+ # e.name # => "bar"
42
+ #
43
+ #
44
+ # === \Parent
45
+ #
46
+ # An element may have a parent.
47
+ #
48
+ # Its parent may be assigned explicitly when the element is created:
49
+ #
50
+ # e0 = REXML::Element.new('foo')
51
+ # e1 = REXML::Element.new('bar', e0)
52
+ # e1.parent # => <foo> ... </>
53
+ #
54
+ # Note: the representation of an element always shows the element's name.
55
+ # If the element has children, the representation indicates that
56
+ # by including an ellipsis (<tt>...</tt>).
57
+ #
58
+ # The parent may be assigned explicitly at any time:
59
+ #
60
+ # e2 = REXML::Element.new('baz')
61
+ # e1.parent = e2
62
+ # e1.parent # => <baz/>
63
+ #
64
+ # When an element is added as a child, its parent is set automatically:
65
+ #
66
+ # e1.add_element(e0)
67
+ # e0.parent # => <bar> ... </>
68
+ #
69
+ # For an element that has no parent, method +parent+ returns +nil+.
70
+ #
71
+ # === Children
72
+ #
73
+ # An element has zero or more children.
74
+ # The children are an ordered collection
75
+ # of all objects whose parent is the element itself.
76
+ #
77
+ # The children may include any combination of elements, text, comments,
78
+ # processing instructions, and CDATA.
79
+ # (This example keeps things clean by controlling whitespace
80
+ # via a +context+ setting.)
81
+ #
82
+ # xml_string = <<-EOT
83
+ # <root>
84
+ # <ele_0/>
85
+ # text 0
86
+ # <!--comment 0-->
87
+ # <?target_0 pi_0?>
88
+ # <![CDATA[cdata 0]]>
89
+ # <ele_1/>
90
+ # text 1
91
+ # <!--comment 1-->
92
+ # <?target_0 pi_1?>
93
+ # <![CDATA[cdata 1]]>
94
+ # </root>
95
+ # EOT
96
+ # context = {ignore_whitespace_nodes: :all, compress_whitespace: :all}
97
+ # d = REXML::Document.new(xml_string, context)
98
+ # root = d.root
99
+ # root.children.size # => 10
100
+ # root.each {|child| p "#{child.class}: #{child}" }
101
+ #
102
+ # Output:
103
+ #
104
+ # "REXML::Element: <ele_0/>"
105
+ # "REXML::Text: \n text 0\n "
106
+ # "REXML::Comment: comment 0"
107
+ # "REXML::Instruction: <?target_0 pi_0?>"
108
+ # "REXML::CData: cdata 0"
109
+ # "REXML::Element: <ele_1/>"
110
+ # "REXML::Text: \n text 1\n "
111
+ # "REXML::Comment: comment 1"
112
+ # "REXML::Instruction: <?target_0 pi_1?>"
113
+ # "REXML::CData: cdata 1"
114
+ #
115
+ # A child may be added using inherited methods
116
+ # Parent#insert_before or Parent#insert_after:
117
+ #
118
+ # xml_string = '<root><a/><c/><d/></root>'
119
+ # d = REXML::Document.new(xml_string)
120
+ # root = d.root
121
+ # c = d.root[1] # => <c/>
122
+ # root.insert_before(c, REXML::Element.new('b'))
123
+ # root.to_a # => [<a/>, <b/>, <c/>, <d/>]
124
+ #
125
+ # A child may be replaced using Parent#replace_child:
126
+ #
127
+ # root.replace_child(c, REXML::Element.new('x'))
128
+ # root.to_a # => [<a/>, <b/>, <x/>, <d/>]
129
+ #
130
+ # A child may be removed using Parent#delete:
131
+ #
132
+ # x = root[2] # => <x/>
133
+ # root.delete(x)
134
+ # root.to_a # => [<a/>, <b/>, <d/>]
135
+ #
136
+ # === Siblings
137
+ #
138
+ # An element has zero or more siblings,
139
+ # which are the other children of the element's parent.
140
+ #
141
+ # In the example above, element +ele_1+ is between a CDATA sibling
142
+ # and a text sibling:
143
+ #
144
+ # ele_1 = root[5] # => <ele_1/>
145
+ # ele_1.previous_sibling # => "cdata 0"
146
+ # ele_1.next_sibling # => "\n text 1\n "
147
+ #
148
+ # === \Attributes
149
+ #
150
+ # An element has zero or more named attributes.
151
+ #
152
+ # A new element has no attributes:
153
+ #
154
+ # e = REXML::Element.new('foo')
155
+ # e.attributes # => {}
156
+ #
157
+ # Attributes may be added:
158
+ #
159
+ # e.add_attribute('bar', 'baz')
160
+ # e.add_attribute('bat', 'bam')
161
+ # e.attributes.size # => 2
162
+ # e['bar'] # => "baz"
163
+ # e['bat'] # => "bam"
164
+ #
165
+ # An existing attribute may be modified:
166
+ #
167
+ # e.add_attribute('bar', 'bad')
168
+ # e.attributes.size # => 2
169
+ # e['bar'] # => "bad"
170
+ #
171
+ # An existing attribute may be deleted:
172
+ #
173
+ # e.delete_attribute('bar')
174
+ # e.attributes.size # => 1
175
+ # e['bar'] # => nil
176
+ #
177
+ # == What's Here
178
+ #
179
+ # To begin with, what's elsewhere?
180
+ #
181
+ # \Class \REXML::Element inherits from its ancestor classes:
182
+ #
183
+ # - REXML::Child
184
+ # - REXML::Parent
185
+ #
186
+ # \REXML::Element itself and its ancestors also include modules:
187
+ #
188
+ # - {Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html]
189
+ # - REXML::Namespace
190
+ # - REXML::Node
191
+ # - REXML::XMLTokens
192
+ #
193
+ # === Methods for Creating an \Element
194
+ #
195
+ # ::new:: Returns a new empty element.
196
+ # #clone:: Returns a clone of another element.
197
+ #
198
+ # === Methods for Attributes
199
+ #
200
+ # {[attribute_name]}[#method-i-5B-5D]:: Returns an attribute value.
201
+ # #add_attribute:: Adds a new attribute.
202
+ # #add_attributes:: Adds multiple new attributes.
203
+ # #attribute:: Returns the attribute value for a given name and optional namespace.
204
+ # #delete_attribute:: Removes an attribute.
205
+ #
206
+ # === Methods for Children
207
+ #
208
+ # {[index]}[#method-i-5B-5D]:: Returns the child at the given offset.
209
+ # #add_element:: Adds an element as the last child.
210
+ # #delete_element:: Deletes a child element.
211
+ # #each_element:: Calls the given block with each child element.
212
+ # #each_element_with_attribute:: Calls the given block with each child element
213
+ # that meets given criteria,
214
+ # which can include the attribute name.
215
+ # #each_element_with_text:: Calls the given block with each child element
216
+ # that meets given criteria,
217
+ # which can include text.
218
+ # #get_elements:: Returns an array of element children that match a given xpath.
219
+ #
220
+ # === Methods for \Text Children
221
+ #
222
+ # #add_text:: Adds a text node to the element.
223
+ # #get_text:: Returns a text node that meets specified criteria.
224
+ # #text:: Returns the text string from the first node that meets specified criteria.
225
+ # #texts:: Returns an array of the text children of the element.
226
+ # #text=:: Adds, removes, or replaces the first text child of the element
227
+ #
228
+ # === Methods for Other Children
229
+ #
230
+ # #cdatas:: Returns an array of the cdata children of the element.
231
+ # #comments:: Returns an array of the comment children of the element.
232
+ # #instructions:: Returns an array of the instruction children of the element.
233
+ #
234
+ # === Methods for Namespaces
235
+ #
236
+ # #add_namespace:: Adds a namespace to the element.
237
+ # #delete_namespace:: Removes a namespace from the element.
238
+ # #namespace:: Returns the string namespace URI for the element.
239
+ # #namespaces:: Returns a hash of all defined namespaces in the element.
240
+ # #prefixes:: Returns an array of the string prefixes (names)
241
+ # of all defined namespaces in the element
242
+ #
243
+ # === Methods for Querying
244
+ #
245
+ # #document:: Returns the document, if any, that the element belongs to.
246
+ # #root:: Returns the most distant element (not document) ancestor of the element.
247
+ # #root_node:: Returns the most distant ancestor of the element.
248
+ # #xpath:: Returns the string xpath to the element
249
+ # relative to the most distant parent
250
+ # #has_attributes?:: Returns whether the element has attributes.
251
+ # #has_elements?:: Returns whether the element has elements.
252
+ # #has_text?:: Returns whether the element has text.
253
+ # #next_element:: Returns the next sibling that is an element.
254
+ # #previous_element:: Returns the previous sibling that is an element.
255
+ # #raw:: Returns whether raw mode is set for the element.
256
+ # #whitespace:: Returns whether whitespace is respected for the element.
257
+ # #ignore_whitespace_nodes:: Returns whether whitespace nodes
258
+ # are to be ignored for the element.
259
+ # #node_type:: Returns symbol <tt>:element</tt>.
260
+ #
261
+ # === One More Method
262
+ #
263
+ # #inspect:: Returns a string representation of the element.
264
+ #
265
+ # === Accessors
266
+ #
267
+ # #elements:: Returns the REXML::Elements object for the element.
268
+ # #attributes:: Returns the REXML::Attributes object for the element.
269
+ # #context:: Returns or sets the context hash for the element.
270
+ #
21
271
  class Element < Parent
22
272
  include Namespace
23
273
 
@@ -30,32 +280,42 @@ module REXML
30
280
  # whitespace handling.
31
281
  attr_accessor :context
32
282
 
33
- # Constructor
34
- # arg::
35
- # if not supplied, will be set to the default value.
36
- # If a String, the name of this object will be set to the argument.
37
- # If an Element, the object will be shallowly cloned; name,
38
- # attributes, and namespaces will be copied. Children will +not+ be
39
- # copied.
40
- # parent::
41
- # if supplied, must be a Parent, and will be used as
42
- # the parent of this object.
43
- # context::
44
- # If supplied, must be a hash containing context items. Context items
45
- # include:
46
- # * <tt>:respect_whitespace</tt> the value of this is :+all+ or an array of
47
- # strings being the names of the elements to respect
48
- # whitespace for. Defaults to :+all+.
49
- # * <tt>:compress_whitespace</tt> the value can be :+all+ or an array of
50
- # strings being the names of the elements to ignore whitespace on.
51
- # Overrides :+respect_whitespace+.
52
- # * <tt>:ignore_whitespace_nodes</tt> the value can be :+all+ or an array
53
- # of strings being the names of the elements in which to ignore
54
- # whitespace-only nodes. If this is set, Text nodes which contain only
55
- # whitespace will not be added to the document tree.
56
- # * <tt>:raw</tt> can be :+all+, or an array of strings being the names of
57
- # the elements to process in raw mode. In raw mode, special
58
- # characters in text is not converted to or from entities.
283
+ # :call-seq:
284
+ # Element.new(name = 'UNDEFINED', parent = nil, context = nil) -> new_element
285
+ # Element.new(element, parent = nil, context = nil) -> new_element
286
+ #
287
+ # Returns a new \REXML::Element object.
288
+ #
289
+ # When no arguments are given,
290
+ # returns an element with name <tt>'UNDEFINED'</tt>:
291
+ #
292
+ # e = REXML::Element.new # => <UNDEFINED/>
293
+ # e.class # => REXML::Element
294
+ # e.name # => "UNDEFINED"
295
+ #
296
+ # When only argument +name+ is given,
297
+ # returns an element of the given name:
298
+ #
299
+ # REXML::Element.new('foo') # => <foo/>
300
+ #
301
+ # When only argument +element+ is given, it must be an \REXML::Element object;
302
+ # returns a shallow copy of the given element:
303
+ #
304
+ # e0 = REXML::Element.new('foo')
305
+ # e1 = REXML::Element.new(e0) # => <foo/>
306
+ #
307
+ # When argument +parent+ is also given, it must be an REXML::Parent object:
308
+ #
309
+ # e = REXML::Element.new('foo', REXML::Parent.new)
310
+ # e.parent # => #<REXML::Parent @parent=nil, @children=[<foo/>]>
311
+ #
312
+ # When argument +context+ is also given, it must be a hash
313
+ # representing the context for the element;
314
+ # see {Element Context}[../doc/rexml/context_rdoc.html]:
315
+ #
316
+ # e = REXML::Element.new('foo', nil, {raw: :all})
317
+ # e.context # => {:raw=>:all}
318
+ #
59
319
  def initialize( arg = UNDEFINED, parent=nil, context=nil )
60
320
  super(parent)
61
321
 
@@ -74,6 +334,27 @@ module REXML
74
334
  end
75
335
  end
76
336
 
337
+ # :call-seq:
338
+ # inspect -> string
339
+ #
340
+ # Returns a string representation of the element.
341
+ #
342
+ # For an element with no attributes and no children, shows the element name:
343
+ #
344
+ # REXML::Element.new.inspect # => "<UNDEFINED/>"
345
+ #
346
+ # Shows attributes, if any:
347
+ #
348
+ # e = REXML::Element.new('foo')
349
+ # e.add_attributes({'bar' => 0, 'baz' => 1})
350
+ # e.inspect # => "<foo bar='0' baz='1'/>"
351
+ #
352
+ # Shows an ellipsis (<tt>...</tt>), if there are child elements:
353
+ #
354
+ # e.add_element(REXML::Element.new('bar'))
355
+ # e.add_element(REXML::Element.new('baz'))
356
+ # e.inspect # => "<foo bar='0' baz='1'> ... </>"
357
+ #
77
358
  def inspect
78
359
  rv = "<#@expanded_name"
79
360
 
@@ -89,60 +370,123 @@ module REXML
89
370
  end
90
371
  end
91
372
 
92
-
93
- # Creates a shallow copy of self.
94
- # d = Document.new "<a><b/><b/><c><d/></c></a>"
95
- # new_a = d.root.clone
96
- # puts new_a # => "<a/>"
373
+ # :call-seq:
374
+ # clone -> new_element
375
+ #
376
+ # Returns a shallow copy of the element, containing the name and attributes,
377
+ # but not the parent or children:
378
+ #
379
+ # e = REXML::Element.new('foo')
380
+ # e.add_attributes({'bar' => 0, 'baz' => 1})
381
+ # e.clone # => <foo bar='0' baz='1'/>
382
+ #
97
383
  def clone
98
384
  self.class.new self
99
385
  end
100
386
 
101
- # Evaluates to the root node of the document that this element
102
- # belongs to. If this element doesn't belong to a document, but does
103
- # belong to another Element, the parent's root will be returned, until the
104
- # earliest ancestor is found.
105
- #
106
- # Note that this is not the same as the document element.
107
- # In the following example, <a> is the document element, and the root
108
- # node is the parent node of the document element. You may ask yourself
109
- # why the root node is useful: consider the doctype and XML declaration,
110
- # and any processing instructions before the document element... they
111
- # are children of the root node, or siblings of the document element.
112
- # The only time this isn't true is when an Element is created that is
113
- # not part of any Document. In this case, the ancestor that has no
114
- # parent acts as the root node.
115
- # d = Document.new '<a><b><c/></b></a>'
116
- # a = d[1] ; c = a[1][1]
117
- # d.root_node == d # TRUE
118
- # a.root_node # namely, d
119
- # c.root_node # again, d
387
+ # :call-seq:
388
+ # root_node -> document or element
389
+ #
390
+ # Returns the most distant ancestor of +self+.
391
+ #
392
+ # When the element is part of a document,
393
+ # returns the root node of the document.
394
+ # Note that the root node is different from the document element;
395
+ # in this example +a+ is document element and the root node is its parent:
396
+ #
397
+ # d = REXML::Document.new('<a><b><c/></b></a>')
398
+ # top_element = d.first # => <a> ... </>
399
+ # child = top_element.first # => <b> ... </>
400
+ # d.root_node == d # => true
401
+ # top_element.root_node == d # => true
402
+ # child.root_node == d # => true
403
+ #
404
+ # When the element is not part of a document, but does have ancestor elements,
405
+ # returns the most distant ancestor element:
406
+ #
407
+ # e0 = REXML::Element.new('foo')
408
+ # e1 = REXML::Element.new('bar')
409
+ # e1.parent = e0
410
+ # e2 = REXML::Element.new('baz')
411
+ # e2.parent = e1
412
+ # e2.root_node == e0 # => true
413
+ #
414
+ # When the element has no ancestor elements,
415
+ # returns +self+:
416
+ #
417
+ # e = REXML::Element.new('foo')
418
+ # e.root_node == e # => true
419
+ #
420
+ # Related: #root, #document.
421
+ #
120
422
  def root_node
121
423
  parent.nil? ? self : parent.root_node
122
424
  end
123
425
 
426
+ # :call-seq:
427
+ # root -> element
428
+ #
429
+ # Returns the most distant _element_ (not document) ancestor of the element:
430
+ #
431
+ # d = REXML::Document.new('<a><b><c/></b></a>')
432
+ # top_element = d.first
433
+ # child = top_element.first
434
+ # top_element.root == top_element # => true
435
+ # child.root == top_element # => true
436
+ #
437
+ # For a document, returns the topmost element:
438
+ #
439
+ # d.root == top_element # => true
440
+ #
441
+ # Related: #root_node, #document.
442
+ #
124
443
  def root
125
- return elements[1] if self.kind_of? Document
126
- return self if parent.kind_of? Document or parent.nil?
127
- return parent.root
444
+ target = self
445
+ while target
446
+ return target.elements[1] if target.kind_of? Document
447
+ parent = target.parent
448
+ return target if parent.kind_of? Document or parent.nil?
449
+ target = parent
450
+ end
451
+ nil
128
452
  end
129
453
 
130
- # Evaluates to the document to which this element belongs, or nil if this
131
- # element doesn't belong to a document.
454
+ # :call-seq:
455
+ # document -> document or nil
456
+ #
457
+ # If the element is part of a document, returns that document:
458
+ #
459
+ # d = REXML::Document.new('<a><b><c/></b></a>')
460
+ # top_element = d.first
461
+ # child = top_element.first
462
+ # top_element.document == d # => true
463
+ # child.document == d # => true
464
+ #
465
+ # If the element is not part of a document, returns +nil+:
466
+ #
467
+ # REXML::Element.new.document # => nil
468
+ #
469
+ # For a document, returns +self+:
470
+ #
471
+ # d.document == d # => true
472
+ #
473
+ # Related: #root, #root_node.
474
+ #
132
475
  def document
133
476
  rt = root
134
477
  rt.parent if rt
135
478
  end
136
479
 
137
- # Evaluates to +true+ if whitespace is respected for this element. This
138
- # is the case if:
139
- # 1. Neither :+respect_whitespace+ nor :+compress_whitespace+ has any value
140
- # 2. The context has :+respect_whitespace+ set to :+all+ or
141
- # an array containing the name of this element, and
142
- # :+compress_whitespace+ isn't set to :+all+ or an array containing the
143
- # name of this element.
144
- # The evaluation is tested against +expanded_name+, and so is namespace
145
- # sensitive.
480
+ # :call-seq:
481
+ # whitespace
482
+ #
483
+ # Returns +true+ if whitespace is respected for this element,
484
+ # +false+ otherwise.
485
+ #
486
+ # See {Element Context}[../doc/rexml/context_rdoc.html].
487
+ #
488
+ # The evaluation is tested against the element's +expanded_name+,
489
+ # and so is namespace-sensitive.
146
490
  def whitespace
147
491
  @whitespace = nil
148
492
  if @context
@@ -159,6 +503,13 @@ module REXML
159
503
  @whitespace
160
504
  end
161
505
 
506
+ # :call-seq:
507
+ # ignore_whitespace_nodes
508
+ #
509
+ # Returns +true+ if whitespace nodes are ignored for the element.
510
+ #
511
+ # See {Element Context}[../doc/rexml/context_rdoc.html].
512
+ #
162
513
  def ignore_whitespace_nodes
163
514
  @ignore_whitespace_nodes = false
164
515
  if @context
@@ -170,9 +521,12 @@ module REXML
170
521
  end
171
522
  end
172
523
 
173
- # Evaluates to +true+ if raw mode is set for this element. This
174
- # is the case if the context has :+raw+ set to :+all+ or
175
- # an array containing the name of this element.
524
+ # :call-seq:
525
+ # raw
526
+ #
527
+ # Returns +true+ if raw mode is set for the element.
528
+ #
529
+ # See {Element Context}[../doc/rexml/context_rdoc.html].
176
530
  #
177
531
  # The evaluation is tested against +expanded_name+, and so is namespace
178
532
  # sensitive.
@@ -180,7 +534,7 @@ module REXML
180
534
  @raw = (@context and @context[:raw] and
181
535
  (@context[:raw] == :all or
182
536
  @context[:raw].include? expanded_name))
183
- @raw
537
+ @raw
184
538
  end
185
539
 
186
540
  #once :whitespace, :raw, :ignore_whitespace_nodes
@@ -189,10 +543,25 @@ module REXML
189
543
  # Namespaces #
190
544
  #################################################
191
545
 
192
- # Evaluates to an +Array+ containing the prefixes (names) of all defined
193
- # namespaces at this context node.
194
- # doc = Document.new("<a xmlns:x='1' xmlns:y='2'><b/><c xmlns:z='3'/></a>")
195
- # doc.elements['//b'].prefixes # -> ['x', 'y']
546
+ # :call-seq:
547
+ # prefixes -> array_of_namespace_prefixes
548
+ #
549
+ # Returns an array of the string prefixes (names) of all defined namespaces
550
+ # in the element and its ancestors:
551
+ #
552
+ # xml_string = <<-EOT
553
+ # <root>
554
+ # <a xmlns:x='1' xmlns:y='2'>
555
+ # <b/>
556
+ # <c xmlns:z='3'/>
557
+ # </a>
558
+ # </root>
559
+ # EOT
560
+ # d = REXML::Document.new(xml_string, {compress_whitespace: :all})
561
+ # d.elements['//a'].prefixes # => ["x", "y"]
562
+ # d.elements['//b'].prefixes # => ["x", "y"]
563
+ # d.elements['//c'].prefixes # => ["x", "y", "z"]
564
+ #
196
565
  def prefixes
197
566
  prefixes = []
198
567
  prefixes = parent.prefixes if parent
@@ -200,6 +569,25 @@ module REXML
200
569
  return prefixes
201
570
  end
202
571
 
572
+ # :call-seq:
573
+ # namespaces -> array_of_namespace_names
574
+ #
575
+ # Returns a hash of all defined namespaces
576
+ # in the element and its ancestors:
577
+ #
578
+ # xml_string = <<-EOT
579
+ # <root>
580
+ # <a xmlns:x='1' xmlns:y='2'>
581
+ # <b/>
582
+ # <c xmlns:z='3'/>
583
+ # </a>
584
+ # </root>
585
+ # EOT
586
+ # d = REXML::Document.new(xml_string)
587
+ # d.elements['//a'].namespaces # => {"x"=>"1", "y"=>"2"}
588
+ # d.elements['//b'].namespaces # => {"x"=>"1", "y"=>"2"}
589
+ # d.elements['//c'].namespaces # => {"x"=>"1", "y"=>"2", "z"=>"3"}
590
+ #
203
591
  def namespaces
204
592
  namespaces = {}
205
593
  namespaces = parent.namespaces if parent
@@ -207,19 +595,26 @@ module REXML
207
595
  return namespaces
208
596
  end
209
597
 
210
- # Evaluates to the URI for a prefix, or the empty string if no such
211
- # namespace is declared for this element. Evaluates recursively for
212
- # ancestors. Returns the default namespace, if there is one.
213
- # prefix::
214
- # the prefix to search for. If not supplied, returns the default
215
- # namespace if one exists
216
- # Returns::
217
- # the namespace URI as a String, or nil if no such namespace
218
- # exists. If the namespace is undefined, returns an empty string
219
- # doc = Document.new("<a xmlns='1' xmlns:y='2'><b/><c xmlns:z='3'/></a>")
220
- # b = doc.elements['//b']
221
- # b.namespace # -> '1'
222
- # b.namespace("y") # -> '2'
598
+ # :call-seq:
599
+ # namespace(prefix = nil) -> string_uri or nil
600
+ #
601
+ # Returns the string namespace URI for the element,
602
+ # possibly deriving from one of its ancestors.
603
+ #
604
+ # xml_string = <<-EOT
605
+ # <root>
606
+ # <a xmlns='1' xmlns:y='2'>
607
+ # <b/>
608
+ # <c xmlns:z='3'/>
609
+ # </a>
610
+ # </root>
611
+ # EOT
612
+ # d = REXML::Document.new(xml_string)
613
+ # b = d.elements['//b']
614
+ # b.namespace # => "1"
615
+ # b.namespace('y') # => "2"
616
+ # b.namespace('nosuch') # => nil
617
+ #
223
618
  def namespace(prefix=nil)
224
619
  if prefix.nil?
225
620
  prefix = prefix()
@@ -229,25 +624,34 @@ module REXML
229
624
  else
230
625
  prefix = "xmlns:#{prefix}" unless prefix[0,5] == 'xmlns'
231
626
  end
232
- ns = attributes[ prefix ]
233
- ns = parent.namespace(prefix) if ns.nil? and parent
627
+ ns = nil
628
+ target = self
629
+ while ns.nil? and target
630
+ ns = target.attributes[prefix]
631
+ target = target.parent
632
+ end
234
633
  ns = '' if ns.nil? and prefix == 'xmlns'
235
634
  return ns
236
635
  end
237
636
 
238
- # Adds a namespace to this element.
239
- # prefix::
240
- # the prefix string, or the namespace URI if +uri+ is not
241
- # supplied
242
- # uri::
243
- # the namespace URI. May be nil, in which +prefix+ is used as
244
- # the URI
245
- # Evaluates to: this Element
246
- # a = Element.new("a")
247
- # a.add_namespace("xmlns:foo", "bar" )
248
- # a.add_namespace("foo", "bar") # shorthand for previous line
249
- # a.add_namespace("twiddle")
250
- # puts a #-> <a xmlns:foo='bar' xmlns='twiddle'/>
637
+ # :call-seq:
638
+ # add_namespace(prefix, uri = nil) -> self
639
+ #
640
+ # Adds a namespace to the element; returns +self+.
641
+ #
642
+ # With the single argument +prefix+,
643
+ # adds a namespace using the given +prefix+ and the namespace URI:
644
+ #
645
+ # e = REXML::Element.new('foo')
646
+ # e.add_namespace('bar')
647
+ # e.namespaces # => {"xmlns"=>"bar"}
648
+ #
649
+ # With both arguments +prefix+ and +uri+ given,
650
+ # adds a namespace using both arguments:
651
+ #
652
+ # e.add_namespace('baz', 'bat')
653
+ # e.namespaces # => {"xmlns"=>"bar", "baz"=>"bat"}
654
+ #
251
655
  def add_namespace( prefix, uri=nil )
252
656
  unless uri
253
657
  @attributes["xmlns"] = prefix
@@ -258,16 +662,28 @@ module REXML
258
662
  self
259
663
  end
260
664
 
261
- # Removes a namespace from this node. This only works if the namespace is
262
- # actually declared in this node. If no argument is passed, deletes the
263
- # default namespace.
665
+ # :call-seq:
666
+ # delete_namespace(namespace = 'xmlns') -> self
667
+ #
668
+ # Removes a namespace from the element.
669
+ #
670
+ # With no argument, removes the default namespace:
671
+ #
672
+ # d = REXML::Document.new "<a xmlns:foo='bar' xmlns='twiddle'/>"
673
+ # d.to_s # => "<a xmlns:foo='bar' xmlns='twiddle'/>"
674
+ # d.root.delete_namespace # => <a xmlns:foo='bar'/>
675
+ # d.to_s # => "<a xmlns:foo='bar'/>"
676
+ #
677
+ # With argument +namespace+, removes the specified namespace:
678
+ #
679
+ # d.root.delete_namespace('foo')
680
+ # d.to_s # => "<a/>"
681
+ #
682
+ # Does nothing if no such namespace is found:
683
+ #
684
+ # d.root.delete_namespace('nosuch')
685
+ # d.to_s # => "<a/>"
264
686
  #
265
- # Evaluates to: this element
266
- # doc = Document.new "<a xmlns:foo='bar' xmlns='twiddle'/>"
267
- # doc.root.delete_namespace
268
- # puts doc # -> <a xmlns:foo='bar'/>
269
- # doc.root.delete_namespace 'foo'
270
- # puts doc # -> <a/>
271
687
  def delete_namespace namespace="xmlns"
272
688
  namespace = "xmlns:#{namespace}" unless namespace == 'xmlns'
273
689
  attribute = attributes.get_attribute(namespace)
@@ -279,20 +695,40 @@ module REXML
279
695
  # Elements #
280
696
  #################################################
281
697
 
282
- # Adds a child to this element, optionally setting attributes in
283
- # the element.
284
- # element::
285
- # optional. If Element, the element is added.
286
- # Otherwise, a new Element is constructed with the argument (see
287
- # Element.initialize).
288
- # attrs::
289
- # If supplied, must be a Hash containing String name,value
290
- # pairs, which will be used to set the attributes of the new Element.
291
- # Returns:: the Element that was added
292
- # el = doc.add_element 'my-tag'
293
- # el = doc.add_element 'my-tag', {'attr1'=>'val1', 'attr2'=>'val2'}
294
- # el = Element.new 'my-tag'
295
- # doc.add_element el
698
+ # :call-seq:
699
+ # add_element(name, attributes = nil) -> new_element
700
+ # add_element(element, attributes = nil) -> element
701
+ #
702
+ # Adds a child element, optionally setting attributes
703
+ # on the added element; returns the added element.
704
+ #
705
+ # With string argument +name+, creates a new element with that name
706
+ # and adds the new element as a child:
707
+ #
708
+ # e0 = REXML::Element.new('foo')
709
+ # e0.add_element('bar')
710
+ # e0[0] # => <bar/>
711
+ #
712
+ #
713
+ # With argument +name+ and hash argument +attributes+,
714
+ # sets attributes on the new element:
715
+ #
716
+ # e0.add_element('baz', {'bat' => '0', 'bam' => '1'})
717
+ # e0[1] # => <baz bat='0' bam='1'/>
718
+ #
719
+ # With element argument +element+, adds that element as a child:
720
+ #
721
+ # e0 = REXML::Element.new('foo')
722
+ # e1 = REXML::Element.new('bar')
723
+ # e0.add_element(e1)
724
+ # e0[0] # => <bar/>
725
+ #
726
+ # With argument +element+ and hash argument +attributes+,
727
+ # sets attributes on the added element:
728
+ #
729
+ # e0.add_element(e1, {'bat' => '0', 'bam' => '1'})
730
+ # e0[1] # => <bar bat='0' bam='1'/>
731
+ #
296
732
  def add_element element, attrs=nil
297
733
  raise "First argument must be either an element name, or an Element object" if element.nil?
298
734
  el = @elements.add(element)
@@ -302,52 +738,112 @@ module REXML
302
738
  el
303
739
  end
304
740
 
741
+ # :call-seq:
742
+ # delete_element(index) -> removed_element or nil
743
+ # delete_element(element) -> removed_element or nil
744
+ # delete_element(xpath) -> removed_element or nil
745
+ #
305
746
  # Deletes a child element.
306
- # element::
307
- # Must be an +Element+, +String+, or +Integer+. If Element,
308
- # the element is removed. If String, the element is found (via XPath)
309
- # and removed. <em>This means that any parent can remove any
310
- # descendant.<em> If Integer, the Element indexed by that number will be
311
- # removed.
312
- # Returns:: the element that was removed.
313
- # doc.delete_element "/a/b/c[@id='4']"
314
- # doc.delete_element doc.elements["//k"]
315
- # doc.delete_element 1
747
+ #
748
+ # When 1-based integer argument +index+ is given,
749
+ # removes and returns the child element at that offset if it exists;
750
+ # indexing does not include text nodes;
751
+ # returns +nil+ if the element does not exist:
752
+ #
753
+ # d = REXML::Document.new '<a><b/>text<c/></a>'
754
+ # a = d.root # => <a> ... </>
755
+ # a.delete_element(1) # => <b/>
756
+ # a.delete_element(1) # => <c/>
757
+ # a.delete_element(1) # => nil
758
+ #
759
+ # When element argument +element+ is given,
760
+ # removes and returns that child element if it exists,
761
+ # otherwise returns +nil+:
762
+ #
763
+ # d = REXML::Document.new '<a><b/>text<c/></a>'
764
+ # a = d.root # => <a> ... </>
765
+ # c = a[2] # => <c/>
766
+ # a.delete_element(c) # => <c/>
767
+ # a.delete_element(c) # => nil
768
+ #
769
+ # When xpath argument +xpath+ is given,
770
+ # removes and returns the element at xpath if it exists,
771
+ # otherwise returns +nil+:
772
+ #
773
+ # d = REXML::Document.new '<a><b/>text<c/></a>'
774
+ # a = d.root # => <a> ... </>
775
+ # a.delete_element('//c') # => <c/>
776
+ # a.delete_element('//c') # => nil
777
+ #
316
778
  def delete_element element
317
779
  @elements.delete element
318
780
  end
319
781
 
320
- # Evaluates to +true+ if this element has at least one child Element
321
- # doc = Document.new "<a><b/><c>Text</c></a>"
322
- # doc.root.has_elements # -> true
323
- # doc.elements["/a/b"].has_elements # -> false
324
- # doc.elements["/a/c"].has_elements # -> false
782
+ # :call-seq:
783
+ # has_elements?
784
+ #
785
+ # Returns +true+ if the element has one or more element children,
786
+ # +false+ otherwise:
787
+ #
788
+ # d = REXML::Document.new '<a><b/>text<c/></a>'
789
+ # a = d.root # => <a> ... </>
790
+ # a.has_elements? # => true
791
+ # b = a[0] # => <b/>
792
+ # b.has_elements? # => false
793
+ #
325
794
  def has_elements?
326
795
  !@elements.empty?
327
796
  end
328
797
 
329
- # Iterates through the child elements, yielding for each Element that
330
- # has a particular attribute set.
331
- # key::
332
- # the name of the attribute to search for
333
- # value::
334
- # the value of the attribute
335
- # max::
336
- # (optional) causes this method to return after yielding
337
- # for this number of matching children
338
- # name::
339
- # (optional) if supplied, this is an XPath that filters
340
- # the children to check.
341
- #
342
- # doc = Document.new "<a><b @id='1'/><c @id='2'/><d @id='1'/><e/></a>"
343
- # # Yields b, c, d
344
- # doc.root.each_element_with_attribute( 'id' ) {|e| p e}
345
- # # Yields b, d
346
- # doc.root.each_element_with_attribute( 'id', '1' ) {|e| p e}
347
- # # Yields b
348
- # doc.root.each_element_with_attribute( 'id', '1', 1 ) {|e| p e}
349
- # # Yields d
350
- # doc.root.each_element_with_attribute( 'id', '1', 0, 'd' ) {|e| p e}
798
+ # :call-seq:
799
+ # each_element_with_attribute(attr_name, value = nil, max = 0, xpath = nil) {|e| ... }
800
+ #
801
+ # Calls the given block with each child element that meets given criteria.
802
+ #
803
+ # When only string argument +attr_name+ is given,
804
+ # calls the block with each child element that has that attribute:
805
+ #
806
+ # d = REXML::Document.new '<a><b id="1"/><c id="2"/><d id="1"/><e/></a>'
807
+ # a = d.root
808
+ # a.each_element_with_attribute('id') {|e| p e }
809
+ #
810
+ # Output:
811
+ #
812
+ # <b id='1'/>
813
+ # <c id='2'/>
814
+ # <d id='1'/>
815
+ #
816
+ # With argument +attr_name+ and string argument +value+ given,
817
+ # calls the block with each child element that has that attribute
818
+ # with that value:
819
+ #
820
+ # a.each_element_with_attribute('id', '1') {|e| p e }
821
+ #
822
+ # Output:
823
+ #
824
+ # <b id='1'/>
825
+ # <d id='1'/>
826
+ #
827
+ # With arguments +attr_name+, +value+, and integer argument +max+ given,
828
+ # calls the block with at most +max+ child elements:
829
+ #
830
+ # a.each_element_with_attribute('id', '1', 1) {|e| p e }
831
+ #
832
+ # Output:
833
+ #
834
+ # <b id='1'/>
835
+ #
836
+ # With all arguments given, including +xpath+,
837
+ # calls the block with only those child elements
838
+ # that meet the first three criteria,
839
+ # and also match the given +xpath+:
840
+ #
841
+ # a.each_element_with_attribute('id', '1', 2, '//d') {|e| p e }
842
+ #
843
+ # Output:
844
+ #
845
+ # <d id='1'/>
846
+ #
351
847
  def each_element_with_attribute( key, value=nil, max=0, name=nil, &block ) # :yields: Element
352
848
  each_with_something( proc {|child|
353
849
  if value.nil?
@@ -358,27 +854,53 @@ module REXML
358
854
  }, max, name, &block )
359
855
  end
360
856
 
361
- # Iterates through the children, yielding for each Element that
362
- # has a particular text set.
363
- # text::
364
- # the text to search for. If nil, or not supplied, will iterate
365
- # over all +Element+ children that contain at least one +Text+ node.
366
- # max::
367
- # (optional) causes this method to return after yielding
368
- # for this number of matching children
369
- # name::
370
- # (optional) if supplied, this is an XPath that filters
371
- # the children to check.
372
- #
373
- # doc = Document.new '<a><b>b</b><c>b</c><d>d</d><e/></a>'
374
- # # Yields b, c, d
375
- # doc.each_element_with_text {|e|p e}
376
- # # Yields b, c
377
- # doc.each_element_with_text('b'){|e|p e}
378
- # # Yields b
379
- # doc.each_element_with_text('b', 1){|e|p e}
380
- # # Yields d
381
- # doc.each_element_with_text(nil, 0, 'd'){|e|p e}
857
+ # :call-seq:
858
+ # each_element_with_text(text = nil, max = 0, xpath = nil) {|e| ... }
859
+ #
860
+ # Calls the given block with each child element that meets given criteria.
861
+ #
862
+ # With no arguments, calls the block with each child element that has text:
863
+ #
864
+ # d = REXML::Document.new '<a><b>b</b><c>b</c><d>d</d><e/></a>'
865
+ # a = d.root
866
+ # a.each_element_with_text {|e| p e }
867
+ #
868
+ # Output:
869
+ #
870
+ # <b> ... </>
871
+ # <c> ... </>
872
+ # <d> ... </>
873
+ #
874
+ # With the single string argument +text+,
875
+ # calls the block with each element that has exactly that text:
876
+ #
877
+ # a.each_element_with_text('b') {|e| p e }
878
+ #
879
+ # Output:
880
+ #
881
+ # <b> ... </>
882
+ # <c> ... </>
883
+ #
884
+ # With argument +text+ and integer argument +max+,
885
+ # calls the block with at most +max+ elements:
886
+ #
887
+ # a.each_element_with_text('b', 1) {|e| p e }
888
+ #
889
+ # Output:
890
+ #
891
+ # <b> ... </>
892
+ #
893
+ # With all arguments given, including +xpath+,
894
+ # calls the block with only those child elements
895
+ # that meet the first two criteria,
896
+ # and also match the given +xpath+:
897
+ #
898
+ # a.each_element_with_text('b', 2, '//c') {|e| p e }
899
+ #
900
+ # Output:
901
+ #
902
+ # <c> ... </>
903
+ #
382
904
  def each_element_with_text( text=nil, max=0, name=nil, &block ) # :yields: Element
383
905
  each_with_something( proc {|child|
384
906
  if text.nil?
@@ -389,35 +911,71 @@ module REXML
389
911
  }, max, name, &block )
390
912
  end
391
913
 
392
- # Synonym for Element.elements.each
914
+ # :call-seq:
915
+ # each_element {|e| ... }
916
+ #
917
+ # Calls the given block with each child element:
918
+ #
919
+ # d = REXML::Document.new '<a><b>b</b><c>b</c><d>d</d><e/></a>'
920
+ # a = d.root
921
+ # a.each_element {|e| p e }
922
+ #
923
+ # Output:
924
+ #
925
+ # <b> ... </>
926
+ # <c> ... </>
927
+ # <d> ... </>
928
+ # <e/>
929
+ #
393
930
  def each_element( xpath=nil, &block ) # :yields: Element
394
931
  @elements.each( xpath, &block )
395
932
  end
396
933
 
397
- # Synonym for Element.to_a
398
- # This is a little slower than calling elements.each directly.
399
- # xpath:: any XPath by which to search for elements in the tree
400
- # Returns:: an array of Elements that match the supplied path
934
+ # :call-seq:
935
+ # get_elements(xpath)
936
+ #
937
+ # Returns an array of the elements that match the given +xpath+:
938
+ #
939
+ # xml_string = <<-EOT
940
+ # <root>
941
+ # <a level='1'>
942
+ # <a level='2'/>
943
+ # </a>
944
+ # </root>
945
+ # EOT
946
+ # d = REXML::Document.new(xml_string)
947
+ # d.root.get_elements('//a') # => [<a level='1'> ... </>, <a level='2'/>]
948
+ #
401
949
  def get_elements( xpath )
402
950
  @elements.to_a( xpath )
403
951
  end
404
952
 
405
- # Returns the next sibling that is an element, or nil if there is
406
- # no Element sibling after this one
407
- # doc = Document.new '<a><b/>text<c/></a>'
408
- # doc.root.elements['b'].next_element #-> <c/>
409
- # doc.root.elements['c'].next_element #-> nil
953
+ # :call-seq:
954
+ # next_element
955
+ #
956
+ # Returns the next sibling that is an element if it exists,
957
+ # +niL+ otherwise:
958
+ #
959
+ # d = REXML::Document.new '<a><b/>text<c/></a>'
960
+ # d.root.elements['b'].next_element #-> <c/>
961
+ # d.root.elements['c'].next_element #-> nil
962
+ #
410
963
  def next_element
411
964
  element = next_sibling
412
965
  element = element.next_sibling until element.nil? or element.kind_of? Element
413
966
  return element
414
967
  end
415
968
 
416
- # Returns the previous sibling that is an element, or nil if there is
417
- # no Element sibling prior to this one
418
- # doc = Document.new '<a><b/>text<c/></a>'
419
- # doc.root.elements['c'].previous_element #-> <b/>
420
- # doc.root.elements['b'].previous_element #-> nil
969
+ # :call-seq:
970
+ # previous_element
971
+ #
972
+ # Returns the previous sibling that is an element if it exists,
973
+ # +niL+ otherwise:
974
+ #
975
+ # d = REXML::Document.new '<a><b/>text<c/></a>'
976
+ # d.root.elements['c'].previous_element #-> <b/>
977
+ # d.root.elements['b'].previous_element #-> nil
978
+ #
421
979
  def previous_element
422
980
  element = previous_sibling
423
981
  element = element.previous_sibling until element.nil? or element.kind_of? Element
@@ -429,36 +987,69 @@ module REXML
429
987
  # Text #
430
988
  #################################################
431
989
 
432
- # Evaluates to +true+ if this element has at least one Text child
990
+ # :call-seq:
991
+ # has_text? -> true or false
992
+ #
993
+ # Returns +true+ if the element has one or more text noded,
994
+ # +false+ otherwise:
995
+ #
996
+ # d = REXML::Document.new '<a><b/>text<c/></a>'
997
+ # a = d.root
998
+ # a.has_text? # => true
999
+ # b = a[0]
1000
+ # b.has_text? # => false
1001
+ #
433
1002
  def has_text?
434
1003
  not text().nil?
435
1004
  end
436
1005
 
437
- # A convenience method which returns the String value of the _first_
438
- # child text element, if one exists, and +nil+ otherwise.
1006
+ # :call-seq:
1007
+ # text(xpath = nil) -> text_string or nil
439
1008
  #
440
- # <em>Note that an element may have multiple Text elements, perhaps
441
- # separated by other children</em>. Be aware that this method only returns
442
- # the first Text node.
1009
+ # Returns the text string from the first text node child
1010
+ # in a specified element, if it exists, +nil+ otherwise.
443
1011
  #
444
- # This method returns the +value+ of the first text child node, which
445
- # ignores the +raw+ setting, so always returns normalized text. See
446
- # the Text::value documentation.
1012
+ # With no argument, returns the text from the first text node in +self+:
1013
+ #
1014
+ # d = REXML::Document.new "<p>some text <b>this is bold!</b> more text</p>"
1015
+ # d.root.text.class # => String
1016
+ # d.root.text # => "some text "
1017
+ #
1018
+ # With argument +xpath+, returns text from the first text node
1019
+ # in the element that matches +xpath+:
1020
+ #
1021
+ # d.root.text(1) # => "this is bold!"
1022
+ #
1023
+ # Note that an element may have multiple text nodes,
1024
+ # possibly separated by other non-text children, as above.
1025
+ # Even so, the returned value is the string text from the first such node.
1026
+ #
1027
+ # Note also that the text note is retrieved by method get_text,
1028
+ # and so is always normalized text.
447
1029
  #
448
- # doc = Document.new "<p>some text <b>this is bold!</b> more text</p>"
449
- # # The element 'p' has two text elements, "some text " and " more text".
450
- # doc.root.text #-> "some text "
451
1030
  def text( path = nil )
452
1031
  rv = get_text(path)
453
1032
  return rv.value unless rv.nil?
454
1033
  nil
455
1034
  end
456
1035
 
457
- # Returns the first child Text node, if any, or +nil+ otherwise.
458
- # This method returns the actual +Text+ node, rather than the String content.
459
- # doc = Document.new "<p>some text <b>this is bold!</b> more text</p>"
460
- # # The element 'p' has two text elements, "some text " and " more text".
461
- # doc.root.get_text.value #-> "some text "
1036
+ # :call-seq:
1037
+ # get_text(xpath = nil) -> text_node or nil
1038
+ #
1039
+ # Returns the first text node child in a specified element, if it exists,
1040
+ # +nil+ otherwise.
1041
+ #
1042
+ # With no argument, returns the first text node from +self+:
1043
+ #
1044
+ # d = REXML::Document.new "<p>some text <b>this is bold!</b> more text</p>"
1045
+ # d.root.get_text.class # => REXML::Text
1046
+ # d.root.get_text # => "some text "
1047
+ #
1048
+ # With argument +xpath+, returns the first text node from the element
1049
+ # that matches +xpath+:
1050
+ #
1051
+ # d.root.get_text(1) # => "this is bold!"
1052
+ #
462
1053
  def get_text path = nil
463
1054
  rv = nil
464
1055
  if path
@@ -470,26 +1061,31 @@ module REXML
470
1061
  return rv
471
1062
  end
472
1063
 
473
- # Sets the first Text child of this object. See text() for a
474
- # discussion about Text children.
475
- #
476
- # If a Text child already exists, the child is replaced by this
477
- # content. This means that Text content can be deleted by calling
478
- # this method with a nil argument. In this case, the next Text
479
- # child becomes the first Text child. In no case is the order of
480
- # any siblings disturbed.
481
- # text::
482
- # If a String, a new Text child is created and added to
483
- # this Element as the first Text child. If Text, the text is set
484
- # as the first Child element. If nil, then any existing first Text
485
- # child is removed.
486
- # Returns:: this Element.
487
- # doc = Document.new '<a><b/></a>'
488
- # doc.root.text = 'Sean' #-> '<a><b/>Sean</a>'
489
- # doc.root.text = 'Elliott' #-> '<a><b/>Elliott</a>'
490
- # doc.root.add_element 'c' #-> '<a><b/>Elliott<c/></a>'
491
- # doc.root.text = 'Russell' #-> '<a><b/>Russell<c/></a>'
492
- # doc.root.text = nil #-> '<a><b/><c/></a>'
1064
+ # :call-seq:
1065
+ # text = string -> string
1066
+ # text = nil -> nil
1067
+ #
1068
+ # Adds, replaces, or removes the first text node child in the element.
1069
+ #
1070
+ # With string argument +string+,
1071
+ # creates a new \REXML::Text node containing that string,
1072
+ # honoring the current settings for whitespace and row,
1073
+ # then places the node as the first text child in the element;
1074
+ # returns +string+.
1075
+ #
1076
+ # If the element has no text child, the text node is added:
1077
+ #
1078
+ # d = REXML::Document.new '<a><b/></a>'
1079
+ # d.root.text = 'foo' #-> '<a><b/>foo</a>'
1080
+ #
1081
+ # If the element has a text child, it is replaced:
1082
+ #
1083
+ # d.root.text = 'bar' #-> '<a><b/>bar</a>'
1084
+ #
1085
+ # With argument +nil+, removes the first text child:
1086
+ #
1087
+ # d.root.text = nil #-> '<a><b/><c/></a>'
1088
+ #
493
1089
  def text=( text )
494
1090
  if text.kind_of? String
495
1091
  text = Text.new( text, whitespace(), nil, raw() )
@@ -509,17 +1105,45 @@ module REXML
509
1105
  return self
510
1106
  end
511
1107
 
512
- # A helper method to add a Text child. Actual Text instances can
513
- # be added with regular Parent methods, such as add() and <<()
514
- # text::
515
- # if a String, a new Text instance is created and added
516
- # to the parent. If Text, the object is added directly.
517
- # Returns:: this Element
518
- # e = Element.new('a') #-> <e/>
519
- # e.add_text 'foo' #-> <e>foo</e>
520
- # e.add_text Text.new(' bar') #-> <e>foo bar</e>
521
- # Note that at the end of this example, the branch has <b>3</b> nodes; the 'e'
522
- # element and <b>2</b> Text node children.
1108
+ # :call-seq:
1109
+ # add_text(string) -> nil
1110
+ # add_text(text_node) -> self
1111
+ #
1112
+ # Adds text to the element.
1113
+ #
1114
+ # When string argument +string+ is given, returns +nil+.
1115
+ #
1116
+ # If the element has no child text node,
1117
+ # creates a \REXML::Text object using the string,
1118
+ # honoring the current settings for whitespace and raw,
1119
+ # then adds that node to the element:
1120
+ #
1121
+ # d = REXML::Document.new('<a><b/></a>')
1122
+ # a = d.root
1123
+ # a.add_text('foo')
1124
+ # a.to_a # => [<b/>, "foo"]
1125
+ #
1126
+ # If the element has child text nodes,
1127
+ # appends the string to the _last_ text node:
1128
+ #
1129
+ # d = REXML::Document.new('<a>foo<b/>bar</a>')
1130
+ # a = d.root
1131
+ # a.add_text('baz')
1132
+ # a.to_a # => ["foo", <b/>, "barbaz"]
1133
+ # a.add_text('baz')
1134
+ # a.to_a # => ["foo", <b/>, "barbazbaz"]
1135
+ #
1136
+ # When text node argument +text_node+ is given,
1137
+ # appends the node as the last text node in the element;
1138
+ # returns +self+:
1139
+ #
1140
+ # d = REXML::Document.new('<a>foo<b/>bar</a>')
1141
+ # a = d.root
1142
+ # a.add_text(REXML::Text.new('baz'))
1143
+ # a.to_a # => ["foo", <b/>, "bar", "baz"]
1144
+ # a.add_text(REXML::Text.new('baz'))
1145
+ # a.to_a # => ["foo", <b/>, "bar", "baz", "baz"]
1146
+ #
523
1147
  def add_text( text )
524
1148
  if text.kind_of? String
525
1149
  if @children[-1].kind_of? Text
@@ -532,10 +1156,39 @@ module REXML
532
1156
  return self
533
1157
  end
534
1158
 
1159
+ # :call-seq:
1160
+ # node_type -> :element
1161
+ #
1162
+ # Returns symbol <tt>:element</tt>:
1163
+ #
1164
+ # d = REXML::Document.new('<a/>')
1165
+ # a = d.root # => <a/>
1166
+ # a.node_type # => :element
1167
+ #
535
1168
  def node_type
536
1169
  :element
537
1170
  end
538
1171
 
1172
+ # :call-seq:
1173
+ # xpath -> string_xpath
1174
+ #
1175
+ # Returns the string xpath to the element
1176
+ # relative to the most distant parent:
1177
+ #
1178
+ # d = REXML::Document.new('<a><b><c/></b></a>')
1179
+ # a = d.root # => <a> ... </>
1180
+ # b = a[0] # => <b> ... </>
1181
+ # c = b[0] # => <c/>
1182
+ # d.xpath # => ""
1183
+ # a.xpath # => "/a"
1184
+ # b.xpath # => "/a/b"
1185
+ # c.xpath # => "/a/b/c"
1186
+ #
1187
+ # If there is no parent, returns the expanded name of the element:
1188
+ #
1189
+ # e = REXML::Element.new('foo')
1190
+ # e.xpath # => "foo"
1191
+ #
539
1192
  def xpath
540
1193
  path_elements = []
541
1194
  cur = self
@@ -551,19 +1204,45 @@ module REXML
551
1204
  # Attributes #
552
1205
  #################################################
553
1206
 
554
- # Fetches an attribute value or a child.
1207
+ # :call-seq:
1208
+ # [index] -> object
1209
+ # [attr_name] -> attr_value
1210
+ # [attr_sym] -> attr_value
1211
+ #
1212
+ # With integer argument +index+ given,
1213
+ # returns the child at offset +index+, or +nil+ if none:
1214
+ #
1215
+ # d = REXML::Document.new '><root><a/>text<b/>more<c/></root>'
1216
+ # root = d.root
1217
+ # (0..root.size).each do |index|
1218
+ # node = root[index]
1219
+ # p "#{index}: #{node} (#{node.class})"
1220
+ # end
1221
+ #
1222
+ # Output:
1223
+ #
1224
+ # "0: <a/> (REXML::Element)"
1225
+ # "1: text (REXML::Text)"
1226
+ # "2: <b/> (REXML::Element)"
1227
+ # "3: more (REXML::Text)"
1228
+ # "4: <c/> (REXML::Element)"
1229
+ # "5: (NilClass)"
1230
+ #
1231
+ # With string argument +attr_name+ given,
1232
+ # returns the string value for the given attribute name if it exists,
1233
+ # otherwise +nil+:
555
1234
  #
556
- # If String or Symbol is specified, it's treated as attribute
557
- # name. Attribute value as String or +nil+ is returned. This case
558
- # is shortcut of +attributes[name]+.
1235
+ # d = REXML::Document.new('<root attr="value"></root>')
1236
+ # root = d.root
1237
+ # root['attr'] # => "value"
1238
+ # root['nosuch'] # => nil
559
1239
  #
560
- # If Integer is specified, it's treated as the index of
561
- # child. It returns Nth child.
1240
+ # With symbol argument +attr_sym+ given,
1241
+ # returns <tt>[attr_sym.to_s]</tt>:
1242
+ #
1243
+ # root[:attr] # => "value"
1244
+ # root[:nosuch] # => nil
562
1245
  #
563
- # doc = REXML::Document.new("<a attr='1'><b/><c/></a>")
564
- # doc.root["attr"] # => "1"
565
- # doc.root.attributes["attr"] # => "1"
566
- # doc.root[1] # => <c/>
567
1246
  def [](name_or_index)
568
1247
  case name_or_index
569
1248
  when String
@@ -575,17 +1254,42 @@ module REXML
575
1254
  end
576
1255
  end
577
1256
 
1257
+
1258
+ # :call-seq:
1259
+ # attribute(name, namespace = nil)
1260
+ #
1261
+ # Returns the string value for the given attribute name.
1262
+ #
1263
+ # With only argument +name+ given,
1264
+ # returns the value of the named attribute if it exists, otherwise +nil+:
1265
+ #
1266
+ # xml_string = <<-EOT
1267
+ # <root xmlns="ns0">
1268
+ # <a xmlns="ns1" attr="value"></a>
1269
+ # <b xmlns="ns2" attr="value"></b>
1270
+ # <c attr="value"/>
1271
+ # </root>
1272
+ # EOT
1273
+ # d = REXML::Document.new(xml_string)
1274
+ # root = d.root
1275
+ # a = root[1] # => <a xmlns='ns1' attr='value'/>
1276
+ # a.attribute('attr') # => attr='value'
1277
+ # a.attribute('nope') # => nil
1278
+ #
1279
+ # With arguments +name+ and +namespace+ given,
1280
+ # returns the value of the named attribute if it exists, otherwise +nil+:
1281
+ #
1282
+ # xml_string = "<root xmlns:a='a' a:x='a:x' x='x'/>"
1283
+ # document = REXML::Document.new(xml_string)
1284
+ # document.root.attribute("x") # => x='x'
1285
+ # document.root.attribute("x", "a") # => a:x='a:x'
1286
+ #
578
1287
  def attribute( name, namespace=nil )
579
- prefix = nil
580
- if namespaces.respond_to? :key
581
- prefix = namespaces.key(namespace) if namespace
582
- else
583
- prefix = namespaces.index(namespace) if namespace
584
- end
1288
+ prefix = namespaces.key(namespace) if namespace
585
1289
  prefix = nil if prefix == 'xmlns'
586
1290
 
587
1291
  ret_val =
588
- attributes.get_attribute( "#{prefix ? prefix + ':' : ''}#{name}" )
1292
+ attributes.get_attribute( prefix ? "#{prefix}:#{name}" : name )
589
1293
 
590
1294
  return ret_val unless ret_val.nil?
591
1295
  return nil if prefix.nil?
@@ -598,29 +1302,46 @@ module REXML
598
1302
 
599
1303
  end
600
1304
 
601
- # Evaluates to +true+ if this element has any attributes set, false
602
- # otherwise.
1305
+ # :call-seq:
1306
+ # has_attributes? -> true or false
1307
+ #
1308
+ # Returns +true+ if the element has attributes, +false+ otherwise:
1309
+ #
1310
+ # d = REXML::Document.new('<root><a attr="val"/><b/></root>')
1311
+ # a, b = *d.root
1312
+ # a.has_attributes? # => true
1313
+ # b.has_attributes? # => false
1314
+ #
603
1315
  def has_attributes?
604
1316
  return !@attributes.empty?
605
1317
  end
606
1318
 
1319
+ # :call-seq:
1320
+ # add_attribute(name, value) -> value
1321
+ # add_attribute(attribute) -> attribute
1322
+ #
607
1323
  # Adds an attribute to this element, overwriting any existing attribute
608
1324
  # by the same name.
609
- # key::
610
- # can be either an Attribute or a String. If an Attribute,
611
- # the attribute is added to the list of Element attributes. If String,
612
- # the argument is used as the name of the new attribute, and the value
613
- # parameter must be supplied.
614
- # value::
615
- # Required if +key+ is a String, and ignored if the first argument is
616
- # an Attribute. This is a String, and is used as the value
617
- # of the new Attribute. This should be the unnormalized value of the
618
- # attribute (without entities).
619
- # Returns:: the Attribute added
620
- # e = Element.new 'e'
621
- # e.add_attribute( 'a', 'b' ) #-> <e a='b'/>
622
- # e.add_attribute( 'x:a', 'c' ) #-> <e a='b' x:a='c'/>
623
- # e.add_attribute Attribute.new('b', 'd') #-> <e a='b' x:a='c' b='d'/>
1325
+ #
1326
+ # With string argument +name+ and object +value+ are given,
1327
+ # adds the attribute created with that name and value:
1328
+ #
1329
+ # e = REXML::Element.new
1330
+ # e.add_attribute('attr', 'value') # => "value"
1331
+ # e['attr'] # => "value"
1332
+ # e.add_attribute('attr', 'VALUE') # => "VALUE"
1333
+ # e['attr'] # => "VALUE"
1334
+ #
1335
+ # With only attribute object +attribute+ given,
1336
+ # adds the given attribute:
1337
+ #
1338
+ # a = REXML::Attribute.new('attr', 'value')
1339
+ # e.add_attribute(a) # => attr='value'
1340
+ # e['attr'] # => "value"
1341
+ # a = REXML::Attribute.new('attr', 'VALUE')
1342
+ # e.add_attribute(a) # => attr='VALUE'
1343
+ # e['attr'] # => "VALUE"
1344
+ #
624
1345
  def add_attribute( key, value=nil )
625
1346
  if key.kind_of? Attribute
626
1347
  @attributes << key
@@ -629,10 +1350,29 @@ module REXML
629
1350
  end
630
1351
  end
631
1352
 
632
- # Add multiple attributes to this element.
633
- # hash:: is either a hash, or array of arrays
634
- # el.add_attributes( {"name1"=>"value1", "name2"=>"value2"} )
635
- # el.add_attributes( [ ["name1","value1"], ["name2"=>"value2"] ] )
1353
+ # :call-seq:
1354
+ # add_attributes(hash) -> hash
1355
+ # add_attributes(array)
1356
+ #
1357
+ # Adds zero or more attributes to the element;
1358
+ # returns the argument.
1359
+ #
1360
+ # If hash argument +hash+ is given,
1361
+ # each key must be a string;
1362
+ # adds each attribute created with the key/value pair:
1363
+ #
1364
+ # e = REXML::Element.new
1365
+ # h = {'foo' => 'bar', 'baz' => 'bat'}
1366
+ # e.add_attributes(h)
1367
+ #
1368
+ # If argument +array+ is given,
1369
+ # each array member must be a 2-element array <tt>[name, value];
1370
+ # each name must be a string:
1371
+ #
1372
+ # e = REXML::Element.new
1373
+ # a = [['foo' => 'bar'], ['baz' => 'bat']]
1374
+ # e.add_attributes(a)
1375
+ #
636
1376
  def add_attributes hash
637
1377
  if hash.kind_of? Hash
638
1378
  hash.each_pair {|key, value| @attributes[key] = value }
@@ -641,19 +1381,17 @@ module REXML
641
1381
  end
642
1382
  end
643
1383
 
644
- # Removes an attribute
645
- # key::
646
- # either an Attribute or a String. In either case, the
647
- # attribute is found by matching the attribute name to the argument,
648
- # and then removed. If no attribute is found, no action is taken.
649
- # Returns::
650
- # the attribute removed, or nil if this Element did not contain
651
- # a matching attribute
652
- # e = Element.new('E')
653
- # e.add_attribute( 'name', 'Sean' ) #-> <E name='Sean'/>
654
- # r = e.add_attribute( 'sur:name', 'Russell' ) #-> <E name='Sean' sur:name='Russell'/>
655
- # e.delete_attribute( 'name' ) #-> <E sur:name='Russell'/>
656
- # e.delete_attribute( r ) #-> <E/>
1384
+ # :call-seq:
1385
+ # delete_attribute(name) -> removed_attribute or nil
1386
+ #
1387
+ # Removes a named attribute if it exists;
1388
+ # returns the removed attribute if found, otherwise +nil+:
1389
+ #
1390
+ # e = REXML::Element.new('foo')
1391
+ # e.add_attribute('bar', 'baz')
1392
+ # e.delete_attribute('bar') # => <bar/>
1393
+ # e.delete_attribute('bar') # => nil
1394
+ #
657
1395
  def delete_attribute(key)
658
1396
  attr = @attributes.get_attribute(key)
659
1397
  attr.remove unless attr.nil?
@@ -663,26 +1401,80 @@ module REXML
663
1401
  # Other Utilities #
664
1402
  #################################################
665
1403
 
666
- # Get an array of all CData children.
667
- # IMMUTABLE
1404
+ # :call-seq:
1405
+ # cdatas -> array_of_cdata_children
1406
+ #
1407
+ # Returns a frozen array of the REXML::CData children of the element:
1408
+ #
1409
+ # xml_string = <<-EOT
1410
+ # <root>
1411
+ # <![CDATA[foo]]>
1412
+ # <![CDATA[bar]]>
1413
+ # </root>
1414
+ # EOT
1415
+ # d = REXML::Document.new(xml_string)
1416
+ # cds = d.root.cdatas # => ["foo", "bar"]
1417
+ # cds.frozen? # => true
1418
+ # cds.map {|cd| cd.class } # => [REXML::CData, REXML::CData]
1419
+ #
668
1420
  def cdatas
669
1421
  find_all { |child| child.kind_of? CData }.freeze
670
1422
  end
671
1423
 
672
- # Get an array of all Comment children.
673
- # IMMUTABLE
1424
+ # :call-seq:
1425
+ # comments -> array_of_comment_children
1426
+ #
1427
+ # Returns a frozen array of the REXML::Comment children of the element:
1428
+ #
1429
+ # xml_string = <<-EOT
1430
+ # <root>
1431
+ # <!--foo-->
1432
+ # <!--bar-->
1433
+ # </root>
1434
+ # EOT
1435
+ # d = REXML::Document.new(xml_string)
1436
+ # cs = d.root.comments
1437
+ # cs.frozen? # => true
1438
+ # cs.map {|c| c.class } # => [REXML::Comment, REXML::Comment]
1439
+ # cs.map {|c| c.to_s } # => ["foo", "bar"]
1440
+ #
674
1441
  def comments
675
1442
  find_all { |child| child.kind_of? Comment }.freeze
676
1443
  end
677
1444
 
678
- # Get an array of all Instruction children.
679
- # IMMUTABLE
1445
+ # :call-seq:
1446
+ # instructions -> array_of_instruction_children
1447
+ #
1448
+ # Returns a frozen array of the REXML::Instruction children of the element:
1449
+ #
1450
+ # xml_string = <<-EOT
1451
+ # <root>
1452
+ # <?target0 foo?>
1453
+ # <?target1 bar?>
1454
+ # </root>
1455
+ # EOT
1456
+ # d = REXML::Document.new(xml_string)
1457
+ # is = d.root.instructions
1458
+ # is.frozen? # => true
1459
+ # is.map {|i| i.class } # => [REXML::Instruction, REXML::Instruction]
1460
+ # is.map {|i| i.to_s } # => ["<?target0 foo?>", "<?target1 bar?>"]
1461
+ #
680
1462
  def instructions
681
1463
  find_all { |child| child.kind_of? Instruction }.freeze
682
1464
  end
683
1465
 
684
- # Get an array of all Text children.
685
- # IMMUTABLE
1466
+ # :call-seq:
1467
+ # texts -> array_of_text_children
1468
+ #
1469
+ # Returns a frozen array of the REXML::Text children of the element:
1470
+ #
1471
+ # xml_string = '<root><a/>text<b/>more<c/></root>'
1472
+ # d = REXML::Document.new(xml_string)
1473
+ # ts = d.root.texts
1474
+ # ts.frozen? # => true
1475
+ # ts.map {|t| t.class } # => [REXML::Text, REXML::Text]
1476
+ # ts.map {|t| t.to_s } # => ["text", "more"]
1477
+ #
686
1478
  def texts
687
1479
  find_all { |child| child.kind_of? Text }.freeze
688
1480
  end
@@ -758,35 +1550,129 @@ module REXML
758
1550
  # XPath search support. You are expected to only encounter this class as
759
1551
  # the <tt>element.elements</tt> object. Therefore, you are
760
1552
  # _not_ expected to instantiate this yourself.
1553
+ #
1554
+ # xml_string = <<-EOT
1555
+ # <?xml version="1.0" encoding="UTF-8"?>
1556
+ # <bookstore>
1557
+ # <book category="cooking">
1558
+ # <title lang="en">Everyday Italian</title>
1559
+ # <author>Giada De Laurentiis</author>
1560
+ # <year>2005</year>
1561
+ # <price>30.00</price>
1562
+ # </book>
1563
+ # <book category="children">
1564
+ # <title lang="en">Harry Potter</title>
1565
+ # <author>J K. Rowling</author>
1566
+ # <year>2005</year>
1567
+ # <price>29.99</price>
1568
+ # </book>
1569
+ # <book category="web">
1570
+ # <title lang="en">XQuery Kick Start</title>
1571
+ # <author>James McGovern</author>
1572
+ # <author>Per Bothner</author>
1573
+ # <author>Kurt Cagle</author>
1574
+ # <author>James Linn</author>
1575
+ # <author>Vaidyanathan Nagarajan</author>
1576
+ # <year>2003</year>
1577
+ # <price>49.99</price>
1578
+ # </book>
1579
+ # <book category="web" cover="paperback">
1580
+ # <title lang="en">Learning XML</title>
1581
+ # <author>Erik T. Ray</author>
1582
+ # <year>2003</year>
1583
+ # <price>39.95</price>
1584
+ # </book>
1585
+ # </bookstore>
1586
+ # EOT
1587
+ # d = REXML::Document.new(xml_string)
1588
+ # elements = d.root.elements
1589
+ # elements # => #<REXML::Elements @element=<bookstore> ... </>>
1590
+ #
761
1591
  class Elements
762
1592
  include Enumerable
763
- # Constructor
764
- # parent:: the parent Element
1593
+ # :call-seq:
1594
+ # new(parent) -> new_elements_object
1595
+ #
1596
+ # Returns a new \Elements object with the given +parent+.
1597
+ # Does _not_ assign <tt>parent.elements = self</tt>:
1598
+ #
1599
+ # d = REXML::Document.new(xml_string)
1600
+ # eles = REXML::Elements.new(d.root)
1601
+ # eles # => #<REXML::Elements @element=<bookstore> ... </>>
1602
+ # eles == d.root.elements # => false
1603
+ #
765
1604
  def initialize parent
766
1605
  @element = parent
767
1606
  end
768
1607
 
769
- # Fetches a child element. Filters only Element children, regardless of
770
- # the XPath match.
771
- # index::
772
- # the search parameter. This is either an Integer, which
773
- # will be used to find the index'th child Element, or an XPath,
774
- # which will be used to search for the Element. <em>Because
775
- # of the nature of XPath searches, any element in the connected XML
776
- # document can be fetched through any other element.</em> <b>The
777
- # Integer index is 1-based, not 0-based.</b> This means that the first
778
- # child element is at index 1, not 0, and the +n+th element is at index
779
- # +n+, not <tt>n-1</tt>. This is because XPath indexes element children
780
- # starting from 1, not 0, and the indexes should be the same.
781
- # name::
782
- # optional, and only used in the first argument is an
783
- # Integer. In that case, the index'th child Element that has the
784
- # supplied name will be returned. Note again that the indexes start at 1.
785
- # Returns:: the first matching Element, or nil if no child matched
786
- # doc = Document.new '<a><b/><c id="1"/><c id="2"/><d/></a>'
787
- # doc.root.elements[1] #-> <b/>
788
- # doc.root.elements['c'] #-> <c id="1"/>
789
- # doc.root.elements[2,'c'] #-> <c id="2"/>
1608
+ # :call-seq:
1609
+ # parent
1610
+ #
1611
+ # Returns the parent element cited in creating the \Elements object.
1612
+ # This element is also the default starting point for searching
1613
+ # in the \Elements object.
1614
+ #
1615
+ # d = REXML::Document.new(xml_string)
1616
+ # elements = REXML::Elements.new(d.root)
1617
+ # elements.parent == d.root # => true
1618
+ #
1619
+ def parent
1620
+ @element
1621
+ end
1622
+
1623
+ # :call-seq:
1624
+ # elements[index] -> element or nil
1625
+ # elements[xpath] -> element or nil
1626
+ # elements[n, name] -> element or nil
1627
+ #
1628
+ # Returns the first \Element object selected by the arguments,
1629
+ # if any found, or +nil+ if none found.
1630
+ #
1631
+ # Notes:
1632
+ # - The +index+ is 1-based, not 0-based, so that:
1633
+ # - The first element has index <tt>1</tt>
1634
+ # - The _nth_ element has index +n+.
1635
+ # - The selection ignores non-\Element nodes.
1636
+ #
1637
+ # When the single argument +index+ is given,
1638
+ # returns the element given by the index, if any; otherwise, +nil+:
1639
+ #
1640
+ # d = REXML::Document.new(xml_string)
1641
+ # eles = d.root.elements
1642
+ # eles # => #<REXML::Elements @element=<bookstore> ... </>>
1643
+ # eles[1] # => <book category='cooking'> ... </>
1644
+ # eles.size # => 4
1645
+ # eles[4] # => <book category='web' cover='paperback'> ... </>
1646
+ # eles[5] # => nil
1647
+ #
1648
+ # The node at this index is not an \Element, and so is not returned:
1649
+ #
1650
+ # eles = d.root.first.first # => <title lang='en'> ... </>
1651
+ # eles.to_a # => ["Everyday Italian"]
1652
+ # eles[1] # => nil
1653
+ #
1654
+ # When the single argument +xpath+ is given,
1655
+ # returns the first element found via that +xpath+, if any; otherwise, +nil+:
1656
+ #
1657
+ # eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>>
1658
+ # eles['/bookstore'] # => <bookstore> ... </>
1659
+ # eles['//book'] # => <book category='cooking'> ... </>
1660
+ # eles['//book [@category="children"]'] # => <book category='children'> ... </>
1661
+ # eles['/nosuch'] # => nil
1662
+ # eles['//nosuch'] # => nil
1663
+ # eles['//book [@category="nosuch"]'] # => nil
1664
+ # eles['.'] # => <bookstore> ... </>
1665
+ # eles['..'].class # => REXML::Document
1666
+ #
1667
+ # With arguments +n+ and +name+ given,
1668
+ # returns the _nth_ found element that has the given +name+,
1669
+ # or +nil+ if there is no such _nth_ element:
1670
+ #
1671
+ # eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>>
1672
+ # eles[1, 'book'] # => <book category='cooking'> ... </>
1673
+ # eles[4, 'book'] # => <book category='web' cover='paperback'> ... </>
1674
+ # eles[5, 'book'] # => nil
1675
+ #
790
1676
  def []( index, name=nil)
791
1677
  if index.kind_of? Integer
792
1678
  raise "index (#{index}) must be >= 1" if index < 1
@@ -806,19 +1692,42 @@ module REXML
806
1692
  end
807
1693
  end
808
1694
 
809
- # Sets an element, replacing any previous matching element. If no
810
- # existing element is found ,the element is added.
811
- # index:: Used to find a matching element to replace. See []().
812
- # element::
813
- # The element to replace the existing element with
814
- # the previous element
815
- # Returns:: nil if no previous element was found.
1695
+ # :call-seq:
1696
+ # elements[] = index, replacement_element -> replacement_element or nil
1697
+ #
1698
+ # Replaces or adds an element.
1699
+ #
1700
+ # When <tt>eles[index]</tt> exists, replaces it with +replacement_element+
1701
+ # and returns +replacement_element+:
1702
+ #
1703
+ # d = REXML::Document.new(xml_string)
1704
+ # eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>>
1705
+ # eles[1] # => <book category='cooking'> ... </>
1706
+ # eles[1] = REXML::Element.new('foo')
1707
+ # eles[1] # => <foo/>
1708
+ #
1709
+ # Does nothing (or raises an exception)
1710
+ # if +replacement_element+ is not an \Element:
1711
+ # eles[2] # => <book category='web' cover='paperback'> ... </>
1712
+ # eles[2] = REXML::Text.new('bar')
1713
+ # eles[2] # => <book category='web' cover='paperback'> ... </>
1714
+ #
1715
+ # When <tt>eles[index]</tt> does not exist,
1716
+ # adds +replacement_element+ to the element and returns
1717
+ #
1718
+ # d = REXML::Document.new(xml_string)
1719
+ # eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>>
1720
+ # eles.size # => 4
1721
+ # eles[50] = REXML::Element.new('foo') # => <foo/>
1722
+ # eles.size # => 5
1723
+ # eles[5] # => <foo/>
1724
+ #
1725
+ # Does nothing (or raises an exception)
1726
+ # if +replacement_element+ is not an \Element:
1727
+ #
1728
+ # eles[50] = REXML::Text.new('bar') # => "bar"
1729
+ # eles.size # => 5
816
1730
  #
817
- # doc = Document.new '<a/>'
818
- # doc.root.elements[10] = Element.new('b') #-> <a><b/></a>
819
- # doc.root.elements[1] #-> <b/>
820
- # doc.root.elements[1] = Element.new('c') #-> <a><c/></a>
821
- # doc.root.elements['c'] = Element.new('d') #-> <a><d/></a>
822
1731
  def []=( index, element )
823
1732
  previous = self[index]
824
1733
  if previous.nil?
@@ -829,14 +1738,34 @@ module REXML
829
1738
  return previous
830
1739
  end
831
1740
 
832
- # Returns +true+ if there are no +Element+ children, +false+ otherwise
1741
+ # :call-seq:
1742
+ # empty? -> true or false
1743
+ #
1744
+ # Returns +true+ if there are no children, +false+ otherwise.
1745
+ #
1746
+ # d = REXML::Document.new('')
1747
+ # d.elements.empty? # => true
1748
+ # d = REXML::Document.new(xml_string)
1749
+ # d.elements.empty? # => false
1750
+ #
833
1751
  def empty?
834
1752
  @element.find{ |child| child.kind_of? Element}.nil?
835
1753
  end
836
1754
 
837
- # Returns the index of the supplied child (starting at 1), or -1 if
838
- # the element is not a child
839
- # element:: an +Element+ child
1755
+ # :call-seq:
1756
+ # index(element)
1757
+ #
1758
+ # Returns the 1-based index of the given +element+, if found;
1759
+ # otherwise, returns -1:
1760
+ #
1761
+ # d = REXML::Document.new(xml_string)
1762
+ # elements = d.root.elements
1763
+ # ele_1, ele_2, ele_3, ele_4 = *elements
1764
+ # elements.index(ele_4) # => 4
1765
+ # elements.delete(ele_3)
1766
+ # elements.index(ele_4) # => 3
1767
+ # elements.index(ele_3) # => -1
1768
+ #
840
1769
  def index element
841
1770
  rv = 0
842
1771
  found = @element.find do |child|
@@ -848,17 +1777,47 @@ module REXML
848
1777
  return -1
849
1778
  end
850
1779
 
851
- # Deletes a child Element
852
- # element::
853
- # Either an Element, which is removed directly; an
854
- # xpath, where the first matching child is removed; or an Integer,
855
- # where the n'th Element is removed.
856
- # Returns:: the removed child
857
- # doc = Document.new '<a><b/><c/><c id="1"/></a>'
858
- # b = doc.root.elements[1]
859
- # doc.root.elements.delete b #-> <a><c/><c id="1"/></a>
860
- # doc.elements.delete("a/c[@id='1']") #-> <a><c/></a>
861
- # doc.root.elements.delete 1 #-> <a/>
1780
+ # :call-seq:
1781
+ # delete(index) -> removed_element or nil
1782
+ # delete(element) -> removed_element or nil
1783
+ # delete(xpath) -> removed_element or nil
1784
+ #
1785
+ # Removes an element; returns the removed element, or +nil+ if none removed.
1786
+ #
1787
+ # With integer argument +index+ given,
1788
+ # removes the child element at that offset:
1789
+ #
1790
+ # d = REXML::Document.new(xml_string)
1791
+ # elements = d.root.elements
1792
+ # elements.size # => 4
1793
+ # elements[2] # => <book category='children'> ... </>
1794
+ # elements.delete(2) # => <book category='children'> ... </>
1795
+ # elements.size # => 3
1796
+ # elements[2] # => <book category='web'> ... </>
1797
+ # elements.delete(50) # => nil
1798
+ #
1799
+ # With element argument +element+ given,
1800
+ # removes that child element:
1801
+ #
1802
+ # d = REXML::Document.new(xml_string)
1803
+ # elements = d.root.elements
1804
+ # ele_1, ele_2, ele_3, ele_4 = *elements
1805
+ # elements.size # => 4
1806
+ # elements[2] # => <book category='children'> ... </>
1807
+ # elements.delete(ele_2) # => <book category='children'> ... </>
1808
+ # elements.size # => 3
1809
+ # elements[2] # => <book category='web'> ... </>
1810
+ # elements.delete(ele_2) # => nil
1811
+ #
1812
+ # With string argument +xpath+ given,
1813
+ # removes the first element found via that xpath:
1814
+ #
1815
+ # d = REXML::Document.new(xml_string)
1816
+ # elements = d.root.elements
1817
+ # elements.delete('//book') # => <book category='cooking'> ... </>
1818
+ # elements.delete('//book [@category="children"]') # => <book category='children'> ... </>
1819
+ # elements.delete('//nosuch') # => nil
1820
+ #
862
1821
  def delete element
863
1822
  if element.kind_of? Element
864
1823
  @element.delete element
@@ -868,12 +1827,23 @@ module REXML
868
1827
  end
869
1828
  end
870
1829
 
871
- # Removes multiple elements. Filters for Element children, regardless of
872
- # XPath matching.
873
- # xpath:: all elements matching this String path are removed.
874
- # Returns:: an Array of Elements that have been removed
875
- # doc = Document.new '<a><c/><c/><c/><c/></a>'
876
- # deleted = doc.elements.delete_all 'a/c' #-> [<c/>, <c/>, <c/>, <c/>]
1830
+ # :call-seq:
1831
+ # delete_all(xpath)
1832
+ #
1833
+ # Removes all elements found via the given +xpath+;
1834
+ # returns the array of removed elements, if any, else +nil+.
1835
+ #
1836
+ # d = REXML::Document.new(xml_string)
1837
+ # elements = d.root.elements
1838
+ # elements.size # => 4
1839
+ # deleted_elements = elements.delete_all('//book [@category="web"]')
1840
+ # deleted_elements.size # => 2
1841
+ # elements.size # => 2
1842
+ # deleted_elements = elements.delete_all('//book')
1843
+ # deleted_elements.size # => 2
1844
+ # elements.size # => 0
1845
+ # elements.delete_all('//book') # => []
1846
+ #
877
1847
  def delete_all( xpath )
878
1848
  rv = []
879
1849
  XPath::each( @element, xpath) {|element|
@@ -886,15 +1856,68 @@ module REXML
886
1856
  return rv
887
1857
  end
888
1858
 
889
- # Adds an element
890
- # element::
891
- # if supplied, is either an Element, String, or
892
- # Source (see Element.initialize). If not supplied or nil, a
893
- # new, default Element will be constructed
894
- # Returns:: the added Element
895
- # a = Element.new('a')
896
- # a.elements.add(Element.new('b')) #-> <a><b/></a>
897
- # a.elements.add('c') #-> <a><b/><c/></a>
1859
+ # :call-seq:
1860
+ # add -> new_element
1861
+ # add(name) -> new_element
1862
+ # add(element) -> element
1863
+ #
1864
+ # Adds an element; returns the element added.
1865
+ #
1866
+ # With no argument, creates and adds a new element.
1867
+ # The new element has:
1868
+ #
1869
+ # - No name.
1870
+ # - \Parent from the \Elements object.
1871
+ # - Context from the that parent.
1872
+ #
1873
+ # Example:
1874
+ #
1875
+ # d = REXML::Document.new(xml_string)
1876
+ # elements = d.root.elements
1877
+ # parent = elements.parent # => <bookstore> ... </>
1878
+ # parent.context = {raw: :all}
1879
+ # elements.size # => 4
1880
+ # new_element = elements.add # => </>
1881
+ # elements.size # => 5
1882
+ # new_element.name # => nil
1883
+ # new_element.parent # => <bookstore> ... </>
1884
+ # new_element.context # => {:raw=>:all}
1885
+ #
1886
+ # With string argument +name+, creates and adds a new element.
1887
+ # The new element has:
1888
+ #
1889
+ # - Name +name+.
1890
+ # - \Parent from the \Elements object.
1891
+ # - Context from the that parent.
1892
+ #
1893
+ # Example:
1894
+ #
1895
+ # d = REXML::Document.new(xml_string)
1896
+ # elements = d.root.elements
1897
+ # parent = elements.parent # => <bookstore> ... </>
1898
+ # parent.context = {raw: :all}
1899
+ # elements.size # => 4
1900
+ # new_element = elements.add('foo') # => <foo/>
1901
+ # elements.size # => 5
1902
+ # new_element.name # => "foo"
1903
+ # new_element.parent # => <bookstore> ... </>
1904
+ # new_element.context # => {:raw=>:all}
1905
+ #
1906
+ # With argument +element+,
1907
+ # creates and adds a clone of the given +element+.
1908
+ # The new element has name, parent, and context from the given +element+.
1909
+ #
1910
+ # d = REXML::Document.new(xml_string)
1911
+ # elements = d.root.elements
1912
+ # elements.size # => 4
1913
+ # e0 = REXML::Element.new('foo')
1914
+ # e1 = REXML::Element.new('bar', e0, {raw: :all})
1915
+ # element = elements.add(e1) # => <bar/>
1916
+ # elements.size # => 5
1917
+ # element.name # => "bar"
1918
+ # element.parent # => <bookstore> ... </>
1919
+ # element.context # => {:raw=>:all}
1920
+ #
898
1921
  def add element=nil
899
1922
  if element.nil?
900
1923
  Element.new("", self, @element.context)
@@ -909,24 +1932,55 @@ module REXML
909
1932
 
910
1933
  alias :<< :add
911
1934
 
912
- # Iterates through all of the child Elements, optionally filtering
913
- # them by a given XPath
914
- # xpath::
915
- # optional. If supplied, this is a String XPath, and is used to
916
- # filter the children, so that only matching children are yielded. Note
917
- # that XPaths are automatically filtered for Elements, so that
918
- # non-Element children will not be yielded
919
- # doc = Document.new '<a><b/><c/><d/>sean<b/><c/><d/></a>'
920
- # doc.root.elements.each {|e|p e} #-> Yields b, c, d, b, c, d elements
921
- # doc.root.elements.each('b') {|e|p e} #-> Yields b, b elements
922
- # doc.root.elements.each('child::node()') {|e|p e}
923
- # #-> Yields <b/>, <c/>, <d/>, <b/>, <c/>, <d/>
924
- # XPath.each(doc.root, 'child::node()', &block)
925
- # #-> Yields <b/>, <c/>, <d/>, sean, <b/>, <c/>, <d/>
1935
+ # :call-seq:
1936
+ # each(xpath = nil) {|element| ... } -> self
1937
+ #
1938
+ # Iterates over the elements.
1939
+ #
1940
+ # With no argument, calls the block with each element:
1941
+ #
1942
+ # d = REXML::Document.new(xml_string)
1943
+ # elements = d.root.elements
1944
+ # elements.each {|element| p element }
1945
+ #
1946
+ # Output:
1947
+ #
1948
+ # <book category='cooking'> ... </>
1949
+ # <book category='children'> ... </>
1950
+ # <book category='web'> ... </>
1951
+ # <book category='web' cover='paperback'> ... </>
1952
+ #
1953
+ # With argument +xpath+, calls the block with each element
1954
+ # that matches the given +xpath+:
1955
+ #
1956
+ # elements.each('//book [@category="web"]') {|element| p element }
1957
+ #
1958
+ # Output:
1959
+ #
1960
+ # <book category='web'> ... </>
1961
+ # <book category='web' cover='paperback'> ... </>
1962
+ #
926
1963
  def each( xpath=nil )
927
1964
  XPath::each( @element, xpath ) {|e| yield e if e.kind_of? Element }
928
1965
  end
929
1966
 
1967
+ # :call-seq:
1968
+ # collect(xpath = nil) {|element| ... } -> array
1969
+ #
1970
+ # Iterates over the elements; returns the array of block return values.
1971
+ #
1972
+ # With no argument, iterates over all elements:
1973
+ #
1974
+ # d = REXML::Document.new(xml_string)
1975
+ # elements = d.root.elements
1976
+ # elements.collect {|element| element.size } # => [9, 9, 17, 9]
1977
+ #
1978
+ # With argument +xpath+, iterates over elements that match
1979
+ # the given +xpath+:
1980
+ #
1981
+ # xpath = '//book [@category="web"]'
1982
+ # elements.collect(xpath) {|element| element.size } # => [17, 9]
1983
+ #
930
1984
  def collect( xpath=nil )
931
1985
  collection = []
932
1986
  XPath::each( @element, xpath ) {|e|
@@ -935,6 +1989,83 @@ module REXML
935
1989
  collection
936
1990
  end
937
1991
 
1992
+ # :call-seq:
1993
+ # inject(xpath = nil, initial = nil) -> object
1994
+ #
1995
+ # Calls the block with elements; returns the last block return value.
1996
+ #
1997
+ # With no argument, iterates over the elements, calling the block
1998
+ # <tt>elements.size - 1</tt> times.
1999
+ #
2000
+ # - The first call passes the first and second elements.
2001
+ # - The second call passes the first block return value and the third element.
2002
+ # - The third call passes the second block return value and the fourth element.
2003
+ # - And so on.
2004
+ #
2005
+ # In this example, the block returns the passed element,
2006
+ # which is then the object argument to the next call:
2007
+ #
2008
+ # d = REXML::Document.new(xml_string)
2009
+ # elements = d.root.elements
2010
+ # elements.inject do |object, element|
2011
+ # p [elements.index(object), elements.index(element)]
2012
+ # element
2013
+ # end
2014
+ #
2015
+ # Output:
2016
+ #
2017
+ # [1, 2]
2018
+ # [2, 3]
2019
+ # [3, 4]
2020
+ #
2021
+ # With the single argument +xpath+, calls the block only with
2022
+ # elements matching that xpath:
2023
+ #
2024
+ # elements.inject('//book [@category="web"]') do |object, element|
2025
+ # p [elements.index(object), elements.index(element)]
2026
+ # element
2027
+ # end
2028
+ #
2029
+ # Output:
2030
+ #
2031
+ # [3, 4]
2032
+ #
2033
+ # With argument +xpath+ given as +nil+
2034
+ # and argument +initial+ also given,
2035
+ # calls the block once for each element.
2036
+ #
2037
+ # - The first call passes the +initial+ and the first element.
2038
+ # - The second call passes the first block return value and the second element.
2039
+ # - The third call passes the second block return value and the third element.
2040
+ # - And so on.
2041
+ #
2042
+ # In this example, the first object index is <tt>-1</tt>
2043
+ #
2044
+ # elements.inject(nil, 'Initial') do |object, element|
2045
+ # p [elements.index(object), elements.index(element)]
2046
+ # element
2047
+ # end
2048
+ #
2049
+ # Output:
2050
+ #
2051
+ # [-1, 1]
2052
+ # [1, 2]
2053
+ # [2, 3]
2054
+ # [3, 4]
2055
+ #
2056
+ # In this form the passed object can be used as an accumulator:
2057
+ #
2058
+ # elements.inject(nil, 0) do |total, element|
2059
+ # total += element.size
2060
+ # end # => 44
2061
+ #
2062
+ # With both arguments +xpath+ and +initial+ are given,
2063
+ # calls the block only with elements matching that xpath:
2064
+ #
2065
+ # elements.inject('//book [@category="web"]', 0) do |total, element|
2066
+ # total += element.size
2067
+ # end # => 26
2068
+ #
938
2069
  def inject( xpath=nil, initial=nil )
939
2070
  first = true
940
2071
  XPath::each( @element, xpath ) {|e|
@@ -950,23 +2081,39 @@ module REXML
950
2081
  initial
951
2082
  end
952
2083
 
953
- # Returns the number of +Element+ children of the parent object.
954
- # doc = Document.new '<a>sean<b/>elliott<b/>russell<b/></a>'
955
- # doc.root.size #-> 6, 3 element and 3 text nodes
956
- # doc.root.elements.size #-> 3
2084
+ # :call-seq:
2085
+ # size -> integer
2086
+ #
2087
+ # Returns the count of \Element children:
2088
+ #
2089
+ # d = REXML::Document.new '<a>sean<b/>elliott<b/>russell<b/></a>'
2090
+ # d.root.elements.size # => 3 # Three elements.
2091
+ # d.root.size # => 6 # Three elements plus three text nodes..
2092
+ #
957
2093
  def size
958
2094
  count = 0
959
2095
  @element.each {|child| count+=1 if child.kind_of? Element }
960
2096
  count
961
2097
  end
962
2098
 
963
- # Returns an Array of Element children. An XPath may be supplied to
964
- # filter the children. Only Element children are returned, even if the
965
- # supplied XPath matches non-Element children.
966
- # doc = Document.new '<a>sean<b/>elliott<c/></a>'
967
- # doc.root.elements.to_a #-> [ <b/>, <c/> ]
968
- # doc.root.elements.to_a("child::node()") #-> [ <b/>, <c/> ]
969
- # XPath.match(doc.root, "child::node()") #-> [ sean, <b/>, elliott, <c/> ]
2099
+ # :call-seq:
2100
+ # to_a(xpath = nil) -> array_of_elements
2101
+ #
2102
+ # Returns an array of element children (not including non-element children).
2103
+ #
2104
+ # With no argument, returns an array of all element children:
2105
+ #
2106
+ # d = REXML::Document.new '<a>sean<b/>elliott<c/></a>'
2107
+ # elements = d.root.elements
2108
+ # elements.to_a # => [<b/>, <c/>] # Omits non-element children.
2109
+ # children = d.root.children
2110
+ # children # => ["sean", <b/>, "elliott", <c/>] # Includes non-element children.
2111
+ #
2112
+ # With argument +xpath+, returns an array of element children
2113
+ # that match the xpath:
2114
+ #
2115
+ # elements.to_a('//c') # => [<c/>]
2116
+ #
970
2117
  def to_a( xpath=nil )
971
2118
  rv = XPath.match( @element, xpath )
972
2119
  return rv.find_all{|e| e.kind_of? Element} if xpath
@@ -988,36 +2135,89 @@ module REXML
988
2135
  # A class that defines the set of Attributes of an Element and provides
989
2136
  # operations for accessing elements in that set.
990
2137
  class Attributes < Hash
991
- # Constructor
992
- # element:: the Element of which this is an Attribute
2138
+
2139
+ # :call-seq:
2140
+ # new(element)
2141
+ #
2142
+ # Creates and returns a new \REXML::Attributes object.
2143
+ # The element given by argument +element+ is stored,
2144
+ # but its own attributes are not modified:
2145
+ #
2146
+ # ele = REXML::Element.new('foo')
2147
+ # attrs = REXML::Attributes.new(ele)
2148
+ # attrs.object_id == ele.attributes.object_id # => false
2149
+ #
2150
+ # Other instance methods in class \REXML::Attributes may refer to:
2151
+ #
2152
+ # - +element.document+.
2153
+ # - +element.prefix+.
2154
+ # - +element.expanded_name+.
2155
+ #
993
2156
  def initialize element
994
2157
  @element = element
995
2158
  end
996
2159
 
997
- # Fetches an attribute value. If you want to get the Attribute itself,
998
- # use get_attribute()
999
- # name:: an XPath attribute name. Namespaces are relevant here.
1000
- # Returns::
1001
- # the String value of the matching attribute, or +nil+ if no
1002
- # matching attribute was found. This is the unnormalized value
1003
- # (with entities expanded).
2160
+ # :call-seq:
2161
+ # [name] -> attribute_value or nil
2162
+ #
2163
+ # Returns the value for the attribute given by +name+,
2164
+ # if it exists; otherwise +nil+.
2165
+ # The value returned is the unnormalized attribute value,
2166
+ # with entities expanded:
2167
+ #
2168
+ # xml_string = <<-EOT
2169
+ # <root xmlns:foo="http://foo" xmlns:bar="http://bar">
2170
+ # <ele foo:att='1' bar:att='2' att='&lt;'/>
2171
+ # </root>
2172
+ # EOT
2173
+ # d = REXML::Document.new(xml_string)
2174
+ # ele = d.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
2175
+ # ele.attributes['att'] # => "<"
2176
+ # ele.attributes['bar:att'] # => "2"
2177
+ # ele.attributes['nosuch'] # => nil
2178
+ #
2179
+ # Related: get_attribute (returns an \Attribute object).
1004
2180
  #
1005
- # doc = Document.new "<a foo:att='1' bar:att='2' att='&lt;'/>"
1006
- # doc.root.attributes['att'] #-> '<'
1007
- # doc.root.attributes['bar:att'] #-> '2'
1008
2181
  def [](name)
1009
2182
  attr = get_attribute(name)
1010
2183
  return attr.value unless attr.nil?
1011
2184
  return nil
1012
2185
  end
1013
2186
 
2187
+ # :call-seq:
2188
+ # to_a -> array_of_attribute_objects
2189
+ #
2190
+ # Returns an array of \REXML::Attribute objects representing
2191
+ # the attributes:
2192
+ #
2193
+ # xml_string = <<-EOT
2194
+ # <root xmlns:foo="http://foo" xmlns:bar="http://bar">
2195
+ # <ele foo:att='1' bar:att='2' att='&lt;'/>
2196
+ # </root>
2197
+ # EOT
2198
+ # d = REXML::Document.new(xml_string)
2199
+ # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
2200
+ # attrs = ele.attributes.to_a # => [foo:att='1', bar:att='2', att='&lt;']
2201
+ # attrs.first.class # => REXML::Attribute
2202
+ #
1014
2203
  def to_a
1015
2204
  enum_for(:each_attribute).to_a
1016
2205
  end
1017
2206
 
1018
- # Returns the number of attributes the owning Element contains.
1019
- # doc = Document "<a x='1' y='2' foo:x='3'/>"
1020
- # doc.root.attributes.length #-> 3
2207
+ # :call-seq:
2208
+ # length
2209
+ #
2210
+ # Returns the count of attributes:
2211
+ #
2212
+ # xml_string = <<-EOT
2213
+ # <root xmlns:foo="http://foo" xmlns:bar="http://bar">
2214
+ # <ele foo:att='1' bar:att='2' att='&lt;'/>
2215
+ # </root>
2216
+ # EOT
2217
+ # d = REXML::Document.new(xml_string)
2218
+ # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
2219
+ # ele.attributes.length # => 3
2220
+ #
1021
2221
  def length
1022
2222
  c = 0
1023
2223
  each_attribute { c+=1 }
@@ -1025,13 +2225,28 @@ module REXML
1025
2225
  end
1026
2226
  alias :size :length
1027
2227
 
1028
- # Iterates over the attributes of an Element. Yields actual Attribute
1029
- # nodes, not String values.
2228
+ # :call-seq:
2229
+ # each_attribute {|attr| ... }
2230
+ #
2231
+ # Calls the given block with each \REXML::Attribute object:
2232
+ #
2233
+ # xml_string = <<-EOT
2234
+ # <root xmlns:foo="http://foo" xmlns:bar="http://bar">
2235
+ # <ele foo:att='1' bar:att='2' att='&lt;'/>
2236
+ # </root>
2237
+ # EOT
2238
+ # d = REXML::Document.new(xml_string)
2239
+ # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
2240
+ # ele.attributes.each_attribute do |attr|
2241
+ # p [attr.class, attr]
2242
+ # end
2243
+ #
2244
+ # Output:
2245
+ #
2246
+ # [REXML::Attribute, foo:att='1']
2247
+ # [REXML::Attribute, bar:att='2']
2248
+ # [REXML::Attribute, att='&lt;']
1030
2249
  #
1031
- # doc = Document.new '<a x="1" y="2"/>'
1032
- # doc.root.attributes.each_attribute {|attr|
1033
- # p attr.expanded_name+" => "+attr.value
1034
- # }
1035
2250
  def each_attribute # :yields: attribute
1036
2251
  return to_enum(__method__) unless block_given?
1037
2252
  each_value do |val|
@@ -1043,11 +2258,28 @@ module REXML
1043
2258
  end
1044
2259
  end
1045
2260
 
1046
- # Iterates over each attribute of an Element, yielding the expanded name
1047
- # and value as a pair of Strings.
2261
+ # :call-seq:
2262
+ # each {|expanded_name, value| ... }
2263
+ #
2264
+ # Calls the given block with each expanded-name/value pair:
2265
+ #
2266
+ # xml_string = <<-EOT
2267
+ # <root xmlns:foo="http://foo" xmlns:bar="http://bar">
2268
+ # <ele foo:att='1' bar:att='2' att='&lt;'/>
2269
+ # </root>
2270
+ # EOT
2271
+ # d = REXML::Document.new(xml_string)
2272
+ # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
2273
+ # ele.attributes.each do |expanded_name, value|
2274
+ # p [expanded_name, value]
2275
+ # end
2276
+ #
2277
+ # Output:
2278
+ #
2279
+ # ["foo:att", "1"]
2280
+ # ["bar:att", "2"]
2281
+ # ["att", "<"]
1048
2282
  #
1049
- # doc = Document.new '<a x="1" y="2"/>'
1050
- # doc.root.attributes.each {|name, value| p name+" => "+value }
1051
2283
  def each
1052
2284
  return to_enum(__method__) unless block_given?
1053
2285
  each_attribute do |attr|
@@ -1055,15 +2287,25 @@ module REXML
1055
2287
  end
1056
2288
  end
1057
2289
 
1058
- # Fetches an attribute
1059
- # name::
1060
- # the name by which to search for the attribute. Can be a
1061
- # <tt>prefix:name</tt> namespace name.
1062
- # Returns:: The first matching attribute, or nil if there was none. This
1063
- # value is an Attribute node, not the String value of the attribute.
1064
- # doc = Document.new '<a x:foo="1" foo="2" bar="3"/>'
1065
- # doc.root.attributes.get_attribute("foo").value #-> "2"
1066
- # doc.root.attributes.get_attribute("x:foo").value #-> "1"
2290
+ # :call-seq:
2291
+ # get_attribute(name) -> attribute_object or nil
2292
+ #
2293
+ # Returns the \REXML::Attribute object for the given +name+:
2294
+ #
2295
+ # xml_string = <<-EOT
2296
+ # <root xmlns:foo="http://foo" xmlns:bar="http://bar">
2297
+ # <ele foo:att='1' bar:att='2' att='&lt;'/>
2298
+ # </root>
2299
+ # EOT
2300
+ # d = REXML::Document.new(xml_string)
2301
+ # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
2302
+ # attrs = ele.attributes
2303
+ # attrs.get_attribute('foo:att') # => foo:att='1'
2304
+ # attrs.get_attribute('foo:att').class # => REXML::Attribute
2305
+ # attrs.get_attribute('bar:att') # => bar:att='2'
2306
+ # attrs.get_attribute('att') # => att='&lt;'
2307
+ # attrs.get_attribute('nosuch') # => nil
2308
+ #
1067
2309
  def get_attribute( name )
1068
2310
  attr = fetch( name, nil )
1069
2311
  if attr.nil?
@@ -1097,18 +2339,29 @@ module REXML
1097
2339
  return attr
1098
2340
  end
1099
2341
 
1100
- # Sets an attribute, overwriting any existing attribute value by the
1101
- # same name. Namespace is significant.
1102
- # name:: the name of the attribute
1103
- # value::
1104
- # (optional) If supplied, the value of the attribute. If
1105
- # nil, any existing matching attribute is deleted.
1106
- # Returns::
1107
- # Owning element
1108
- # doc = Document.new "<a x:foo='1' foo='3'/>"
1109
- # doc.root.attributes['y:foo'] = '2'
1110
- # doc.root.attributes['foo'] = '4'
1111
- # doc.root.attributes['x:foo'] = nil
2342
+ # :call-seq:
2343
+ # [name] = value -> value
2344
+ #
2345
+ # When +value+ is non-+nil+,
2346
+ # assigns that to the attribute for the given +name+,
2347
+ # overwriting the previous value if it exists:
2348
+ #
2349
+ # xml_string = <<-EOT
2350
+ # <root xmlns:foo="http://foo" xmlns:bar="http://bar">
2351
+ # <ele foo:att='1' bar:att='2' att='&lt;'/>
2352
+ # </root>
2353
+ # EOT
2354
+ # d = REXML::Document.new(xml_string)
2355
+ # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
2356
+ # attrs = ele.attributes
2357
+ # attrs['foo:att'] = '2' # => "2"
2358
+ # attrs['baz:att'] = '3' # => "3"
2359
+ #
2360
+ # When +value+ is +nil+, deletes the attribute if it exists:
2361
+ #
2362
+ # attrs['baz:att'] = nil
2363
+ # attrs.include?('baz:att') # => false
2364
+ #
1112
2365
  def []=( name, value )
1113
2366
  if value.nil? # Delete the named attribute
1114
2367
  attr = get_attribute(name)
@@ -1131,17 +2384,6 @@ module REXML
1131
2384
  elsif old_attr.kind_of? Hash
1132
2385
  old_attr[value.prefix] = value
1133
2386
  elsif old_attr.prefix != value.prefix
1134
- # Check for conflicting namespaces
1135
- if value.prefix != "xmlns" and old_attr.prefix != "xmlns"
1136
- old_namespace = old_attr.namespace
1137
- new_namespace = value.namespace
1138
- if old_namespace == new_namespace
1139
- raise ParseException.new(
1140
- "Namespace conflict in adding attribute \"#{value.name}\": "+
1141
- "Prefix \"#{old_attr.prefix}\" = \"#{old_namespace}\" and "+
1142
- "prefix \"#{value.prefix}\" = \"#{new_namespace}\"")
1143
- end
1144
- end
1145
2387
  store value.name, {old_attr.prefix => old_attr,
1146
2388
  value.prefix => value}
1147
2389
  else
@@ -1150,12 +2392,17 @@ module REXML
1150
2392
  return @element
1151
2393
  end
1152
2394
 
1153
- # Returns an array of Strings containing all of the prefixes declared
1154
- # by this set of # attributes. The array does not include the default
2395
+ # :call-seq:
2396
+ # prefixes -> array_of_prefix_strings
2397
+ #
2398
+ # Returns an array of prefix strings in the attributes.
2399
+ # The array does not include the default
1155
2400
  # namespace declaration, if one exists.
1156
- # doc = Document.new("<a xmlns='foo' xmlns:x='bar' xmlns:y='twee' "+
1157
- # "z='glorp' p:k='gru'/>")
1158
- # prefixes = doc.root.attributes.prefixes #-> ['x', 'y']
2401
+ #
2402
+ # xml_string = '<a xmlns="foo" xmlns:x="bar" xmlns:y="twee" z="glorp"/>'
2403
+ # d = REXML::Document.new(xml_string)
2404
+ # d.root.attributes.prefixes # => ["x", "y"]
2405
+ #
1159
2406
  def prefixes
1160
2407
  ns = []
1161
2408
  each_attribute do |attribute|
@@ -1172,6 +2419,15 @@ module REXML
1172
2419
  ns
1173
2420
  end
1174
2421
 
2422
+ # :call-seq:
2423
+ # namespaces
2424
+ #
2425
+ # Returns a hash of name/value pairs for the namespaces:
2426
+ #
2427
+ # xml_string = '<a xmlns="foo" xmlns:x="bar" xmlns:y="twee" z="glorp"/>'
2428
+ # d = REXML::Document.new(xml_string)
2429
+ # d.root.attributes.namespaces # => {"xmlns"=>"foo", "x"=>"bar", "y"=>"twee"}
2430
+ #
1175
2431
  def namespaces
1176
2432
  namespaces = {}
1177
2433
  each_attribute do |attribute|
@@ -1188,16 +2444,34 @@ module REXML
1188
2444
  namespaces
1189
2445
  end
1190
2446
 
1191
- # Removes an attribute
1192
- # attribute::
1193
- # either a String, which is the name of the attribute to remove --
1194
- # namespaces are significant here -- or the attribute to remove.
1195
- # Returns:: the owning element
1196
- # doc = Document.new "<a y:foo='0' x:foo='1' foo='3' z:foo='4'/>"
1197
- # doc.root.attributes.delete 'foo' #-> <a y:foo='0' x:foo='1' z:foo='4'/>"
1198
- # doc.root.attributes.delete 'x:foo' #-> <a y:foo='0' z:foo='4'/>"
1199
- # attr = doc.root.attributes.get_attribute('y:foo')
1200
- # doc.root.attributes.delete attr #-> <a z:foo='4'/>"
2447
+ # :call-seq:
2448
+ # delete(name) -> element
2449
+ # delete(attribute) -> element
2450
+ #
2451
+ # Removes a specified attribute if it exists;
2452
+ # returns the attributes' element.
2453
+ #
2454
+ # When string argument +name+ is given,
2455
+ # removes the attribute of that name if it exists:
2456
+ #
2457
+ # xml_string = <<-EOT
2458
+ # <root xmlns:foo="http://foo" xmlns:bar="http://bar">
2459
+ # <ele foo:att='1' bar:att='2' att='&lt;'/>
2460
+ # </root>
2461
+ # EOT
2462
+ # d = REXML::Document.new(xml_string)
2463
+ # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
2464
+ # attrs = ele.attributes
2465
+ # attrs.delete('foo:att') # => <ele bar:att='2' att='&lt;'/>
2466
+ # attrs.delete('foo:att') # => <ele bar:att='2' att='&lt;'/>
2467
+ #
2468
+ # When attribute argument +attribute+ is given,
2469
+ # removes that attribute if it exists:
2470
+ #
2471
+ # attr = REXML::Attribute.new('bar:att', '2')
2472
+ # attrs.delete(attr) # => <ele att='&lt;'/> # => <ele att='&lt;'/>
2473
+ # attrs.delete(attr) # => <ele att='&lt;'/> # => <ele/>
2474
+ #
1201
2475
  def delete( attribute )
1202
2476
  name = nil
1203
2477
  prefix = nil
@@ -1225,19 +2499,48 @@ module REXML
1225
2499
  @element
1226
2500
  end
1227
2501
 
1228
- # Adds an attribute, overriding any existing attribute by the
1229
- # same name. Namespaces are significant.
1230
- # attribute:: An Attribute
2502
+ # :call-seq:
2503
+ # add(attribute) -> attribute
2504
+ #
2505
+ # Adds attribute +attribute+, replacing the previous
2506
+ # attribute of the same name if it exists;
2507
+ # returns +attribute+:
2508
+ #
2509
+ # xml_string = <<-EOT
2510
+ # <root xmlns:foo="http://foo" xmlns:bar="http://bar">
2511
+ # <ele foo:att='1' bar:att='2' att='&lt;'/>
2512
+ # </root>
2513
+ # EOT
2514
+ # d = REXML::Document.new(xml_string)
2515
+ # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
2516
+ # attrs = ele.attributes
2517
+ # attrs # => {"att"=>{"foo"=>foo:att='1', "bar"=>bar:att='2', ""=>att='&lt;'}}
2518
+ # attrs.add(REXML::Attribute.new('foo:att', '2')) # => foo:att='2'
2519
+ # attrs.add(REXML::Attribute.new('baz', '3')) # => baz='3'
2520
+ # attrs.include?('baz') # => true
2521
+ #
1231
2522
  def add( attribute )
1232
2523
  self[attribute.name] = attribute
1233
2524
  end
1234
2525
 
1235
2526
  alias :<< :add
1236
2527
 
1237
- # Deletes all attributes matching a name. Namespaces are significant.
1238
- # name::
1239
- # A String; all attributes that match this path will be removed
1240
- # Returns:: an Array of the Attributes that were removed
2528
+ # :call-seq:
2529
+ # delete_all(name) -> array_of_removed_attributes
2530
+ #
2531
+ # Removes all attributes matching the given +name+;
2532
+ # returns an array of the removed attributes:
2533
+ #
2534
+ # xml_string = <<-EOT
2535
+ # <root xmlns:foo="http://foo" xmlns:bar="http://bar">
2536
+ # <ele foo:att='1' bar:att='2' att='&lt;'/>
2537
+ # </root>
2538
+ # EOT
2539
+ # d = REXML::Document.new(xml_string)
2540
+ # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
2541
+ # attrs = ele.attributes
2542
+ # attrs.delete_all('att') # => [att='&lt;']
2543
+ #
1241
2544
  def delete_all( name )
1242
2545
  rv = []
1243
2546
  each_attribute { |attribute|
@@ -1247,11 +2550,23 @@ module REXML
1247
2550
  return rv
1248
2551
  end
1249
2552
 
1250
- # The +get_attribute_ns+ method retrieves a method by its namespace
1251
- # and name. Thus it is possible to reliably identify an attribute
1252
- # even if an XML processor has changed the prefix.
2553
+ # :call-seq:
2554
+ # get_attribute_ns(namespace, name)
2555
+ #
2556
+ # Returns the \REXML::Attribute object among the attributes
2557
+ # that matches the given +namespace+ and +name+:
2558
+ #
2559
+ # xml_string = <<-EOT
2560
+ # <root xmlns:foo="http://foo" xmlns:bar="http://bar">
2561
+ # <ele foo:att='1' bar:att='2' att='&lt;'/>
2562
+ # </root>
2563
+ # EOT
2564
+ # d = REXML::Document.new(xml_string)
2565
+ # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='&lt;'/>
2566
+ # attrs = ele.attributes
2567
+ # attrs.get_attribute_ns('http://foo', 'att') # => foo:att='1'
2568
+ # attrs.get_attribute_ns('http://foo', 'nosuch') # => nil
1253
2569
  #
1254
- # Method contributed by Henrik Martensson
1255
2570
  def get_attribute_ns(namespace, name)
1256
2571
  result = nil
1257
2572
  each_attribute() { |attribute|