moxml 0.1.26 → 0.3.0
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 +4 -4
- data/.github/workflows/windows.yml +62 -0
- data/Gemfile +3 -1
- data/README.adoc +69 -6
- data/lib/compat/opal/moxml_boot.rb +6 -1
- data/lib/moxml/adapter/base.rb +53 -56
- data/lib/moxml/adapter/customized_leptris/declaration.rb +32 -0
- data/lib/moxml/adapter/customized_leptris/doctype.rb +31 -0
- data/lib/moxml/adapter/customized_leptris/entity_reference.rb +12 -0
- data/lib/moxml/adapter/customized_leptris/text_segment.rb +26 -0
- data/lib/moxml/adapter/customized_leptris.rb +16 -0
- data/lib/moxml/adapter/customized_libxml/cdata.rb +3 -12
- data/lib/moxml/adapter/customized_libxml/comment.rb +2 -9
- data/lib/moxml/adapter/customized_libxml/declaration.rb +1 -10
- data/lib/moxml/adapter/customized_libxml/node.rb +8 -0
- data/lib/moxml/adapter/customized_libxml/processing_instruction.rb +2 -10
- data/lib/moxml/adapter/customized_libxml/text.rb +2 -9
- data/lib/moxml/adapter/customized_oga/entity_decoder.rb +37 -0
- data/lib/moxml/adapter/customized_oga/raw_value_override.rb +52 -0
- data/lib/moxml/adapter/customized_oga/xml_generator.rb +4 -34
- data/lib/moxml/adapter/customized_oga.rb +2 -0
- data/lib/moxml/adapter/customized_ox/entity_reference.rb +1 -17
- data/lib/moxml/adapter/customized_rexml/entity_reference.rb +1 -11
- data/lib/moxml/adapter/headed_ox.rb +9 -128
- data/lib/moxml/adapter/leptris.rb +965 -0
- data/lib/moxml/adapter/libxml.rb +137 -117
- data/lib/moxml/adapter/nokogiri.rb +29 -8
- data/lib/moxml/adapter/oga.rb +139 -55
- data/lib/moxml/adapter/ox.rb +93 -137
- data/lib/moxml/adapter/rexml.rb +64 -51
- data/lib/moxml/adapter.rb +2 -1
- data/lib/moxml/attribute.rb +8 -4
- data/lib/moxml/attribute_resolver.rb +189 -0
- data/lib/moxml/builder.rb +0 -18
- data/lib/moxml/config.rb +29 -5
- data/lib/moxml/context.rb +33 -0
- data/lib/moxml/document.rb +1 -1
- data/lib/moxml/element.rb +29 -12
- data/lib/moxml/entity/reference.rb +28 -0
- data/lib/moxml/entity/restorer.rb +95 -0
- data/lib/moxml/entity.rb +126 -0
- data/lib/moxml/entity_registry.rb +1 -1
- data/lib/moxml/error.rb +0 -42
- data/lib/moxml/node.rb +135 -10
- data/lib/moxml/node_set.rb +1 -1
- data/lib/moxml/sax/namespace_splitter.rb +5 -2
- data/lib/moxml/signature/model/key/dsa_key_value.rb +1 -2
- data/lib/moxml/version.rb +1 -1
- data/lib/moxml/xml_emitter.rb +71 -0
- data/lib/moxml/xpath/compiler.rb +143 -8
- data/lib/moxml/xpath/parser.rb +22 -15
- data/lib/moxml.rb +3 -3
- data/spec/examples/readme_examples_spec.rb +0 -4
- data/spec/examples/xpath_examples_spec.rb +0 -11
- data/spec/integration/all_adapters_spec.rb +18 -1
- data/spec/integration/sax_parity_spec.rb +58 -0
- data/spec/integration/shared_examples/edge_cases.rb +0 -10
- data/spec/integration/shared_examples/high_level/{document_builder_behavior.rb → parse_behavior.rb} +1 -2
- data/spec/integration/shared_examples/integration_workflows.rb +0 -10
- data/spec/integration/shared_examples/node_wrappers/attribute_behavior.rb +98 -0
- data/spec/integration/shared_examples/node_wrappers/namespace_behavior.rb +26 -0
- data/spec/integration/shared_examples/node_wrappers/node_behavior.rb +15 -8
- data/spec/moxml/adapter/leptris_spec.rb +75 -0
- data/spec/moxml/adapter/libxml_internals_spec.rb +1 -1
- data/spec/moxml/adapter/ox_spec.rb +20 -6
- data/spec/moxml/adapter/platform_spec.rb +15 -2
- data/spec/moxml/adapter/shared_examples/adapter_contract.rb +180 -0
- data/spec/moxml/attribute_resolver_spec.rb +108 -0
- data/spec/moxml/config_spec.rb +0 -1
- data/spec/moxml/context_spec.rb +22 -0
- data/spec/moxml/doctype_spec.rb +1 -1
- data/spec/moxml/entity_spec.rb +76 -0
- data/spec/moxml/error_spec.rb +0 -50
- data/spec/moxml/node_spec.rb +104 -0
- data/spec/moxml/sax_entity_parity_spec.rb +358 -0
- data/spec/moxml/xml_emitter_spec.rb +64 -0
- data/spec/moxml/xpath/axes_spec.rb +112 -0
- data/spec/moxml/xpath/parser_spec.rb +6 -0
- data/spec/moxml/xpath_capabilities_spec.rb +5 -3
- data/spec/performance/benchmark_spec.rb +13 -6
- data/spec/performance/thread_safety_spec.rb +0 -4
- data/spec/spec_helper.rb +5 -1
- metadata +23 -6
- data/lib/moxml/adapter/libxml/entity_restorer.rb +0 -94
- data/lib/moxml/document_builder.rb +0 -167
- data/spec/moxml/document_builder_spec.rb +0 -30
|
@@ -23,14 +23,21 @@ RSpec.shared_examples "Performance Examples" do
|
|
|
23
23
|
context "measures performance" do
|
|
24
24
|
let(:doc) { context.parse(large_xml) }
|
|
25
25
|
|
|
26
|
+
# Absolute ips varies more than 2x across machines and even between
|
|
27
|
+
# runs on the same machine (observed: rexml serializer 1.5-2.9 ips on
|
|
28
|
+
# one machine). These thresholds sit at roughly half of typical
|
|
29
|
+
# modern-runner throughput so they only trip on asymptotic
|
|
30
|
+
# regressions (accidental O(n^2), broken caching), not on hardware
|
|
31
|
+
# variance or a few percent of legitimate overhead.
|
|
26
32
|
let(:thresholds) do
|
|
27
33
|
{
|
|
28
|
-
nokogiri: { parser:
|
|
29
|
-
oga: { parser:
|
|
30
|
-
rexml: { parser: 0, serializer:
|
|
31
|
-
ox: { parser:
|
|
32
|
-
headed_ox: { parser:
|
|
33
|
-
libxml: { parser:
|
|
34
|
+
nokogiri: { parser: 8, serializer: 500 },
|
|
35
|
+
oga: { parser: 5, serializer: 50 },
|
|
36
|
+
rexml: { parser: 0, serializer: 1 },
|
|
37
|
+
ox: { parser: 1, serializer: 400 },
|
|
38
|
+
headed_ox: { parser: 1, serializer: 400 },
|
|
39
|
+
libxml: { parser: 200, serializer: 30 },
|
|
40
|
+
leptris: { parser: 1300, serializer: 600 },
|
|
34
41
|
}
|
|
35
42
|
end
|
|
36
43
|
|
|
@@ -20,10 +20,6 @@ RSpec.shared_examples "Thread Safety Examples" do
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
it "handles concurrent processing" do
|
|
23
|
-
if Moxml.new.config.adapter_name == :ox
|
|
24
|
-
skip "Ox doesn't have a native XPath"
|
|
25
|
-
end
|
|
26
|
-
|
|
27
23
|
processor = processor_class.new
|
|
28
24
|
threads = []
|
|
29
25
|
results = Queue.new
|
data/spec/spec_helper.rb
CHANGED
|
@@ -101,7 +101,11 @@ RSpec.configure do |config|
|
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
Moxml.configure do |config|
|
|
104
|
-
|
|
104
|
+
# Pin the suite baseline to the fallback adapter so results are
|
|
105
|
+
# reproducible regardless of whether the local environment has a
|
|
106
|
+
# leptris new enough to win the runtime-default probe. Leptris gets
|
|
107
|
+
# full coverage through its explicit adapter/integration matrices.
|
|
108
|
+
config.adapter = RUBY_ENGINE == "opal" ? Moxml::Config::OPAL_DEFAULT_ADAPTER : Moxml::Config::FALLBACK_ADAPTER
|
|
105
109
|
config.strict_parsing = true
|
|
106
110
|
config.default_encoding = "UTF-8"
|
|
107
111
|
config.entity_load_mode = :optional if RUBY_ENGINE == "opal"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: moxml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-08-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: |
|
|
14
14
|
Moxml is a unified XML manipulation library that provides a common API
|
|
@@ -27,6 +27,7 @@ files:
|
|
|
27
27
|
- ".github/workflows/rake.yml"
|
|
28
28
|
- ".github/workflows/release.yml"
|
|
29
29
|
- ".github/workflows/round-trip.yml"
|
|
30
|
+
- ".github/workflows/windows.yml"
|
|
30
31
|
- ".gitignore"
|
|
31
32
|
- ".gitmodules"
|
|
32
33
|
- ".rspec"
|
|
@@ -119,6 +120,11 @@ files:
|
|
|
119
120
|
- lib/moxml.rb
|
|
120
121
|
- lib/moxml/adapter.rb
|
|
121
122
|
- lib/moxml/adapter/base.rb
|
|
123
|
+
- lib/moxml/adapter/customized_leptris.rb
|
|
124
|
+
- lib/moxml/adapter/customized_leptris/declaration.rb
|
|
125
|
+
- lib/moxml/adapter/customized_leptris/doctype.rb
|
|
126
|
+
- lib/moxml/adapter/customized_leptris/entity_reference.rb
|
|
127
|
+
- lib/moxml/adapter/customized_leptris/text_segment.rb
|
|
122
128
|
- lib/moxml/adapter/customized_libxml.rb
|
|
123
129
|
- lib/moxml/adapter/customized_libxml/cdata.rb
|
|
124
130
|
- lib/moxml/adapter/customized_libxml/comment.rb
|
|
@@ -129,6 +135,8 @@ files:
|
|
|
129
135
|
- lib/moxml/adapter/customized_libxml/processing_instruction.rb
|
|
130
136
|
- lib/moxml/adapter/customized_libxml/text.rb
|
|
131
137
|
- lib/moxml/adapter/customized_oga.rb
|
|
138
|
+
- lib/moxml/adapter/customized_oga/entity_decoder.rb
|
|
139
|
+
- lib/moxml/adapter/customized_oga/raw_value_override.rb
|
|
132
140
|
- lib/moxml/adapter/customized_oga/xml_declaration.rb
|
|
133
141
|
- lib/moxml/adapter/customized_oga/xml_generator.rb
|
|
134
142
|
- lib/moxml/adapter/customized_ox.rb
|
|
@@ -140,14 +148,15 @@ files:
|
|
|
140
148
|
- lib/moxml/adapter/customized_rexml/entity_reference.rb
|
|
141
149
|
- lib/moxml/adapter/customized_rexml/formatter.rb
|
|
142
150
|
- lib/moxml/adapter/headed_ox.rb
|
|
151
|
+
- lib/moxml/adapter/leptris.rb
|
|
143
152
|
- lib/moxml/adapter/libxml.rb
|
|
144
153
|
- lib/moxml/adapter/libxml/entity_ref_registry.rb
|
|
145
|
-
- lib/moxml/adapter/libxml/entity_restorer.rb
|
|
146
154
|
- lib/moxml/adapter/nokogiri.rb
|
|
147
155
|
- lib/moxml/adapter/oga.rb
|
|
148
156
|
- lib/moxml/adapter/ox.rb
|
|
149
157
|
- lib/moxml/adapter/rexml.rb
|
|
150
158
|
- lib/moxml/attribute.rb
|
|
159
|
+
- lib/moxml/attribute_resolver.rb
|
|
151
160
|
- lib/moxml/builder.rb
|
|
152
161
|
- lib/moxml/c14n.rb
|
|
153
162
|
- lib/moxml/c14n/attribute_handler.rb
|
|
@@ -177,8 +186,10 @@ files:
|
|
|
177
186
|
- lib/moxml/declaration.rb
|
|
178
187
|
- lib/moxml/doctype.rb
|
|
179
188
|
- lib/moxml/document.rb
|
|
180
|
-
- lib/moxml/document_builder.rb
|
|
181
189
|
- lib/moxml/element.rb
|
|
190
|
+
- lib/moxml/entity.rb
|
|
191
|
+
- lib/moxml/entity/reference.rb
|
|
192
|
+
- lib/moxml/entity/restorer.rb
|
|
182
193
|
- lib/moxml/entity_reference.rb
|
|
183
194
|
- lib/moxml/entity_registry.rb
|
|
184
195
|
- lib/moxml/entity_registry_opal_data.rb
|
|
@@ -246,6 +257,7 @@ files:
|
|
|
246
257
|
- lib/moxml/signature/verifier.rb
|
|
247
258
|
- lib/moxml/text.rb
|
|
248
259
|
- lib/moxml/version.rb
|
|
260
|
+
- lib/moxml/xml_emitter.rb
|
|
249
261
|
- lib/moxml/xml_utils.rb
|
|
250
262
|
- lib/moxml/xml_utils/encoder.rb
|
|
251
263
|
- lib/moxml/xpath.rb
|
|
@@ -371,12 +383,13 @@ files:
|
|
|
371
383
|
- spec/integration/README.md
|
|
372
384
|
- spec/integration/all_adapters_spec.rb
|
|
373
385
|
- spec/integration/headed_ox_integration_spec.rb
|
|
386
|
+
- spec/integration/sax_parity_spec.rb
|
|
374
387
|
- spec/integration/shared_examples/edge_cases.rb
|
|
375
388
|
- spec/integration/shared_examples/entity_reference_whitespace.rb
|
|
376
389
|
- spec/integration/shared_examples/high_level/.gitkeep
|
|
377
390
|
- spec/integration/shared_examples/high_level/builder_behavior.rb
|
|
378
391
|
- spec/integration/shared_examples/high_level/context_behavior.rb
|
|
379
|
-
- spec/integration/shared_examples/high_level/
|
|
392
|
+
- spec/integration/shared_examples/high_level/parse_behavior.rb
|
|
380
393
|
- spec/integration/shared_examples/integration_workflows.rb
|
|
381
394
|
- spec/integration/shared_examples/line_ending_behavior.rb
|
|
382
395
|
- spec/integration/shared_examples/node_wrappers/.gitkeep
|
|
@@ -401,6 +414,7 @@ files:
|
|
|
401
414
|
- spec/moxml/adapter/base_spec.rb
|
|
402
415
|
- spec/moxml/adapter/entity_restoration_spec.rb
|
|
403
416
|
- spec/moxml/adapter/headed_ox_spec.rb
|
|
417
|
+
- spec/moxml/adapter/leptris_spec.rb
|
|
404
418
|
- spec/moxml/adapter/libxml_internals_spec.rb
|
|
405
419
|
- spec/moxml/adapter/libxml_spec.rb
|
|
406
420
|
- spec/moxml/adapter/nokogiri_spec.rb
|
|
@@ -413,6 +427,7 @@ files:
|
|
|
413
427
|
- spec/moxml/adapter_spec.rb
|
|
414
428
|
- spec/moxml/allocation_benchmark_spec.rb
|
|
415
429
|
- spec/moxml/allocation_guard_spec.rb
|
|
430
|
+
- spec/moxml/attribute_resolver_spec.rb
|
|
416
431
|
- spec/moxml/attribute_spec.rb
|
|
417
432
|
- spec/moxml/builder_spec.rb
|
|
418
433
|
- spec/moxml/c14n/api_spec.rb
|
|
@@ -428,12 +443,12 @@ files:
|
|
|
428
443
|
- spec/moxml/declaration_preservation_spec.rb
|
|
429
444
|
- spec/moxml/declaration_spec.rb
|
|
430
445
|
- spec/moxml/doctype_spec.rb
|
|
431
|
-
- spec/moxml/document_builder_spec.rb
|
|
432
446
|
- spec/moxml/document_spec.rb
|
|
433
447
|
- spec/moxml/element_spec.rb
|
|
434
448
|
- spec/moxml/entity_preservation_spec.rb
|
|
435
449
|
- spec/moxml/entity_reference_spec.rb
|
|
436
450
|
- spec/moxml/entity_registry_spec.rb
|
|
451
|
+
- spec/moxml/entity_spec.rb
|
|
437
452
|
- spec/moxml/error_spec.rb
|
|
438
453
|
- spec/moxml/lazy_parse_spec.rb
|
|
439
454
|
- spec/moxml/moxml_spec.rb
|
|
@@ -455,6 +470,7 @@ files:
|
|
|
455
470
|
- spec/moxml/opal_smoke_spec.rb
|
|
456
471
|
- spec/moxml/processing_instruction_spec.rb
|
|
457
472
|
- spec/moxml/sax/namespace_splitter_spec.rb
|
|
473
|
+
- spec/moxml/sax_entity_parity_spec.rb
|
|
458
474
|
- spec/moxml/sax_spec.rb
|
|
459
475
|
- spec/moxml/signature/algorithms/base64_transform_spec.rb
|
|
460
476
|
- spec/moxml/signature/algorithms/digest_base_spec.rb
|
|
@@ -472,6 +488,7 @@ files:
|
|
|
472
488
|
- spec/moxml/signature/round_trip_spec.rb
|
|
473
489
|
- spec/moxml/text_spec.rb
|
|
474
490
|
- spec/moxml/version_spec.rb
|
|
491
|
+
- spec/moxml/xml_emitter_spec.rb
|
|
475
492
|
- spec/moxml/xml_utils/.gitkeep
|
|
476
493
|
- spec/moxml/xml_utils/encoder_spec.rb
|
|
477
494
|
- spec/moxml/xml_utils_spec.rb
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Moxml
|
|
4
|
-
module Adapter
|
|
5
|
-
class Libxml < Base
|
|
6
|
-
# Restores configured character entities into explicit Moxml
|
|
7
|
-
# EntityReference nodes after LibXML has parsed the native tree.
|
|
8
|
-
class EntityRestorer
|
|
9
|
-
def initialize(doc, adapter: Libxml)
|
|
10
|
-
@doc = doc
|
|
11
|
-
@ctx = doc.context
|
|
12
|
-
@registry = @ctx.entity_registry
|
|
13
|
-
@config = @ctx.config
|
|
14
|
-
@adapter = adapter
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def run
|
|
18
|
-
return unless @registry && @doc.root
|
|
19
|
-
|
|
20
|
-
walk(@doc.root)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
private
|
|
24
|
-
|
|
25
|
-
def walk(element)
|
|
26
|
-
# Snapshot because we may add/remove siblings during the walk.
|
|
27
|
-
element.children.to_a.each do |child|
|
|
28
|
-
if child.is_a?(::Moxml::Text)
|
|
29
|
-
restore_text_node(child)
|
|
30
|
-
elsif child.is_a?(::Moxml::Element)
|
|
31
|
-
walk(child)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Matches DocumentBuilder's previous behavior, including the libxml
|
|
37
|
-
# limitation that adjacent native text nodes get merged.
|
|
38
|
-
def restore_text_node(text_node)
|
|
39
|
-
content = text_node.content
|
|
40
|
-
return unless content
|
|
41
|
-
|
|
42
|
-
chunks = chunk_text(content)
|
|
43
|
-
return if chunks.size == 1 && chunks.first.first == :text
|
|
44
|
-
|
|
45
|
-
parent = text_node.parent
|
|
46
|
-
return unless parent
|
|
47
|
-
|
|
48
|
-
text_node.remove
|
|
49
|
-
chunks.each { |type, payload| append_chunk(parent, type, payload) }
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def chunk_text(content)
|
|
53
|
-
chunks = []
|
|
54
|
-
buffer = +""
|
|
55
|
-
restorable = @registry.restorable_codepoints
|
|
56
|
-
|
|
57
|
-
content.each_char do |char|
|
|
58
|
-
cp = char.ord
|
|
59
|
-
if restorable.include?(cp) &&
|
|
60
|
-
(name = @registry.primary_name_for_codepoint(cp)) &&
|
|
61
|
-
@registry.should_restore?(cp, config: @config)
|
|
62
|
-
unless buffer.empty?
|
|
63
|
-
chunks << [:text, buffer.dup]
|
|
64
|
-
buffer.clear
|
|
65
|
-
end
|
|
66
|
-
chunks << [:eref, name]
|
|
67
|
-
else
|
|
68
|
-
buffer << char
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
chunks << [:text, buffer.dup] unless buffer.empty?
|
|
73
|
-
chunks
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def append_chunk(parent, type, payload)
|
|
77
|
-
case type
|
|
78
|
-
when :text
|
|
79
|
-
parent.add_child(::Moxml::Text.new(
|
|
80
|
-
@adapter.create_native_text(payload), @ctx
|
|
81
|
-
))
|
|
82
|
-
when :eref
|
|
83
|
-
parent.add_child(
|
|
84
|
-
::Moxml::EntityReference.new(
|
|
85
|
-
@adapter.create_native_entity_reference(payload),
|
|
86
|
-
@ctx,
|
|
87
|
-
),
|
|
88
|
-
)
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Moxml
|
|
4
|
-
class DocumentBuilder
|
|
5
|
-
attr_reader :context
|
|
6
|
-
|
|
7
|
-
def initialize(context)
|
|
8
|
-
@context = context
|
|
9
|
-
@node_stack = []
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def build(native_doc)
|
|
13
|
-
@current_doc = context.create_document(native_doc)
|
|
14
|
-
|
|
15
|
-
# Transfer has_declaration flag if present in attachments
|
|
16
|
-
if adapter.attachments.key?(native_doc, :has_declaration)
|
|
17
|
-
has_declaration = adapter.attachments.get(native_doc, :has_declaration)
|
|
18
|
-
@current_doc.has_xml_declaration = has_declaration
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Transfer DOCTYPE from parsed document if it exists in attachments
|
|
22
|
-
if adapter.attachments.key?(native_doc, :doctype)
|
|
23
|
-
doctype = adapter.attachments.get(native_doc, :doctype)
|
|
24
|
-
if doctype
|
|
25
|
-
adapter.attachments.set(@current_doc.native, :doctype, doctype)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
visit_node(native_doc)
|
|
30
|
-
@current_doc
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
private
|
|
34
|
-
|
|
35
|
-
def visit_node(node)
|
|
36
|
-
case node_type(node)
|
|
37
|
-
when :document then visit_document(node)
|
|
38
|
-
when :element then visit_element(node)
|
|
39
|
-
when :text then visit_text(node)
|
|
40
|
-
when :cdata then visit_cdata(node)
|
|
41
|
-
when :comment then visit_comment(node)
|
|
42
|
-
when :processing_instruction then visit_processing_instruction(node)
|
|
43
|
-
when :doctype then visit_doctype(node)
|
|
44
|
-
when :entity_reference then visit_entity_reference(node)
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def visit_document(doc)
|
|
49
|
-
@node_stack.push(@current_doc)
|
|
50
|
-
visit_children(doc)
|
|
51
|
-
@node_stack.clear
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def visit_element(node)
|
|
55
|
-
childless_node = adapter.duplicate_node(node)
|
|
56
|
-
adapter.replace_children(childless_node, [])
|
|
57
|
-
# Prepare node for new document (LibXML needs this)
|
|
58
|
-
childless_node = adapter.prepare_for_new_document(
|
|
59
|
-
childless_node,
|
|
60
|
-
@current_doc.native,
|
|
61
|
-
)
|
|
62
|
-
element = Element.new(childless_node, context)
|
|
63
|
-
@node_stack.last.add_child(element)
|
|
64
|
-
|
|
65
|
-
@node_stack.push(element) # add a parent for its children
|
|
66
|
-
visit_children(node)
|
|
67
|
-
@node_stack.pop # remove the parent
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def visit_text(node)
|
|
71
|
-
# Prepare node for new document before wrapping
|
|
72
|
-
prepared = adapter.prepare_for_new_document(node, @current_doc.native)
|
|
73
|
-
content = adapter.text_content(node)
|
|
74
|
-
|
|
75
|
-
# Check if we should restore entity references for this text
|
|
76
|
-
if context.config.restore_entities && text_has_restorable_entities?(content)
|
|
77
|
-
restore_entities_in_text(content)
|
|
78
|
-
else
|
|
79
|
-
@node_stack.last&.add_child(Text.new(prepared, context))
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def text_has_restorable_entities?(content)
|
|
84
|
-
return false unless content
|
|
85
|
-
|
|
86
|
-
registry = context.entity_registry
|
|
87
|
-
codepoints = registry.restorable_codepoints
|
|
88
|
-
content.each_char do |char|
|
|
89
|
-
return true if codepoints.include?(char.ord)
|
|
90
|
-
end
|
|
91
|
-
false
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def restore_entities_in_text(content)
|
|
95
|
-
parent = @node_stack.last
|
|
96
|
-
return unless parent
|
|
97
|
-
|
|
98
|
-
registry = context.entity_registry
|
|
99
|
-
config = context.config
|
|
100
|
-
buffer = +""
|
|
101
|
-
|
|
102
|
-
content.to_s.each_char do |char|
|
|
103
|
-
codepoint = char.ord
|
|
104
|
-
name = registry.primary_name_for_codepoint(codepoint)
|
|
105
|
-
|
|
106
|
-
if name && registry.should_restore?(codepoint, config: config)
|
|
107
|
-
# Flush buffered text before the entity
|
|
108
|
-
unless buffer.empty?
|
|
109
|
-
parent.add_child(Text.new(adapter.create_text(buffer), context))
|
|
110
|
-
buffer.clear
|
|
111
|
-
end
|
|
112
|
-
parent.add_child(
|
|
113
|
-
EntityReference.new(adapter.create_entity_reference(name), context),
|
|
114
|
-
)
|
|
115
|
-
else
|
|
116
|
-
buffer << char
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
# Flush remaining buffer
|
|
121
|
-
unless buffer.empty?
|
|
122
|
-
parent.add_child(Text.new(adapter.create_text(buffer), context))
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def visit_cdata(node)
|
|
127
|
-
prepared = adapter.prepare_for_new_document(node, @current_doc.native)
|
|
128
|
-
@node_stack.last&.add_child(Cdata.new(prepared, context))
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def visit_comment(node)
|
|
132
|
-
prepared = adapter.prepare_for_new_document(node, @current_doc.native)
|
|
133
|
-
@node_stack.last&.add_child(Comment.new(prepared, context))
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def visit_processing_instruction(node)
|
|
137
|
-
prepared = adapter.prepare_for_new_document(node, @current_doc.native)
|
|
138
|
-
@node_stack.last&.add_child(ProcessingInstruction.new(prepared, context))
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def visit_doctype(node)
|
|
142
|
-
prepared = adapter.prepare_for_new_document(node, @current_doc.native)
|
|
143
|
-
@node_stack.last&.add_child(Doctype.new(prepared, context))
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
def visit_entity_reference(node)
|
|
147
|
-
prepared = adapter.prepare_for_new_document(node, @current_doc.native)
|
|
148
|
-
@node_stack.last&.add_child(EntityReference.new(prepared, context))
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def visit_children(node)
|
|
152
|
-
children(node).each { |child| visit_node(child) }
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def node_type(node)
|
|
156
|
-
context.config.adapter.node_type(node)
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def children(node)
|
|
160
|
-
context.config.adapter.children(node)
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def adapter
|
|
164
|
-
context.config.adapter
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
end
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
|
|
5
|
-
# DocumentBuilder is an internal class for parsing
|
|
6
|
-
# These tests verify it correctly builds Moxml document from native documents
|
|
7
|
-
RSpec.describe Moxml::DocumentBuilder do
|
|
8
|
-
let(:context) { Moxml.new }
|
|
9
|
-
|
|
10
|
-
describe "#build" do
|
|
11
|
-
it "builds a Moxml document from native document" do
|
|
12
|
-
native_doc = context.parse("<root><child>content</child></root>").native
|
|
13
|
-
builder = described_class.new(context)
|
|
14
|
-
doc = builder.build(native_doc)
|
|
15
|
-
|
|
16
|
-
expect(doc).to be_a(Moxml::Document)
|
|
17
|
-
expect(doc.root.name).to eq("root")
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
describe "element handling" do
|
|
22
|
-
it "handles nested elements" do
|
|
23
|
-
native_doc = context.parse("<parent><child1/><child2/></parent>").native
|
|
24
|
-
builder = described_class.new(context)
|
|
25
|
-
doc = builder.build(native_doc)
|
|
26
|
-
|
|
27
|
-
expect(doc.root.children.length).to eq(2)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|