1
- require " weak_ref"
2
-
3
1
class XML::Node
4
2
LOOKS_LIKE_XPATH = /^(\.\/ |\/ |\.\. |\. $) /
5
3
6
4
# Every Node must keep a reference to its document Node. To keep things
7
5
# simple, a document Node merely references itself. An unlinked node must
8
6
# still reference its original document Node until adopted into another
9
7
# document's tree (the libxml nodes keep a pointer to their libxml doc).
10
- @document : Node
11
-
12
- # The constructors allocate a XML::Node for a libxml node once, so we don't
13
- # finalize a document twice for example.
14
- #
15
- # We store the reference into the libxml struct (_private) for documents
16
- # because a document's XML::Node lives as long as its libxml doc.
17
- #
18
- # However we can lose references to subtree XML::Node, so using _private would
19
- # leave dangling pointers. We thus keep a cache of weak references to all
20
- # nodes in the document, so we can still collect lost references, and at worst
21
- # reinstantiate a XML::Node if needed.
22
- protected getter! cache : Hash (LibXML ::Node * , WeakRef (Node ))?
8
+ @document : Document ?
23
9
24
10
# Unlinked Nodes, and all their descendant nodes, don't appear in the
25
11
# document's tree anymore, and must be manually freed. Yet, the finalizer
@@ -28,16 +14,7 @@ class XML::Node
28
14
protected getter? unlinked = false
29
15
30
16
# :nodoc:
31
- def self.new (doc : LibXML ::Doc * , errors : Array (Error )? = nil )
32
- if ptr = doc.value._private
33
- ptr.as(Node )
34
- else
35
- new(doc_: doc, errors_: errors)
36
- end
37
- end
38
-
39
- # :nodoc:
40
- def self.new (node : LibXML ::Node * , document : self ) : self
17
+ def self.new (node : LibXML ::Node * , document : Document ) : self
41
18
if node == document.@node
42
19
# should never happen, but just in case
43
20
return document
@@ -54,47 +31,28 @@ class XML::Node
54
31
55
32
# :nodoc:
56
33
@[Deprecated ]
57
- def self.new (node : LibXML ::Node * ) : self
58
- new(node, new(node.value.doc) )
34
+ def self.new (doc : LibXML ::Doc * , errors : Array ( Error )? = nil )
35
+ Document . new(doc, errors).as( Node )
59
36
end
60
37
61
38
# :nodoc:
62
39
@[Deprecated ]
63
- def self.new (node : LibXML ::Attr * ) : self
64
- new(node.as( LibXML :: Node * ), new(node.value.doc))
40
+ def self.new (node : LibXML ::Node * ) : self
41
+ new(node, Document . new(node.value.doc))
65
42
end
66
43
67
- # the initializers must never be called directly, use the constructors above
68
-
69
- private def initialize (* , doc_ : LibXML ::Doc * , errors_ : Array (Error )?)
70
- @node = doc_.as(LibXML ::Node * )
71
- @errors = errors_
72
- @cache = Hash (LibXML ::Node * , WeakRef (Node )).new
73
- @document = uninitialized Node
74
- @document = self
75
- doc_.value._private = self .as(Void * )
44
+ # :nodoc:
45
+ @[Deprecated ]
46
+ def self.new (node : LibXML ::Attr * ) : self
47
+ new(node.as(LibXML ::Node * ), Document .new(node.value.doc))
76
48
end
77
49
78
- private def initialize (* , node_ : LibXML ::Node * , document_ : self )
50
+ # must never be called directly, use the constructors above
51
+ private def initialize (* , node_ : LibXML ::Node * , document_ : Document )
79
52
@node = node_.as(LibXML ::Node * )
80
53
@document = document_
81
54
end
82
55
83
- # :nodoc:
84
- def finalize
85
- return unless @document == self
86
-
87
- # free unlinked nodes and their subtrees
88
- cache.each do |node , ref |
89
- if (obj = ref.value) && obj.unlinked?
90
- LibXML .xmlFreeNode(node)
91
- end
92
- end
93
-
94
- # free the doc and its subtree
95
- LibXML .xmlFreeDoc(@node .as(LibXML ::Doc * ))
96
- end
97
-
98
56
# Gets the attribute content for the *attribute* given by name.
99
57
# Raises `KeyError` if attribute is not found.
100
58
def [] (attribute : String ) : String
@@ -152,7 +110,7 @@ class XML::Node
152
110
153
111
child = @node .value.children
154
112
nodes = Slice (Node ).new(size) do
155
- node = Node .new(child, @ document )
113
+ node = Node .new(child, document)
156
114
child = child.value.next
157
115
node
158
116
end
@@ -179,9 +137,9 @@ class XML::Node
179
137
LibXML .xmlNodeSetContent(self , content)
180
138
end
181
139
182
- # Gets the document for this Node as a `XML::Node `.
183
- def document : XML :: Node
184
- @document
140
+ # Gets the document for this Node as a `Document `.
141
+ def document : Document
142
+ @document .not_nil!
185
143
end
186
144
187
145
# Returns `true` if this is a Document or HTML Document node.
@@ -197,16 +155,12 @@ class XML::Node
197
155
198
156
# Returns the encoding of this node's document.
199
157
def encoding : String ?
200
- if encoding = @document .@node .as(LibXML ::Doc * ).value.encoding
201
- String .new(encoding)
202
- end
158
+ document.encoding
203
159
end
204
160
205
161
# Returns the version of this node's document.
206
162
def version : String ?
207
- if version = @document .@node .as(LibXML ::Doc * ).value.version
208
- String .new(version)
209
- end
163
+ document.version
210
164
end
211
165
212
166
# Returns `true` if this is an Element node.
@@ -220,7 +174,7 @@ class XML::Node
220
174
child = @node .value.children
221
175
while child
222
176
if child.value.type == XML ::Node ::Type ::ELEMENT_NODE
223
- return Node .new(child, @ document )
177
+ return Node .new(child, document)
224
178
end
225
179
child = child.value.next
226
180
end
@@ -360,7 +314,7 @@ class XML::Node
360
314
# Returns the next sibling node or `nil` if not found.
361
315
def next : XML ::Node ?
362
316
next_node = @node .value.next
363
- next_node ? Node .new(next_node, @ document ) : nil
317
+ next_node ? Node .new(next_node, document) : nil
364
318
end
365
319
366
320
# :ditto:
@@ -373,7 +327,7 @@ class XML::Node
373
327
next_node = @node .value.next
374
328
while next_node
375
329
if next_node.value.type == XML ::Node ::Type ::ELEMENT_NODE
376
- return Node .new(next_node, @ document )
330
+ return Node .new(next_node, document)
377
331
end
378
332
next_node = next_node.value.next
379
333
end
@@ -423,7 +377,7 @@ class XML::Node
423
377
nil
424
378
else
425
379
ns = @node .value.ns
426
- ns ? Namespace .new(@ document , ns) : nil
380
+ ns ? Namespace .new(document, ns) : nil
427
381
end
428
382
end
429
383
@@ -433,7 +387,7 @@ class XML::Node
433
387
434
388
ns = @node .value.ns_def
435
389
while ns
436
- namespaces << Namespace .new(@ document , ns)
390
+ namespaces << Namespace .new(document, ns)
437
391
ns = ns.value.next
438
392
end
439
393
@@ -481,7 +435,7 @@ class XML::Node
481
435
482
436
if ns_list
483
437
while ns_list.value
484
- yield Namespace .new(@ document , ns_list.value)
438
+ yield Namespace .new(document, ns_list.value)
485
439
ns_list += 1
486
440
end
487
441
end
@@ -495,21 +449,21 @@ class XML::Node
495
449
# Returns the parent node or `nil` if not found.
496
450
def parent : XML ::Node ?
497
451
parent = @node .value.parent
498
- parent ? Node .new(parent, @ document ) : nil
452
+ parent ? Node .new(parent, document) : nil
499
453
end
500
454
501
455
# Returns the previous sibling node or `nil` if not found.
502
456
def previous : XML ::Node ?
503
457
prev_node = @node .value.prev
504
- prev_node ? Node .new(prev_node, @ document ) : nil
458
+ prev_node ? Node .new(prev_node, document) : nil
505
459
end
506
460
507
461
# Returns the previous sibling node that is an element or `nil` if not found.
508
462
def previous_element : XML ::Node ?
509
463
prev_node = @node .value.prev
510
464
while prev_node
511
465
if prev_node.value.type == XML ::Node ::Type ::ELEMENT_NODE
512
- return Node .new(prev_node, @ document )
466
+ return Node .new(prev_node, document)
513
467
end
514
468
prev_node = prev_node.value.prev
515
469
end
@@ -530,7 +484,7 @@ class XML::Node
530
484
# Returns the root node for this document or `nil`.
531
485
def root : XML ::Node ?
532
486
root = LibXML .xmlDocGetRootElement(@node .value.doc)
533
- root ? Node .new(root, @ document ) : nil
487
+ root ? Node .new(root, document) : nil
534
488
end
535
489
536
490
# Same as `#content`.
@@ -736,10 +690,9 @@ class XML::Node
736
690
xpath(path, namespaces, variables).as(String )
737
691
end
738
692
739
- # Returns the list of `XML::Error` found when parsing this document.
740
- # Returns `nil` if no errors were found.
693
+ @[Deprecated (" Access XML::Document#errors instead." )]
741
694
def errors : Array (XML ::Error )?
742
- return @errors unless @errors .try & .empty?
695
+ document.errors
743
696
end
744
697
745
698
private def check_no_null_byte (string )
0 commit comments