leptris 1.9.197.2-arm-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3890 -0
- data/CLAUDE.md +115 -0
- data/CONTEXT.md +39 -0
- data/LICENSE.md +33 -0
- data/README.adoc +755 -0
- data/Rakefile +407 -0
- data/TODO.perf/01-ext-accelerated-default-reads.md +28 -0
- data/TODO.perf/02-auto-enable-native-layer.md +19 -0
- data/TODO.perf/03-bulk-xpath-result-materialization.md +18 -0
- data/TODO.perf/04-ext-bound-serialization.md +15 -0
- data/TODO.perf/05-native-surface-completion.md +19 -0
- data/TODO.perf/06-perf-battery-native-legs.md +16 -0
- data/TODO.perf/07-native-read-floor.md +31 -0
- data/TODO.perf/08-version-coherent-native-mutations.md +24 -0
- data/TODO.perf/09-adoption-lift-fast-path.md +38 -0
- data/TODO.perf/10-bulk-attribute-materialization.md +20 -0
- data/TODO.perf/11-moxml-gap-battery.md +18 -0
- data/TODO.perf/12-document-lifetime-in-c.md +47 -0
- data/TODO.perf/13-structural-memos.md +49 -0
- data/TODO.perf/14-insert-family-c-bound.md +31 -0
- data/TODO.perf/15-compiled-expression-cache.md +35 -0
- data/TODO.perf/16-at-xpath-single-result-seam.md +38 -0
- data/TODO.perf/17-precomputed-fast-path-flags.md +35 -0
- data/TODO.perf/18-inner-html-one-c-pass.md +32 -0
- data/TODO.perf/19-lazy-node-pointer.md +39 -0
- data/TODO.perf/20-css-translation-cache.md +24 -0
- data/TODO.perf/21-key-memo-consult.md +20 -0
- data/TODO.perf/22-eager-nodeset-materialization.md +30 -0
- data/TODO.perf/23-cbound-value-mutations.md +27 -0
- data/TODO.perf/24-immutable-read-lanes.md +22 -0
- data/TODO.perf/25-scope-owned-bulk-path.md +39 -0
- data/TODO.perf/26-post-mutation-memo-seeding.md +26 -0
- data/TODO.perf/27-c-yield-traversal.md +38 -0
- data/TODO.perf/28-address-fills.md +25 -0
- data/TODO.perf/29-ns-xpath-compiled.md +23 -0
- data/TODO.perf/30-copy-and-element-child-faces.md +31 -0
- data/TODO.perf/31-battery-rounds-4-8.md +23 -0
- data/TODO.perf/32-clean-host-floor-table.md +29 -0
- data/TODO.perf/33-cbound-root-set.md +29 -0
- data/TODO.perf/34-fragment-fast-lane.md +41 -0
- data/TODO.perf/35-parse-default-c-face.md +24 -0
- data/TODO.perf/36-raise-in-c-serializer-encoding.md +34 -0
- data/TODO.perf/37-document-lazy-pointer.md +28 -0
- data/TODO.perf/38-bulk-walk-and-bench-gate.md +23 -0
- data/TODO.restructure/01-constraint-compliance-audit.md +40 -0
- data/TODO.restructure/02-deep-copy-seam.md +21 -0
- data/TODO.restructure/03-evaluation-context-seam.md +18 -0
- data/TODO.restructure/04-spec-mece-restructure.md +20 -0
- data/TODO.restructure/05-memory-documentation.md +13 -0
- data/TODO.restructure/06-architecture-map-refresh.md +12 -0
- data/TODO.restructure/07-external-gates.md +16 -0
- data/TODO.restructure/08-new-code-audit-and-ext-memo.md +11 -0
- data/TODO.restructure/09-iteration-scope.md +29 -0
- data/TODO.restructure/10-cold-attr-read-diet.md +19 -0
- data/TODO.restructure/11-construction-factory-diet.md +16 -0
- data/TODO.restructure/12-resultattr-merge.md +12 -0
- data/TODO.restructure/13-spec-mece-followup.md +9 -0
- data/TODO.restructure/14-constraint-audit-refresh.md +15 -0
- data/TODO.restructure/15-readme-feature-documentation.md +10 -0
- data/TODO.restructure/16-claudemd-map-refresh.md +5 -0
- data/TODO.restructure/17-perf-drift-record.md +12 -0
- data/TODO.restructure/18-audit-spec-doubles-and-gem-contents.md +12 -0
- data/TODO.restructure/19-gitignore-hygiene.md +10 -0
- data/TODO.restructure/20-utf8proc-enablement.md +34 -0
- data/TODO.restructure/21-typeddata-variant-prototype.md +42 -0
- data/TODO.restructure/22-sax-drain-bulk-strip.md +28 -0
- data/docs/adr/0001-lockstep-mirror.md +17 -0
- data/docs/adr/0002-utf8-at-the-seam.md +19 -0
- data/docs/adr/0003-readonly-memoization-pattern.md +34 -0
- data/docs/adr/0004-lifetime-guard.md +21 -0
- data/docs/adr/0005-autoload-manifest-ordering.md +19 -0
- data/docs/adr/0006-ruby-variant-policy.md +25 -0
- data/ext/build_windows_native.rb +42 -0
- data/ext/leptris/native/extconf.rb +55 -0
- data/ext/leptris/native/native.c +2646 -0
- data/leptris.gemspec +43 -0
- data/lib/leptris/version.rb +5 -0
- data/lib/leptris/xml/attr.rb +76 -0
- data/lib/leptris/xml/c_string_array.rb +37 -0
- data/lib/leptris/xml/cdata.rb +27 -0
- data/lib/leptris/xml/comment.rb +27 -0
- data/lib/leptris/xml/css_to_xpath.rb +192 -0
- data/lib/leptris/xml/descriptor.rb +214 -0
- data/lib/leptris/xml/diff.rb +74 -0
- data/lib/leptris/xml/doc_type.rb +54 -0
- data/lib/leptris/xml/document.rb +716 -0
- data/lib/leptris/xml/document_fragment.rb +63 -0
- data/lib/leptris/xml/element.rb +771 -0
- data/lib/leptris/xml/entity_reference.rb +13 -0
- data/lib/leptris/xml/evaluation_context.rb +32 -0
- data/lib/leptris/xml/ffi.rb +1527 -0
- data/lib/leptris/xml/iteration_scope.rb +56 -0
- data/lib/leptris/xml/iterparse.rb +144 -0
- data/lib/leptris/xml/namespace.rb +43 -0
- data/lib/leptris/xml/native.so +0 -0
- data/lib/leptris/xml/native_layer.rb +92 -0
- data/lib/leptris/xml/node.rb +686 -0
- data/lib/leptris/xml/node_set.rb +212 -0
- data/lib/leptris/xml/parse_options.rb +97 -0
- data/lib/leptris/xml/plan_value.rb +121 -0
- data/lib/leptris/xml/processing_instruction.rb +90 -0
- data/lib/leptris/xml/pull.rb +212 -0
- data/lib/leptris/xml/relaxng.rb +126 -0
- data/lib/leptris/xml/result_attr.rb +39 -0
- data/lib/leptris/xml/result_text.rb +36 -0
- data/lib/leptris/xml/sax/document.rb +45 -0
- data/lib/leptris/xml/sax/dom_dispatch.rb +144 -0
- data/lib/leptris/xml/sax/parser.rb +326 -0
- data/lib/leptris/xml/sax/recorder.rb +374 -0
- data/lib/leptris/xml/sax.rb +14 -0
- data/lib/leptris/xml/schematron.rb +88 -0
- data/lib/leptris/xml/searchable.rb +361 -0
- data/lib/leptris/xml/serialization.rb +180 -0
- data/lib/leptris/xml/text.rb +32 -0
- data/lib/leptris/xml/xpath.rb +89 -0
- data/lib/leptris/xml/xquery.rb +77 -0
- data/lib/leptris/xml/xslt.rb +85 -0
- data/lib/leptris/xml.rb +108 -0
- data/lib/leptris.rb +53 -0
- data/lib/libleptris.so +0 -0
- data/lib/libutf8proc.so.3 +0 -0
- data/scripts/gem_smoke.rb +36 -0
- metadata +214 -0
|
@@ -0,0 +1,771 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Leptris::XML::Element < Leptris::XML::Node
|
|
4
|
+
# TODO.perf/01: with the native bundle loaded, hot reads go
|
|
5
|
+
# through the ext (one C-API dispatch + rb_str_new_cstr — no FFI
|
|
6
|
+
# marshaling; ~3x per read). defined? is near-free when absent.
|
|
7
|
+
# Scope-owned (iterparse-yield) elements stay on the FFI path:
|
|
8
|
+
# their pool addresses recycle across yields and their lifetime
|
|
9
|
+
# rules ride the IterationScope seam — the ext fast path is for
|
|
10
|
+
# document-owned trees.
|
|
11
|
+
def native_fast?
|
|
12
|
+
@native_fast
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def name
|
|
16
|
+
return @name if @name
|
|
17
|
+
ensure_alive!
|
|
18
|
+
@name = if @addr_reads_fast
|
|
19
|
+
Leptris::XML::Native.fast_name(@c_address)
|
|
20
|
+
else
|
|
21
|
+
Leptris::XML::FFI.leptris_element_name(c_ptr)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
alias_method :node_name, :name
|
|
25
|
+
|
|
26
|
+
def name=(new_name)
|
|
27
|
+
if @native_fast
|
|
28
|
+
Leptris::XML::Native.set_binding_name(
|
|
29
|
+
@document, @c_address, new_name)
|
|
30
|
+
return @name = new_name
|
|
31
|
+
end
|
|
32
|
+
ensure_writable!
|
|
33
|
+
Leptris::XML::FFI.check_status(
|
|
34
|
+
Leptris::XML::FFI.leptris_element_set_name(c_ptr, new_name))
|
|
35
|
+
@name = new_name
|
|
36
|
+
end
|
|
37
|
+
alias_method :node_name=, :name=
|
|
38
|
+
|
|
39
|
+
def content
|
|
40
|
+
return @content if memo_hit?(@content_version)
|
|
41
|
+
ensure_alive!
|
|
42
|
+
result = if @addr_reads_fast
|
|
43
|
+
Leptris::XML::Native.fast_element_text(@c_address)
|
|
44
|
+
else
|
|
45
|
+
Leptris::XML::FFI.leptris_element_text(c_ptr)
|
|
46
|
+
end
|
|
47
|
+
if @document
|
|
48
|
+
@content = result
|
|
49
|
+
@content_version = @document.version
|
|
50
|
+
end
|
|
51
|
+
result
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def content=(new_content)
|
|
55
|
+
if @native_fast
|
|
56
|
+
Leptris::XML::Native.set_binding_text(
|
|
57
|
+
@document, @c_address, new_content.to_s)
|
|
58
|
+
@content = new_content.to_s
|
|
59
|
+
@content_version = @document.version if @document
|
|
60
|
+
return new_content
|
|
61
|
+
end
|
|
62
|
+
ensure_writable!
|
|
63
|
+
Leptris::XML::FFI.check_status(
|
|
64
|
+
Leptris::XML::FFI.leptris_element_set_text(c_ptr, new_content.to_s))
|
|
65
|
+
@content = new_content.to_s
|
|
66
|
+
@content_version = @document.version if @document
|
|
67
|
+
new_content
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def [](key)
|
|
71
|
+
# BARE names serve from the versioned attributes hash (written
|
|
72
|
+
# name == the only legal key for a no-namespace attribute);
|
|
73
|
+
# materializes on demand, invalidates through the mutation gate
|
|
74
|
+
# (ADR 0003's 1.9.14 extension); a hash read cannot
|
|
75
|
+
# use-after-free. QUALIFIED names (with a colon) go to the
|
|
76
|
+
# engine: they resolve through in-scope declarations, where the
|
|
77
|
+
# written prefix never matters — the hash cannot answer them.
|
|
78
|
+
#
|
|
79
|
+
# Cold single-attribute reads (ruby#150): the previous path
|
|
80
|
+
# materialised EVERY attribute into two hashes on the first
|
|
81
|
+
# bare read (~17 allocs on a 2-attr element). Now a cold miss
|
|
82
|
+
# pays ONE FFI call and fills only that name; subsequent
|
|
83
|
+
# same-name reads hit the hash; a later attributes/keys call
|
|
84
|
+
# rebuilds the full face. Full-memo (@attributes set) still
|
|
85
|
+
# answers misses as nil without another FFI.
|
|
86
|
+
name = key.to_s
|
|
87
|
+
if @document && !name.include?(":")
|
|
88
|
+
values = @attr_values
|
|
89
|
+
if values && @attributes_version == @document.version
|
|
90
|
+
v = values[name]
|
|
91
|
+
# Present, or a FULL-memo miss (absent). A partial-memo nil
|
|
92
|
+
# is ambiguous (un-queried vs absent) — one idempotent
|
|
93
|
+
# engine call resolves it, then the name is cached.
|
|
94
|
+
return v if !v.nil? || @attributes
|
|
95
|
+
ensure_alive!
|
|
96
|
+
v = if @addr_reads_fast
|
|
97
|
+
Leptris::XML::Native.fast_attribute(@c_address, name)
|
|
98
|
+
else
|
|
99
|
+
Leptris::XML::FFI.leptris_element_attribute(c_ptr, name)
|
|
100
|
+
end
|
|
101
|
+
values[name] = v
|
|
102
|
+
return v
|
|
103
|
+
end
|
|
104
|
+
# Completely cold: one read, start a partial values hash.
|
|
105
|
+
# Drop any full-face memos from a prior version — otherwise a
|
|
106
|
+
# post-mutation cold [] would stamp the new version onto a
|
|
107
|
+
# stale @attributes hash and attributes would serve it.
|
|
108
|
+
ensure_alive!
|
|
109
|
+
v = if @addr_reads_fast
|
|
110
|
+
Leptris::XML::Native.fast_attribute(@c_address, name)
|
|
111
|
+
else
|
|
112
|
+
Leptris::XML::FFI.leptris_element_attribute(c_ptr, name)
|
|
113
|
+
end
|
|
114
|
+
@attr_values = { name => v }
|
|
115
|
+
@attributes = nil
|
|
116
|
+
@attribute_nodes = nil
|
|
117
|
+
@keys = nil
|
|
118
|
+
@values = nil
|
|
119
|
+
@attributes_version = @document.version
|
|
120
|
+
return v
|
|
121
|
+
end
|
|
122
|
+
ensure_alive!
|
|
123
|
+
v = Leptris::XML::FFI.leptris_element_attribute(c_ptr, name)
|
|
124
|
+
# Namespace-aware misses (an undeclared prefix never resolves
|
|
125
|
+
# through in-scope declarations) fall back to the WRITTEN name
|
|
126
|
+
# — the same flat face #attributes exposes, so a value written
|
|
127
|
+
# as el["n:x"] reads back through el["n:x"] (#161).
|
|
128
|
+
return v unless v.nil?
|
|
129
|
+
attributes[name]&.value
|
|
130
|
+
end
|
|
131
|
+
alias_method :attr, :[]
|
|
132
|
+
alias_method :get_attribute, :[]
|
|
133
|
+
|
|
134
|
+
def []=(key, value)
|
|
135
|
+
# C-bound write (TODO.perf/11): gates + version bump + engine
|
|
136
|
+
# set in one dispatch; the bump drops the version-stamped
|
|
137
|
+
# attribute memos on both surfaces.
|
|
138
|
+
if native_fast_children?
|
|
139
|
+
Leptris::XML::Native.set_binding_attribute(
|
|
140
|
+
@document, @c_address, key.to_s, value.to_s)
|
|
141
|
+
seed_attribute_memo(key.to_s, value.to_s)
|
|
142
|
+
return value
|
|
143
|
+
end
|
|
144
|
+
ensure_writable!
|
|
145
|
+
Leptris::XML::FFI.check_status(
|
|
146
|
+
Leptris::XML::FFI.leptris_element_set_attribute(c_ptr, key.to_s, value.to_s))
|
|
147
|
+
seed_attribute_memo(key.to_s, value.to_s)
|
|
148
|
+
value
|
|
149
|
+
end
|
|
150
|
+
alias_method :set_attribute, :[]=
|
|
151
|
+
|
|
152
|
+
# TODO.perf/26: the write knows the new value — seed the
|
|
153
|
+
# version-stamped memo so a read-back skips re-derivation. A
|
|
154
|
+
# valid-and-just-invalidated partial memo extends in place; a
|
|
155
|
+
# full face (or anything older) restarts partial, mirroring the
|
|
156
|
+
# cold-[] rules.
|
|
157
|
+
def seed_attribute_memo(name, written)
|
|
158
|
+
return unless @document
|
|
159
|
+
values = @attr_values
|
|
160
|
+
if values && !@attributes &&
|
|
161
|
+
@attributes_version == @document.version - 1
|
|
162
|
+
values[name] = written
|
|
163
|
+
else
|
|
164
|
+
@attr_values = { name => written }
|
|
165
|
+
@attributes = nil
|
|
166
|
+
@attribute_nodes = nil
|
|
167
|
+
@keys = nil
|
|
168
|
+
@values = nil
|
|
169
|
+
end
|
|
170
|
+
@attributes_version = @document.version
|
|
171
|
+
end
|
|
172
|
+
private :seed_attribute_memo
|
|
173
|
+
|
|
174
|
+
def key?(name)
|
|
175
|
+
ensure_alive!
|
|
176
|
+
# Versioned attribute memo first (TODO.perf/21): a hit proves
|
|
177
|
+
# presence without the engine round-trip; the memo cannot
|
|
178
|
+
# prove absence for a partial fill, so misses fall through.
|
|
179
|
+
n = name.to_s
|
|
180
|
+
values = @attr_values
|
|
181
|
+
if @document && values && @attributes_version == @document.version &&
|
|
182
|
+
values.key?(n)
|
|
183
|
+
return true
|
|
184
|
+
end
|
|
185
|
+
return true if Leptris::XML::FFI.leptris_element_has_attribute(
|
|
186
|
+
c_ptr, n) != 0
|
|
187
|
+
# Same written-name fallback as #[] — the engine lookup is
|
|
188
|
+
# namespace-aware and misses undeclared prefixes (#161).
|
|
189
|
+
attributes.key?(n)
|
|
190
|
+
end
|
|
191
|
+
alias_method :has_attribute?, :key?
|
|
192
|
+
|
|
193
|
+
# Expanded-name attribute access (libleptris 1.8.0): URI + local
|
|
194
|
+
# name with XML Namespaces 1.0 semantics — the written prefix
|
|
195
|
+
# never matters, only what it resolves to through this element's
|
|
196
|
+
# in-scope declarations. uri nil/"" matches no-namespace
|
|
197
|
+
# attributes only; xmlns declarations are invisible.
|
|
198
|
+
def attribute_ns(uri, local)
|
|
199
|
+
ensure_alive!
|
|
200
|
+
Leptris::XML::FFI.leptris_element_attribute_ns(
|
|
201
|
+
c_ptr, uri&.to_s, local.to_s)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def has_attribute_ns?(uri, local)
|
|
205
|
+
ensure_alive!
|
|
206
|
+
Leptris::XML::FFI.leptris_element_has_attribute_ns(
|
|
207
|
+
c_ptr, uri&.to_s, local.to_s) != 0
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def remove_attribute(name)
|
|
211
|
+
ensure_writable!
|
|
212
|
+
Leptris::XML::FFI.check_status(
|
|
213
|
+
Leptris::XML::FFI.leptris_element_remove_attribute(c_ptr, name.to_s))
|
|
214
|
+
self
|
|
215
|
+
end
|
|
216
|
+
alias_method :delete, :remove_attribute
|
|
217
|
+
|
|
218
|
+
# Iterates the element's attributes via the v1.1.0 linked-list face
|
|
219
|
+
# (one FFI call per attribute; the name_at/value_at indexing API
|
|
220
|
+
# re-walks the list per index, making it O(n^2) per element).
|
|
221
|
+
# The C attribute handle rides along so Attr can serve the
|
|
222
|
+
# per-attribute namespace accessors (libleptris 1.8.0).
|
|
223
|
+
def each_attribute
|
|
224
|
+
return enum_for(:each_attribute) unless block_given?
|
|
225
|
+
ensure_alive!
|
|
226
|
+
attr = Leptris::XML::FFI.leptris_element_first_attribute(c_ptr)
|
|
227
|
+
until attr.nil? || attr.null?
|
|
228
|
+
name = Leptris::XML::FFI.leptris_attribute_get_name(attr)
|
|
229
|
+
value = Leptris::XML::FFI.leptris_attribute_get_value(c_ptr, attr)
|
|
230
|
+
yield Leptris::XML::Attr.new(name, value, self, c_handle: attr)
|
|
231
|
+
attr = Leptris::XML::FFI.leptris_attribute_next(attr)
|
|
232
|
+
end
|
|
233
|
+
self
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def keys
|
|
237
|
+
return @keys if memo_hit?(@keys_version)
|
|
238
|
+
result = each_attribute.to_a.map(&:name)
|
|
239
|
+
if @document
|
|
240
|
+
@keys = result
|
|
241
|
+
@keys_version = @document.version
|
|
242
|
+
end
|
|
243
|
+
result
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def values
|
|
247
|
+
return @values if memo_hit?(@values_version)
|
|
248
|
+
result = each_attribute.to_a.map(&:value)
|
|
249
|
+
if @document
|
|
250
|
+
@values = result
|
|
251
|
+
@values_version = @document.version
|
|
252
|
+
end
|
|
253
|
+
result
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def attributes
|
|
257
|
+
# Require the full Attr-hash face — a cold [] may have set the
|
|
258
|
+
# version with only a partial @attr_values (ruby#150).
|
|
259
|
+
return @attributes if @attributes && memo_hit?(@attributes_version)
|
|
260
|
+
# TODO.perf/28: the bulk faces touch no document state —
|
|
261
|
+
# scope-eligible via @addr_reads_fast.
|
|
262
|
+
if @addr_reads_fast
|
|
263
|
+
# TODO.perf/10: both memo faces in one C walk.
|
|
264
|
+
result, values = Leptris::XML::Native.bulk_attr_faces(
|
|
265
|
+
@c_address, self)
|
|
266
|
+
if @document
|
|
267
|
+
@attributes = result
|
|
268
|
+
@attr_values = values
|
|
269
|
+
@attributes_version = @document.version
|
|
270
|
+
end
|
|
271
|
+
return result
|
|
272
|
+
end
|
|
273
|
+
result = {}
|
|
274
|
+
values = {}
|
|
275
|
+
each_attribute do |attr|
|
|
276
|
+
result[attr.name] = attr
|
|
277
|
+
values[attr.name] = attr.value
|
|
278
|
+
end
|
|
279
|
+
if @document
|
|
280
|
+
@attributes = result
|
|
281
|
+
@attr_values = values
|
|
282
|
+
@attributes_version = @document.version
|
|
283
|
+
end
|
|
284
|
+
result
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def attribute_nodes
|
|
288
|
+
return @attribute_nodes if memo_hit?(@attribute_nodes_version)
|
|
289
|
+
result = each_attribute.to_a
|
|
290
|
+
if @document
|
|
291
|
+
@attribute_nodes = result
|
|
292
|
+
@attribute_nodes_version = @document.version
|
|
293
|
+
end
|
|
294
|
+
result
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# The element's own namespace prefix (e.g. "foo" for <foo:child/>),
|
|
298
|
+
# or nil when the element has none.
|
|
299
|
+
def prefix
|
|
300
|
+
ensure_alive!
|
|
301
|
+
if native_fast?
|
|
302
|
+
Leptris::XML::Native.fast_prefix(@c_address)
|
|
303
|
+
else
|
|
304
|
+
result = Leptris::XML::FFI.leptris_element_prefix(c_ptr)
|
|
305
|
+
result if result && !result.empty?
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# The written qualified name — "foo:child" with a prefix, the
|
|
310
|
+
# bare local name without. The explicit contract behind the
|
|
311
|
+
# #name/#prefix split: #name answers the local part, the
|
|
312
|
+
# serialized form uses this spelling (#161).
|
|
313
|
+
def qualified_name
|
|
314
|
+
p = prefix
|
|
315
|
+
p ? "#{p}:#{name}" : name
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# Cross-document and cross-scope adoption (#178 ask 1): the
|
|
319
|
+
# engine's attach entries move a subtree but do not lift the
|
|
320
|
+
# source's in-scope namespace declarations, so serialized output
|
|
321
|
+
# can carry undeclared prefixes. Re-declares on +node+ everything
|
|
322
|
+
# from its in-scope set that +target_scope+ (the attach target's
|
|
323
|
+
# in-scope map) does not already resolve identically — the
|
|
324
|
+
# declarations ride the moved element, staying local to the
|
|
325
|
+
# adopted subtree. Call BEFORE attaching: the source scope is
|
|
326
|
+
# read through the node's current ancestors. Identical scopes
|
|
327
|
+
# (the common same-document move) add nothing.
|
|
328
|
+
# TODO.perf/09: one dispatch answers whether ANY lift work can
|
|
329
|
+
# apply (no resolved namespace, no own declarations = provable
|
|
330
|
+
# no-op — the common programmatic-build shape). Checked at the
|
|
331
|
+
# mutation sites so the target's in-scope namespaces are never
|
|
332
|
+
# materialized when the answer is no.
|
|
333
|
+
def self.skip_adoption_lift?(node)
|
|
334
|
+
# Non-elements can never need a lift (lift's own first line
|
|
335
|
+
# no-ops them) — skipping also avoids materializing the
|
|
336
|
+
# target's namespaces for the text/comment children every
|
|
337
|
+
# build appends.
|
|
338
|
+
return true unless node.is_a?(Leptris::XML::Element)
|
|
339
|
+
return false unless defined?(Leptris::XML::NATIVE_FAST)
|
|
340
|
+
!Leptris::XML::Native.ns_lift_needed?(node.c_ptr.address)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def self.lift_namespaces_for_adoption(node, target_scope)
|
|
344
|
+
return unless node.is_a?(Leptris::XML::Element)
|
|
345
|
+
# Declarations the node already carries (its own definitions —
|
|
346
|
+
# preserved by the C copy and by moves) ride it as-is; lifting
|
|
347
|
+
# must not duplicate them, only what came from ANCESTORS.
|
|
348
|
+
own = node.namespace_definitions.each_with_object({}) do |ns, h|
|
|
349
|
+
h[ns.prefix ? "xmlns:#{ns.prefix}" : "xmlns"] = ns.href
|
|
350
|
+
end
|
|
351
|
+
node.namespaces.each do |key, uri|
|
|
352
|
+
next if target_scope[key] == uri || own[key] == uri
|
|
353
|
+
prefix = key.start_with?("xmlns:") ? key.delete_prefix("xmlns:") : nil
|
|
354
|
+
node.add_namespace_definition(prefix, uri)
|
|
355
|
+
end
|
|
356
|
+
# libxml2 reparent parity (moxml #208 lineage): an OWN
|
|
357
|
+
# declaration the attach target already resolves identically
|
|
358
|
+
# is redundant — prune it so serialized output does not
|
|
359
|
+
# re-declare (libxml2 drops these on reparent). Shadowing
|
|
360
|
+
# declarations (same prefix, different URI) are preserved.
|
|
361
|
+
node.namespace_definitions.each do |ns|
|
|
362
|
+
key = ns.prefix ? "xmlns:#{ns.prefix}" : "xmlns"
|
|
363
|
+
next unless target_scope[key] == ns.href
|
|
364
|
+
node.remove_namespace_definition(ns.prefix)
|
|
365
|
+
end
|
|
366
|
+
nil
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def prepend_child(node)
|
|
370
|
+
# C-bound insert (TODO.perf/14): gates + predicate + version
|
|
371
|
+
# bump + engine insert in one dispatch; Qnil = the child needs
|
|
372
|
+
# the namespace lift — the full path below handles it.
|
|
373
|
+
if native_fast_children?
|
|
374
|
+
unless Leptris::XML::Native.insert_binding_child(
|
|
375
|
+
@document, @c_address, node.c_ptr.address, 1).nil?
|
|
376
|
+
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
377
|
+
return node
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
ensure_writable!
|
|
381
|
+
unless Leptris::XML::Element.skip_adoption_lift?(node)
|
|
382
|
+
Leptris::XML::Element.lift_namespaces_for_adoption(node, namespaces)
|
|
383
|
+
end
|
|
384
|
+
Leptris::XML::FFI.check_status(
|
|
385
|
+
Leptris::XML::FFI.leptris_element_prepend_child(c_ptr, node.c_ptr))
|
|
386
|
+
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
387
|
+
node
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def add_next_sibling(node)
|
|
391
|
+
# C-bound insert (TODO.perf/14): gates + predicate + version
|
|
392
|
+
# bump + engine insert in one dispatch; Qnil = the child needs
|
|
393
|
+
# the namespace lift — the full path below handles it.
|
|
394
|
+
if native_fast_children?
|
|
395
|
+
unless Leptris::XML::Native.insert_binding_child(
|
|
396
|
+
@document, @c_address, node.c_ptr.address, 2).nil?
|
|
397
|
+
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
398
|
+
return node
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
ensure_writable!
|
|
402
|
+
unless Leptris::XML::Element.skip_adoption_lift?(node)
|
|
403
|
+
Leptris::XML::Element.lift_namespaces_for_adoption(node, namespaces)
|
|
404
|
+
end
|
|
405
|
+
Leptris::XML::FFI.check_status(
|
|
406
|
+
Leptris::XML::FFI.leptris_element_insert_after(c_ptr, node.c_ptr))
|
|
407
|
+
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
408
|
+
node
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def add_previous_sibling(node)
|
|
412
|
+
# C-bound insert (TODO.perf/14): gates + predicate + version
|
|
413
|
+
# bump + engine insert in one dispatch; Qnil = the child needs
|
|
414
|
+
# the namespace lift — the full path below handles it.
|
|
415
|
+
if native_fast_children?
|
|
416
|
+
unless Leptris::XML::Native.insert_binding_child(
|
|
417
|
+
@document, @c_address, node.c_ptr.address, 3).nil?
|
|
418
|
+
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
419
|
+
return node
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
ensure_writable!
|
|
423
|
+
unless Leptris::XML::Element.skip_adoption_lift?(node)
|
|
424
|
+
Leptris::XML::Element.lift_namespaces_for_adoption(node, namespaces)
|
|
425
|
+
end
|
|
426
|
+
Leptris::XML::FFI.check_status(
|
|
427
|
+
Leptris::XML::FFI.leptris_element_insert_before(c_ptr, node.c_ptr))
|
|
428
|
+
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
429
|
+
node
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def remove_child(node)
|
|
433
|
+
ensure_writable!
|
|
434
|
+
Leptris::XML::FFI.check_status(
|
|
435
|
+
Leptris::XML::FFI.leptris_element_remove_child(c_ptr, node.c_ptr))
|
|
436
|
+
node
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
def children=(node_or_nodes)
|
|
440
|
+
ensure_writable!
|
|
441
|
+
# Remove existing children, then attach the new ones in source order.
|
|
442
|
+
Leptris::XML::FFI.check_status(
|
|
443
|
+
Leptris::XML::FFI.leptris_element_remove_children(c_ptr))
|
|
444
|
+
Array(node_or_nodes).each { |n| add_child(n) }
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
# Replace this element with +new_node+ in the parent's child list.
|
|
448
|
+
# +new_node+ must belong to the same document. Returns +new_node+.
|
|
449
|
+
def replace(new_node)
|
|
450
|
+
parent = self.parent
|
|
451
|
+
raise Leptris::XML::Error, "cannot replace a node with no parent" unless parent
|
|
452
|
+
add_next_sibling(new_node)
|
|
453
|
+
parent.remove_child(self)
|
|
454
|
+
new_node
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
# Like #replace but returns self for chaining.
|
|
458
|
+
def swap(new_node)
|
|
459
|
+
replace(new_node)
|
|
460
|
+
self
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
# Wrap this element in a new element parsed from +markup+ or a dup of
|
|
464
|
+
# +node+. The wrapper takes this element's place in the tree, and this
|
|
465
|
+
# element becomes its only child. Returns self for chaining.
|
|
466
|
+
def wrap(node_or_markup)
|
|
467
|
+
wrapper =
|
|
468
|
+
case node_or_markup
|
|
469
|
+
when Leptris::XML::Element then node_or_markup.dup
|
|
470
|
+
when String
|
|
471
|
+
frag_doc = Leptris::XML::Document.parse(node_or_markup)
|
|
472
|
+
frag_doc.root or raise Leptris::XML::Error, "wrap markup has no root element"
|
|
473
|
+
else
|
|
474
|
+
raise ArgumentError, "wrap expects a String or Element, got #{node_or_markup.class}"
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
parent = self.parent
|
|
478
|
+
raise Leptris::XML::Error, "cannot wrap a node with no parent" unless parent
|
|
479
|
+
|
|
480
|
+
# Insert wrapper at self's position, then move self into wrapper.
|
|
481
|
+
# add_child moves self (unlinks from old parent first), so no explicit
|
|
482
|
+
# remove_child needed — and trying to remove after the move corrupts
|
|
483
|
+
# the C tree (libleptris silently handles non-child args badly).
|
|
484
|
+
add_next_sibling(wrapper)
|
|
485
|
+
wrapper.add_child(self)
|
|
486
|
+
self
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
# Deep copy in a NEW document via Document.copy_of (the single
|
|
490
|
+
# copy seam — every child kind and namespace survives,
|
|
491
|
+
# #696/#721/#812). TODO.perf/30: one C dispatch when the ext
|
|
492
|
+
# is loaded — the namespace-lift decision stays here.
|
|
493
|
+
def dup
|
|
494
|
+
ensure_alive!
|
|
495
|
+
if @addr_reads_fast
|
|
496
|
+
doc = Leptris::XML::Native.copy_binding_element(
|
|
497
|
+
@document, @c_address)
|
|
498
|
+
unless doc.nil?
|
|
499
|
+
copied = doc.root
|
|
500
|
+
unless Leptris::XML::Element.skip_adoption_lift?(copied)
|
|
501
|
+
Leptris::XML::Element.lift_namespaces_for_adoption(copied, {})
|
|
502
|
+
end
|
|
503
|
+
return copied
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
Leptris::XML::Document.copy_of(self)
|
|
507
|
+
end
|
|
508
|
+
alias_method :clone, :dup
|
|
509
|
+
|
|
510
|
+
# Fused create+append (libleptris 1.9.153): builds a child
|
|
511
|
+
# element of +name+ in this element's document and attaches it
|
|
512
|
+
# in ONE C call — the builder-shape single call
|
|
513
|
+
# (leptris_element_create_child). Tree semantics identical to
|
|
514
|
+
# document.create_element(name) + add_child.
|
|
515
|
+
def create_child(name)
|
|
516
|
+
ensure_writable!
|
|
517
|
+
ptr = Leptris::XML::FFI.leptris_element_create_child(c_ptr, name.to_s)
|
|
518
|
+
raise Leptris::XML::Error,
|
|
519
|
+
"leptris_element_create_child failed for #{name.inspect}" if ptr.null?
|
|
520
|
+
Leptris::XML::Node.wrap_fresh(ptr, @document, Leptris::XML::FFI::NODE_ELEMENT)
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
def add_child(node_or_markup)
|
|
524
|
+
ensure_writable!
|
|
525
|
+
case node_or_markup
|
|
526
|
+
when Leptris::XML::Node
|
|
527
|
+
# C-bound append (TODO.perf/08-09): one dispatch runs the
|
|
528
|
+
# readonly/liveness gates, the provable no-op lift
|
|
529
|
+
# predicate, the version bump, and the engine append. Qnil
|
|
530
|
+
# means the child needs the namespace lift — fall through
|
|
531
|
+
# to the full path.
|
|
532
|
+
if native_fast_children?
|
|
533
|
+
# The face raises on failure (TODO.perf/36); Qnil = the
|
|
534
|
+
# child needs the lift — the full path handles it.
|
|
535
|
+
unless Leptris::XML::Native.append_binding_child(
|
|
536
|
+
@document, @c_address, node_or_markup.c_ptr.address).nil?
|
|
537
|
+
Leptris::XML::Node.invalidate_cross_document!(node_or_markup, @document)
|
|
538
|
+
return node_or_markup
|
|
539
|
+
end
|
|
540
|
+
end
|
|
541
|
+
unless Leptris::XML::Element.skip_adoption_lift?(node_or_markup)
|
|
542
|
+
Leptris::XML::Element.lift_namespaces_for_adoption(node_or_markup, namespaces)
|
|
543
|
+
end
|
|
544
|
+
Leptris::XML::FFI.check_status(
|
|
545
|
+
Leptris::XML::FFI.leptris_element_append_child(c_ptr, node_or_markup.c_ptr))
|
|
546
|
+
Leptris::XML::Node.invalidate_cross_document!(node_or_markup, @document)
|
|
547
|
+
node_or_markup
|
|
548
|
+
when String
|
|
549
|
+
# TODO.perf/34: one dispatch parses the markup and appends
|
|
550
|
+
# every fragment child (single gate + bump). Negative
|
|
551
|
+
# returns: -1 re-runs the FFI path for the exact parse
|
|
552
|
+
# error; -1000-st routes through check_status.
|
|
553
|
+
if native_fast_children?
|
|
554
|
+
r = Leptris::XML::Native.append_markup(
|
|
555
|
+
@document, @c_address, node_or_markup)
|
|
556
|
+
if r != -1
|
|
557
|
+
# Appends raise in C on status failures (TODO.perf/36);
|
|
558
|
+
# -1 alone means the parse failed.
|
|
559
|
+
return Leptris::XML::NodeSet.new(@document, []) if r.zero?
|
|
560
|
+
return Leptris::XML::NodeSet.new(@document, last_added(r))
|
|
561
|
+
end
|
|
562
|
+
# parse failure: reproduce the exact error via the legacy
|
|
563
|
+
# path (it re-fails fast)
|
|
564
|
+
Leptris::XML::DocumentFragment.parse(node_or_markup, @document)
|
|
565
|
+
end
|
|
566
|
+
frag = Leptris::XML::DocumentFragment.parse(node_or_markup, @document)
|
|
567
|
+
added = []
|
|
568
|
+
frag.children.each do |n|
|
|
569
|
+
Leptris::XML::FFI.check_status(
|
|
570
|
+
Leptris::XML::FFI.leptris_element_append_child(c_ptr, n.c_ptr))
|
|
571
|
+
added << n
|
|
572
|
+
end
|
|
573
|
+
Leptris::XML::NodeSet.new(@document, added)
|
|
574
|
+
else
|
|
575
|
+
raise ArgumentError, "add_child expects a Node or String, got #{node_or_markup.class}"
|
|
576
|
+
end
|
|
577
|
+
end
|
|
578
|
+
# The markup-append face appends in fragment order; the added
|
|
579
|
+
# nodes are this element's last +count+ children.
|
|
580
|
+
def last_added(count)
|
|
581
|
+
kids = children.to_a
|
|
582
|
+
kids.last(count)
|
|
583
|
+
end
|
|
584
|
+
private :last_added
|
|
585
|
+
|
|
586
|
+
alias_method :<<, :add_child
|
|
587
|
+
|
|
588
|
+
def namespace
|
|
589
|
+
return @namespace if memo_hit?(@namespace_version)
|
|
590
|
+
ensure_alive!
|
|
591
|
+
uri = Leptris::XML::FFI.leptris_element_namespace(c_ptr)
|
|
592
|
+
result =
|
|
593
|
+
if uri.nil? || uri.empty?
|
|
594
|
+
nil
|
|
595
|
+
else
|
|
596
|
+
# The resolved namespace is reached via this element's own
|
|
597
|
+
# prefix, so carry it through: consumers that distinguish
|
|
598
|
+
# {"p" => "urn:p"} from the default {"nil => "urn:p"} need it.
|
|
599
|
+
prefix = Leptris::XML::FFI.leptris_element_prefix(c_ptr)
|
|
600
|
+
prefix = nil if prefix.nil? || prefix.empty?
|
|
601
|
+
Leptris::XML::Namespace.new(self, uri, prefix: prefix)
|
|
602
|
+
end
|
|
603
|
+
if @document
|
|
604
|
+
@namespace = result
|
|
605
|
+
@namespace_version = @document.version
|
|
606
|
+
end
|
|
607
|
+
result
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
# Nokogiri node.namespace= semantics (libleptris 1.9.76, #817):
|
|
611
|
+
# nil (or "") detaches the namespace — the resolved link and the
|
|
612
|
+
# name's prefix clear, and an xmlns="" undeclaration blocks
|
|
613
|
+
# in-scope defaults from capturing the unqualified name. A URI
|
|
614
|
+
# rebinds to an in-scope declaration carrying it (adopting its
|
|
615
|
+
# prefix); a URI with no in-scope declaration raises — declare
|
|
616
|
+
# it first with #add_namespace_definition.
|
|
617
|
+
def namespace=(uri)
|
|
618
|
+
ensure_writable!
|
|
619
|
+
Leptris::XML::FFI.check_status(
|
|
620
|
+
Leptris::XML::FFI.leptris_element_set_namespace(c_ptr, uri&.to_s))
|
|
621
|
+
uri
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
# The local name, prefix, and resolved namespace URI in ONE
|
|
625
|
+
# call (libleptris 1.9.144) — the adapter fan-out read.
|
|
626
|
+
# Equivalent to #name + #prefix + #namespace, single entry.
|
|
627
|
+
def expanded_name
|
|
628
|
+
ensure_alive!
|
|
629
|
+
local = ::FFI::MemoryPointer.new(:pointer)
|
|
630
|
+
prefix = ::FFI::MemoryPointer.new(:pointer)
|
|
631
|
+
uri = ::FFI::MemoryPointer.new(:pointer)
|
|
632
|
+
Leptris::XML::FFI.leptris_element_expanded_name(
|
|
633
|
+
c_ptr, local, prefix, uri)
|
|
634
|
+
local_ptr = local.read_pointer
|
|
635
|
+
prefix_ptr = prefix.read_pointer
|
|
636
|
+
uri_ptr = uri.read_pointer
|
|
637
|
+
result = {
|
|
638
|
+
local: local_ptr.read_string,
|
|
639
|
+
prefix: prefix_ptr.null? ? nil : prefix_ptr.read_string,
|
|
640
|
+
namespace_uri: uri_ptr.null? ? nil : uri_ptr.read_string,
|
|
641
|
+
}
|
|
642
|
+
local.free
|
|
643
|
+
prefix.free
|
|
644
|
+
uri.free
|
|
645
|
+
result
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
def namespace_definitions
|
|
649
|
+
return @namespace_definitions if memo_hit?(@namespace_definitions_version)
|
|
650
|
+
ensure_alive!
|
|
651
|
+
count = Leptris::XML::FFI.leptris_element_namespace_count(c_ptr)
|
|
652
|
+
result = count.times.map do |i|
|
|
653
|
+
prefix = Leptris::XML::FFI.leptris_element_namespace_decl_prefix(c_ptr, i)
|
|
654
|
+
uri = Leptris::XML::FFI.leptris_element_namespace_decl_uri(c_ptr, i)
|
|
655
|
+
Leptris::XML::Namespace.new(self, uri, prefix: prefix)
|
|
656
|
+
end
|
|
657
|
+
if @document
|
|
658
|
+
@namespace_definitions = result
|
|
659
|
+
@namespace_definitions_version = @document.version
|
|
660
|
+
end
|
|
661
|
+
result
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
def namespaces
|
|
665
|
+
return @namespaces if memo_hit?(@namespaces_version)
|
|
666
|
+
scopes = {}
|
|
667
|
+
node = self
|
|
668
|
+
while node.is_a?(Leptris::XML::Element)
|
|
669
|
+
node.namespace_definitions.each do |ns|
|
|
670
|
+
key = ns.prefix ? "xmlns:#{ns.prefix}" : "xmlns"
|
|
671
|
+
scopes[key] ||= ns.href
|
|
672
|
+
end
|
|
673
|
+
# unstamped: this walk also runs inside the adoption lift,
|
|
674
|
+
# between the version bump and the engine move (see
|
|
675
|
+
# Node#unstamped_parent).
|
|
676
|
+
node = node.unstamped_parent
|
|
677
|
+
end
|
|
678
|
+
if @document
|
|
679
|
+
@namespaces = scopes
|
|
680
|
+
@namespaces_version = @document.version
|
|
681
|
+
end
|
|
682
|
+
scopes
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
# The serialized children (Nokogiri parity): elements serialize
|
|
686
|
+
# through the engine (correct escaping), text is XML-escaped,
|
|
687
|
+
# comments/CDATA/PIs render their literal forms. The complement
|
|
688
|
+
# of #inner_text, which returns the unescaped text content.
|
|
689
|
+
def inner_html
|
|
690
|
+
ensure_alive!
|
|
691
|
+
# One C pass (TODO.perf/18): the child chain, per-kind
|
|
692
|
+
# serialization, and the escape set all in one dispatch —
|
|
693
|
+
# byte-identical to the Ruby loop below.
|
|
694
|
+
return Leptris::XML::Native.fast_inner_xml(@c_address) if
|
|
695
|
+
@native_fast
|
|
696
|
+
children.map do |child|
|
|
697
|
+
case child
|
|
698
|
+
when Leptris::XML::Element
|
|
699
|
+
Leptris::XML::Serialization.element_xml_default(child.c_ptr)
|
|
700
|
+
when Leptris::XML::CDATA # before Text: CDATA subclasses it
|
|
701
|
+
"<![CDATA[#{child.content}]]>"
|
|
702
|
+
when Leptris::XML::Text
|
|
703
|
+
Leptris::XML::Serialization.escape_text(child.content)
|
|
704
|
+
when Leptris::XML::Comment then "<!--#{child.content}-->"
|
|
705
|
+
when Leptris::XML::ProcessingInstruction
|
|
706
|
+
data = child.content
|
|
707
|
+
data.empty? ? "<?#{child.name}?>" : "<?#{child.name} #{data}?>"
|
|
708
|
+
else
|
|
709
|
+
Leptris::XML::Serialization.escape_text(child.content.to_s)
|
|
710
|
+
end
|
|
711
|
+
end.join
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
# indent_text: a STRING is the indent unit with Nokogiri's
|
|
715
|
+
# semantics (leptris-ruby#109; via a C-side copy into a fresh
|
|
716
|
+
# document — no element-level ext entry exists yet). true is NOT
|
|
717
|
+
# supported here: the display form is document-level.
|
|
718
|
+
def to_xml(indent: 0, no_decl: false, encoding: nil, indent_text: false,
|
|
719
|
+
expand_empty: false)
|
|
720
|
+
ensure_alive!
|
|
721
|
+
if indent_text == true
|
|
722
|
+
raise ArgumentError,
|
|
723
|
+
"indent_text: true (display form) is document-level — " \
|
|
724
|
+
"pass the unit string for elements"
|
|
725
|
+
end
|
|
726
|
+
return Leptris::XML::Serialization.to_xml_element_unit(
|
|
727
|
+
self, indent_text, indent: indent) if indent_text.is_a?(String)
|
|
728
|
+
return Leptris::XML::Serialization.element_xml_expand_empty(c_ptr) if expand_empty
|
|
729
|
+
Leptris::XML::Serialization.to_xml(
|
|
730
|
+
Leptris::XML::Serialization::ELEMENT_SERIALIZE_INTO, c_ptr,
|
|
731
|
+
indent: indent, no_decl: no_decl, encoding: encoding)
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
def canonicalize(version = Leptris::XML::FFI::C14N_1_0,
|
|
735
|
+
inclusive_namespaces = nil,
|
|
736
|
+
with_comments: false,
|
|
737
|
+
exclusive: false,
|
|
738
|
+
mode: nil)
|
|
739
|
+
resolved_mode = mode || (exclusive ? Leptris::XML::FFI::C14N_MODE_EXCLUSIVE
|
|
740
|
+
: Leptris::XML::FFI::C14N_MODE_CANONICAL)
|
|
741
|
+
Leptris::XML::Serialization.canonicalize(
|
|
742
|
+
Leptris::XML::FFI.method(:leptris_c14n_canonicalize_subtree_ex), c_ptr,
|
|
743
|
+
version: version, mode: resolved_mode,
|
|
744
|
+
inclusive_namespaces: inclusive_namespaces,
|
|
745
|
+
with_comments: with_comments)
|
|
746
|
+
end
|
|
747
|
+
alias_method :c14n, :canonicalize
|
|
748
|
+
|
|
749
|
+
def add_namespace_definition(prefix, href)
|
|
750
|
+
ensure_writable!
|
|
751
|
+
Leptris::XML::FFI.check_status(
|
|
752
|
+
Leptris::XML::FFI.leptris_element_add_namespace_definition(
|
|
753
|
+
c_ptr, prefix.to_s, href.to_s))
|
|
754
|
+
Leptris::XML::Namespace.new(self, href.to_s, prefix: prefix.nil? ? nil : prefix.to_s)
|
|
755
|
+
end
|
|
756
|
+
alias_method :add_namespace, :add_namespace_definition
|
|
757
|
+
|
|
758
|
+
def default_namespace=(href)
|
|
759
|
+
ensure_writable!
|
|
760
|
+
Leptris::XML::FFI.check_status(
|
|
761
|
+
Leptris::XML::FFI.leptris_element_set_default_namespace(c_ptr, href.to_s))
|
|
762
|
+
href
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
def remove_namespace_definition(prefix)
|
|
766
|
+
ensure_writable!
|
|
767
|
+
Leptris::XML::FFI.check_status(
|
|
768
|
+
Leptris::XML::FFI.leptris_element_remove_namespace_definition(c_ptr, prefix.to_s))
|
|
769
|
+
self
|
|
770
|
+
end
|
|
771
|
+
end
|