Class: Moxml::Adapter::Leptris
- Inherits:
-
Base
- Object
- Base
- Moxml::Adapter::Leptris
show all
- Extended by:
- DocumentParts, Markers, Materialize, Serialize
- Defined in:
- lib/moxml/adapter/leptris.rb,
lib/moxml/adapter/leptris/markers.rb,
lib/moxml/adapter/leptris/serialize.rb,
lib/moxml/adapter/leptris/sax_bridge.rb,
lib/moxml/adapter/leptris/materialize.rb,
lib/moxml/adapter/leptris/document_parts.rb
Overview
Adapter over the leptris FFI binding (libleptris C library).
libleptris provides DOM parsing, a native XPath 1.0 engine, SAX,
and serialization. Its document-level creation surfaces exist
(set_doctype 1.9.176/#212, declaration setters #1094) but are
SET-ONLY — no unset for declaration, DOCTYPE, or document PIs —
while the moxml facade contract makes those parts removable
nodes. CustomizedLeptris value objects therefore remain the
lifecycle record, attached through NativeAttachment.
Defined Under Namespace
Modules: DocumentParts, Identity, Markers, Materialize, Serialize
Classes: LeptrisSAXBridge
Constant Summary
collapse
- MINIMUM_BINDING_VERSION =
The binding floor (issue #149): 1.9.32 carried the traverse
fix (leptris-ruby#89) and made built documents reflect their
parts immediately (leptris-ruby#91); the document node
(1.9.26), DTDATTR (1.9.8), and the batch child-pointer read
(1.7.0) surfaces predate it. Older bindings are not eligible
for the default (see Config.leptris_preferred_available?) and
the adapter no longer carries accommodation paths for them.
Raised 1.9.32 -> 1.9.194.1 (2026-09-20): every known consumer
resolves a modern binding (canon itself pins ~> 1.9.193), and
the sub-1.9.194 accommodation paths are deleted.
"1.9.194.1"
- ATTR_RESULT_NATIVE =
leptris_parse_html_string shipped in bindings 1.9.80
(libleptris 1.9.75, engine #659) as Leptris::XML.parse_html.
Attribute-node xpath results carry proper wrappers since
1.9.105 (leptris-ruby#153: ResultAttr with name/value);
before that the native gate routed them to the Ruby engine.
true
- DIGEST_SUPPORTED =
leptris_node_digest shipped in bindings 1.9.99 (libleptris
1.9.99, engine #869): on-demand Merkle subtree hash, zero
cost when unused. Below it, Node#digest answers nil.
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.99")
- NATIVE_XPATH_SEAMS =
1.9.163.7 added the XPath seams (TODO.perf/15+16):
Searchable#at_xpath materializes nodeset entry 0 and frees
the handle in one C dispatch — no NodeSet container, no
AutoPointer (2.8x on repeat at_xpath) — and
CompiledXPath#eval_ptrs runs the compiled handle against
raw pointers, skipping the per-call EvaluationContext
(~2us on every multi-result query).
defined?(::Leptris::XML::Native) &&
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.163.7")
- NATIVE_READ_LAYER =
defined?(::Leptris::XML::NativeNode) &&
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.162.6")
- NATIVE_STRINGS_UTF8 =
1.9.163.2's native layer returns UTF-8, unfrozen strings
(1.9.162.x answered ASCII-8BIT — the reads retagged with a
dup+force_encoding per call).
defined?(::Leptris::XML::NativeNode) &&
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.163.2")
- NATIVE_MUTATIONS_COHERENT =
1.9.163.5 (leptris-ruby#204/#208): native mutations are
version-coherent with the binding (both surfaces' memos
drop on mutation) — the builder factories and native
add_child are adoptable end-to-end; NativeNode grew a
document accessor and line numbers.
defined?(::Leptris::XML::NativeNode) &&
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.163.5")
- NN =
::Leptris::XML::NativeNode
- NN_ATTRIBUTE =
NN.instance_method(:attribute)
- NN_NAME =
NN.instance_method(:name)
- NN_PARENT =
NN.instance_method(:parent)
- NN_NEXT_SIBLING =
NN.instance_method(:next_sibling)
- NN_CONTENT =
NN.instance_method(:content)
- NN_CHILDREN =
NN.instance_method(:children)
- NN_NODE_TYPE =
NN.instance_method(:node_type)
- NN_DOCUMENT =
NN.instance_method(:document)
- NN_ADD_CHILD =
NN.instance_method(:add_child)
- NATIVE_SOURCE_POSITION =
1.9.181 (lockstep): Node#source_position — col_start,
col_end from the engine's source-position API (upstream
#1124); created nodes report zeros.
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.181")
- NATIVE_ENTITY_REFS =
Entity-reference preservation (leptris-ruby#212 / upstream
#1094): 1.9.177 ships ParseOptions.keep_entity_refs plus
first-class EntityReference nodes — the marker machinery
becomes bypassable when the Context's entity_mode is :keep.
defined?(::Leptris::XML::EntityReference) &&
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.177")
- NATIVE_BULK_FIXED =
1.9.163.2 moved the native bulk children into C
(Native.bulk_children) and fixed the 512 truncation
(leptris-ruby#202). The 162.6-163.1 window carries the
binding-side truncation, so moxml fetches with its own
count-then-copy scratch there; newer bindings use their
C bulk path (version-memoized, cheaper than a per-call
count+copy).
defined?(::Leptris::XML::NativeNode) &&
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.163.2")
- NATIVE_IDENTITY =
NATIVE_MUTATIONS_COHERENT &&
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.174.4")
- NATIVE_KLASS_CHILDREN =
leptris-ruby#246 klass-propagating reads: the read
family (children/element_children/next_sibling/parent)
mints the mapped subclass per kind, so children arrive
already carrying the contract — wrap_native's is_a?
guard short-circuits and the per-visit from() mint
(the second allocation of the pair) drops out. Kind
order is the C layer's node-type ints: element, text,
comment, cdata, pi.
NATIVE_IDENTITY &&
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.193.2")
- NATIVE_C_WALK =
defined?(::Leptris::XML::NativeNode) &&
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.174.6")
- PREFIXED_ATTR_PREDICATES_NATIVE =
leptris-ruby#103: prefixed attribute tests inside predicates
stopped resolving through the document's in-scope declarations
on released 1.9.37–1.9.39; 1.9.40 (engine 1.9.14+) restored
the fallback. Older bindings keep the Ruby-engine routing in
native_expression?.
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.40")
- LIBXML2_LAYOUT_PARITY =
leptris/leptris#636 (leptris-ruby 1.9.42): libxml2-compatible
pretty-print — child-PI lines, DOCTYPE internal-subset layout,
no stray trailing newline after non-ASCII text, and the indent
unit (to_xml's indent_text takes the unit string). Older
floor bindings treat indent_text as a display-form boolean, so
the passthrough and the parity pins gate on this.
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.42")
- INDENT_UNIT_SUPPORTED =
leptris-ruby#109 (1.9.45): Element#to_xml takes the
indent-unit string, so moxml's per-child document composition
can carry indent_text: through. Text-bearing leaves still
emit spaces until the engine fix (leptris/leptris#658); the
boolean display form stays document-level and is never
forwarded (the element face raises on it).
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.45")
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.50")
- ENGINE_NOBLANKS_SAFE =
leptris/leptris#677: the engine's DROP_WS_TEXT trims boundary
whitespace of non-blank text nodes. Probed at load rather
than version-gated so the C fix is adopted the moment a fixed
binding installs — no moxml release needed. While the probe
fails, noblanks drops blanks moxml-side after parse.
begin
doc = ::Leptris::XML::Document.parse(
"<r> x</r>", options: ::Leptris::XML::ParseOptions.noblanks
)
text = doc.root.children.first
text.is_a?(::Leptris::XML::Text) && text.content == " x"
rescue StandardError
false
end
- ENGINE_MULTI_DECL_SUBSET_OK =
leptris/leptris#687: the engine's subset serializer mangles
every internal-subset declaration after the first. Probed for
the same reason — once fixed, the whole-document fast path
serves multi-declaration subsets too.
begin
doc = ::Leptris::XML::Document.parse(
%(<?xml version="1.0"?><!DOCTYPE r [<!ELEMENT r (#PCDATA)><!ATTLIST e a CDATA "d">]><r/>),
)
doc.to_xml.include?("<!ATTLIST")
rescue StandardError
false
end
- NATIVE_PLAN_ROWS =
Plan row stream (Moxml::Plan): the engine's one-pass C
snapshot is pre-order with the first text child on the row —
exactly the plan's shape, with no wrapper minting at all.
Marker-bearing documents stay on the generic path (the bulk
stream has no marker split).
true
- NATIVE_PLAN_STRUCTS =
leptris-ruby#272 (binding 1.9.201.1): Native.plan_structs —
the C struct executor behind Moxml::StructPlan. The floor
is the varargs-mint fix: 1.9.197.1–1.9.201.0 seed unmapped
struct members with garbage (GC aborts, corrupted data);
StructPlan falls back to its spec-pinned Moxml::Plan path
below the fix. The probe guards the version gate: a
numerically newer lockstep can publish without the face
(1.9.199.0 shipped without it), and the gate alone would
NoMethodError.
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.201.1") &&
(!defined?(::Leptris::XML::Native) ||
::Leptris::XML::Native.respond_to?(:plan_structs))
- NATIVE_DOC_PARTS =
leptris-ruby#275 (binding 1.9.204, the remove half of the
document-level surfaces): remove_pi, clear_declaration,
remove_doctype complete the set-only creation entries.
Attached DOCTYPEs materialize as native nodes, document-PI
removal works, and declaration state mirrors through.
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.204.0") &&
::Leptris::XML::Document.method_defined?(:remove_doctype) &&
::Leptris::XML::Document.method_defined?(:clear_declaration)
- BLANK_TEXT_RE =
XML whitespace exactly — \v and \f are not XML space.
/\A[ \t\r\n]*\z/
- NATIVE_XPATH_CACHE =
XPath prefers libleptris' native C engine (three orders of
magnitude faster than the Ruby engine) for document-context
queries, with a conservative gate: the Ruby engine handles
element-context evaluation (native relative-context
semantics are not contract-verified), variable references,
and expressions whose results are attribute nodes (native
returns those without name accessors). Both engines resolve
expression prefixes against document-declared namespaces.
XPath::Cache.new(100)
- NATIVE_GATE_CACHE =
The gate decision is expression-intrinsic; re-walking the
cached AST three times per xpath call cost ~23% of repeated
selective queries, so the boolean caches beside it.
XPath::Cache.new(100)
- PARENT_AXIS_CACHE =
Root-context queries alone take the parent-axis guard; the
AST walk is per-expression, so the verdict caches too.
XPath::Cache.new(100)
Constants included
from Serialize
Serialize::ELEMENT_DEFAULT_KWARGS, Serialize::ELEMENT_EXPAND_KWARGS, Serialize::EMPTY_ELEMENT_RE, Serialize::EXPAND_EMPTY_NATIVE, Serialize::LITERAL_REGIONS, Serialize::RAW_AMP_GUARD_ACTIVE, Serialize::RAW_AMP_RE, Serialize::RAW_LT_GUARD_ACTIVE, Serialize::RAW_LT_RE, Serialize::RAW_LT_TRIGGER_RE, Serialize::TRAILING_NL_STRIP_ACTIVE
DocumentParts::DOCUMENT_ATTACHMENT_KEYS, DocumentParts::DOCUMENT_CHILD_PTRS
Constants included
from Materialize
Materialize::EMPTY_FIELDS
Constants included
from XmlUtils
XmlUtils::VALID_ELEMENT_NAMES
Class Attribute Summary collapse
Class Method Summary
collapse
-
.actual_native(child_native, parent_native) ⇒ Object
-
.add_child(parent, child) ⇒ Object
-
.add_next_sibling(node, new_node) ⇒ Object
-
.add_previous_sibling(node, new_node) ⇒ Object
-
.adopt_for_sibling_insert(new_node, anchor) ⇒ Object
The binding's add_*_ sibling methods lift an adopted element's namespace declarations into the target scope; the raw-FFI path above must preserve that.
-
.ast_contains_type?(ast, type) ⇒ Boolean
-
.at_xpath(node, expression, namespaces = {}) ⇒ Object
-
.attachments ⇒ Object
-
.attr_matches?(attr_name) ⇒ Boolean
-
.attribute_element(attr) ⇒ Object
-
.attribute_name(attr) ⇒ Object
-
.attribute_pairs(element) ⇒ Object
One C crossing per element on bindings with the bulk face (leptris-ruby#278): interned names, fresh values, zero Attr objects.
-
.attributes(element) ⇒ Object
-
.bare_attr_value(element, name) ⇒ Object
Fast bare-name read: the binding call plus marker restoration when the document carries them (entity-free documents — the common case — skip the scan; parentless iterparse elements are never bearing).
-
.bare_get_qname_safe? ⇒ Boolean
-
.bare_set_qname_safe? ⇒ Boolean
-
.bulk_binding_children(node) ⇒ Object
The binding-node twin of bulk_native_children (identity via the binding's wrap cache).
-
.bulk_child_buffers(ptr) ⇒ Object
Shared count-then-copy fetch over moxml's grow-to-fit thread-local scratch — the layer above the binding's (buggy on 1.9.163.x) own buffers.
-
.bulk_native_children(node) ⇒ Object
Bulk child fetch with moxml's own grow-to-fit thread-local scratch: count first (exact), size the buffers to it, wrap each pointer as a native-layer node.
-
.bump_serialize_generation ⇒ Object
-
.canonical_native(doc, node) ⇒ Object
Native twin for a binding node through the document's address-keyed native cache — minting on miss so every accessor converges on one native per node (issue #219).
-
.cdata_content(node) ⇒ Object
-
.children(node, entity_bearing: false) ⇒ Object
-
.comment_content(node) ⇒ Object
-
.contains_parent_axis?(ast) ⇒ Boolean
-
.create_document(_native_doc = nil) ⇒ Object
-
.create_native_cdata(content, owner_doc = nil) ⇒ Object
-
.create_native_comment(content, owner_doc = nil) ⇒ Object
-
.create_native_declaration(version, encoding, standalone) ⇒ Object
-
.create_native_doctype(name, external_id, system_id) ⇒ Object
-
.create_native_element(name, owner_doc = nil) ⇒ Object
Native builder factories: one C call and a TypedData wrap.
-
.create_native_entity_reference(name, owner_doc = nil) ⇒ Object
-
.create_native_namespace(element, prefix, uri) ⇒ Object
-
.create_native_processing_instruction(target, content) ⇒ Object
-
.create_native_text(content, owner_doc = nil) ⇒ Object
-
.declaration_attribute(declaration, attr_name) ⇒ Object
-
.digest(node, drop_ws_text: false) ⇒ Object
Subtree digest over the binding node (issue #173).
-
.doc_for(node) ⇒ Object
Binding document for a NativeNode subtree: climb parents to the root (C-bound reads) and look the doc up in the root registry.
-
.doctype_external_id(node) ⇒ Object
-
.doctype_name(node) ⇒ Object
-
.doctype_system_id(node) ⇒ Object
-
.document(node) ⇒ Object
-
.duplicate_node(node) ⇒ Object
-
.engine_xpath(node, expression, namespaces = {}) ⇒ Object
-
.entity_reference_name(node) ⇒ Object
-
.expanded_attr_reads? ⇒ Boolean
Expanded-name (URI + local) attribute VALUE lookup on the engine — the same match rule as the resolver (xmlns declarations invisible, no-namespace never matches a prefixed name), without materializing the attribute list.
-
.expanded_attr_value(element, uri, local) ⇒ Object
-
.expression_uses_parent_axis?(expression) ⇒ Boolean
-
.get_attribute(element, name) ⇒ Object
-
.get_attribute_value(element, name) ⇒ Object
-
.has_declaration?(native_doc, _wrapper) ⇒ Boolean
-
.inner_text(node) ⇒ Object
-
.iterparse(xml, mode = :top_level, _context = nil, &block) ⇒ Object
-
.iterparse_file(path, mode = :top_level, _context = nil, &block) ⇒ Object
-
.line_number(node) ⇒ Object
-
.namespace(node) ⇒ Object
-
.namespace_definitions(element) ⇒ Object
-
.namespace_prefix(namespace) ⇒ Object
-
.namespace_uri(namespace) ⇒ Object
-
.native_c14n_byte_safe? ⇒ Boolean
Native C14N delegation probe: the engine's C14N must byte-match the Ruby reference (ported from canon) on a namespace-sorting + attributes + comments shape before Moxml::C14n hands the default path to it.
-
.native_context_node?(node) ⇒ Boolean
-
.native_expression?(expression) ⇒ Boolean
Document-context queries without variable references, attribute-node results, or the nokogiri-compat xmlns: reserved prefix convention.
-
.native_identity_stable? ⇒ Boolean
-
.native_inclusive10(native) ⇒ Object
-
.native_keep_entity_refs?(node) ⇒ Boolean
-
.native_xpath(node, expression, namespaces, first_only: false) ⇒ Array, ...
Native results, or nil when the query must run on the Ruby engine.
-
.next_sibling(node) ⇒ Object
-
.node_name(node) ⇒ Object
-
.node_type(node) ⇒ Object
-
.parent(node) ⇒ Object
-
.parse(xml, options = {}, _context = nil) ⇒ Object
-
.parse_errors(native_doc) ⇒ Object
-
.parse_flags(options, context: nil) ⇒ Object
nil when no parse flag is requested — the binding treats a nil options hash as plain defaults (matching libxml2/ Nokogiri semantics: blanks kept, no ATTLIST defaults).
-
.parse_fragment(xml, _context = nil) ⇒ Object
Tolerant HTML4/5 parsing into the standard DOM (engine leptris/leptris#659): implied end tags, void elements, raw-text script/style, case-insensitive lowercased names, the HTML named-entity table, synthesized html/head/body.
-
.parse_html(html, _options = {}, _context = nil) ⇒ Object
-
.plan_rows(native) ⇒ Object
-
.plan_structs(native, spec) ⇒ Object
-
.prefix_part(name) ⇒ Object
-
.prefixed_attribute_test?(ast, parent_axis = nil) ⇒ Boolean
Released native engines 1.9.37–1.9.39 do not match prefixed attribute tests inside predicates (@p:kind='a'); the Ruby engine does.
-
.previous_sibling(node) ⇒ Object
-
.processing_instruction_content(node) ⇒ Object
-
.processing_instruction_target(node) ⇒ Object
-
.record_native_doc(root_native, doc) ⇒ Object
-
.remove(node) ⇒ Object
-
.remove_attribute(element, name) ⇒ Object
-
.remove_attribute_native(attr) ⇒ Object
-
.remove_declaration(native_doc) ⇒ Object
-
.replace(node, new_node) ⇒ Object
-
.replace_children(node, new_children) ⇒ Object
-
.resolve_prefix_ns(owner, prefix) ⇒ Object
Nearest in-scope declaration of prefix, walking owner then ancestors.
-
.root(document) ⇒ Object
-
.root_parent(node) ⇒ Object
The binding reports the root element as parentless; the moxml contract roots at the document.
-
.same_node?(one, other) ⇒ Boolean
Protocol-level node equality: one engine can hand out two wrapper classes over the same C node (native layer + binding), so raw == is not identity across the seam.
-
.sax_parse(xml, handler) ⇒ Object
-
.selects_attribute_results?(ast) ⇒ Boolean
-
.set_attribute(element, name, value) ⇒ Object
-
.set_attribute_name(attr, name) ⇒ Object
-
.set_attribute_namespace(attr, namespace) ⇒ Object
Attr is an immutable value object: namespace changes go through the owning element, recreating the attribute with a qualified name, and yield a fresh native for the wrapper.
-
.set_attribute_value(attr, value) ⇒ Object
-
.set_cdata_content(node, content) ⇒ Object
-
.set_comment_content(node, content) ⇒ Object
-
.set_declaration_attribute(declaration, attr_name, value) ⇒ Object
-
.set_namespace(node, namespace) ⇒ Object
-
.set_node_name(node, name) ⇒ Object
-
.set_processing_instruction_content(node, content) ⇒ Object
-
.set_root(doc, element) ⇒ Object
-
.set_text_content(node, content) ⇒ Object
-
.sibling_walk_children(node) ⇒ Object
Fallback for natives without a registered document: walk first_child/next_sibling at the FFI level and wrap binding nodes over the raw pointers.
-
.source_position(node) ⇒ Object
-
.strip_blank_text_nodes!(doc) ⇒ Object
libxml2's XML_PARSE_NOBLANKS semantics (issues #153/#156): drop WHOLLY-whitespace text nodes; the boundary spaces of text-bearing nodes stay (mixed content cannot be re-indented).
-
.strip_blanks_under(ptr, binding_ffi, buf, capacity) ⇒ Object
-
.text_content(node) ⇒ Object
-
.to_binding(node) ⇒ Object
native -> bridged binding node.
-
.uses_xmlns_prefix?(ast) ⇒ Boolean
xmlns:name is a nokogiri-compat convention addressing elements in the default namespace; only the Ruby engine implements it.
-
.walk_descendants(native, context) ⇒ Object
One C visit walk (TODO.perf/27: post-order/abort state in the C callback, rb_yield direct — no FFI closure per node) instead of per-level children walks.
-
.wrap_native(node, type, _context) ⇒ Object
-
.xpath(node, expression, namespaces = {}) ⇒ Object
Methods included from Serialize
native_expand?, next_literal_region, normalize_markup, normalize_serialization, opener_at_offset, raw_serialize, serialize
add_document_child, assemble_document_children, default_declaration_xml, document_has_declaration?, fast_document_output, format_internal_subset, free_document, marker_text_for, markup_decl_end, mirror_declaration_native, native_doctype_xml, serialize_document
Methods included from Markers
entity_bearing?, split_entity_markers
bulk_materialize?, emit_element_fields, materialize_fields, walk_fields
Methods inherited from Base
bulk_materialize?, children_accepts_entity_flag?, create_cdata, create_comment, create_declaration, create_doctype, create_element, create_entity_reference, create_namespace, create_processing_instruction, create_text, decode_entities, entity_bearing?, free_document, in_scope_namespaces, materialize_fields, patch_node, patches_children?, preprocess_entities, restore_entities, sax_supported?, wrappers_recyclable?
Methods included from XmlUtils
#encode_entities, #normalize_xml_value, #validate_comment_content, #validate_declaration_encoding, #validate_declaration_standalone, #validate_declaration_version, #validate_element_name, #validate_entity_reference_name, #validate_pi_target, #validate_prefix, #validate_uri
Class Attribute Details
.serialize_generation ⇒ Object
Returns the value of attribute serialize_generation.
418
419
420
|
# File 'lib/moxml/adapter/leptris.rb', line 418
def serialize_generation
@serialize_generation
end
|
Class Method Details
.actual_native(child_native, parent_native) ⇒ Object
1476
1477
1478
1479
1480
1481
1482
1483
|
# File 'lib/moxml/adapter/leptris.rb', line 1476
def actual_native(child_native, parent_native)
if NATIVE_DOC_PARTS && child_native.is_a?(CustomizedLeptris::Doctype) &&
parent_native.is_a?(::Leptris::XML::Document)
attached = attachments.get(parent_native, :doctype)
return attached if attached.is_a?(::Leptris::XML::DocType)
end
child_native
end
|
.add_child(parent, child) ⇒ Object
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
|
# File 'lib/moxml/adapter/leptris.rb', line 1485
def add_child(parent, child)
if NATIVE_READ_LAYER && !(NATIVE_MUTATIONS_COHERENT &&
parent.is_a?(::Leptris::XML::NativeNode) &&
child.is_a?(::Leptris::XML::NativeNode))
parent = to_binding(parent)
child = to_binding(child)
end
case parent
when ::Leptris::XML::Document then add_document_child(parent, child)
else
if child.is_a?(CustomizedLeptris::EntityReference)
marker = parent.document.create_text_node("#{Entity::MARKER}#{child.name};")
parent.add_child(marker)
attachments.set(parent.document, :entity_markers, true)
bump_serialize_generation
return child
end
if NATIVE_MUTATIONS_COHERENT && parent.is_a?(NN) && child.is_a?(NN)
doc = NN_DOCUMENT.bind_call(parent)
child = doc.create_text_node(child) if child.is_a?(String)
return NN_ADD_CHILD.bind_call(parent, child)
end
child = parent.document.create_text_node(child) if child.is_a?(String)
parent.add_child(child)
end
end
|
.add_next_sibling(node, new_node) ⇒ Object
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
|
# File 'lib/moxml/adapter/leptris.rb', line 1538
def add_next_sibling(node, new_node)
if NATIVE_READ_LAYER
node = to_binding(node)
new_node = to_binding(new_node)
end
return node.add_next_sibling(new_node) if node.is_a?(::Leptris::XML::Element)
adopt_for_sibling_insert(new_node, node)
::Leptris::XML::FFI.check_status(
::Leptris::XML::FFI.leptris_element_insert_after(
node.c_ptr, new_node.c_ptr
),
)
new_node
end
|
.add_previous_sibling(node, new_node) ⇒ Object
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
|
# File 'lib/moxml/adapter/leptris.rb', line 1513
def add_previous_sibling(node, new_node)
if NATIVE_READ_LAYER
node = to_binding(node)
new_node = to_binding(new_node)
end
if new_node.is_a?(::Leptris::XML::ProcessingInstruction) &&
node.document&.root == node
node.document.add_pi(new_node.target, new_node.content.to_s)
return new_node
end
return node.add_previous_sibling(new_node) if node.is_a?(::Leptris::XML::Element)
adopt_for_sibling_insert(new_node, node)
::Leptris::XML::FFI.check_status(
::Leptris::XML::FFI.leptris_element_insert_before(
node.c_ptr, new_node.c_ptr
),
)
new_node
end
|
.adopt_for_sibling_insert(new_node, anchor) ⇒ Object
The binding's add_*_ sibling methods lift an adopted
element's namespace declarations into the target scope;
the raw-FFI path above must preserve that.
1562
1563
1564
1565
1566
1567
1568
|
# File 'lib/moxml/adapter/leptris.rb', line 1562
def adopt_for_sibling_insert(new_node, anchor)
return unless new_node.is_a?(::Leptris::XML::Element)
return if ::Leptris::XML::Element.skip_adoption_lift?(new_node)
scope = anchor.parent.is_a?(::Leptris::XML::Element) ? anchor.parent.namespaces : {}
::Leptris::XML::Element.lift_namespaces_for_adoption(new_node, scope)
end
|
.ast_contains_type?(ast, type) ⇒ Boolean
1927
1928
1929
1930
1931
1932
1933
|
# File 'lib/moxml/adapter/leptris.rb', line 1927
def ast_contains_type?(ast, type)
return true if ast.type == type
ast.children.any? do |child|
child.is_a?(XPath::AST::Node) && ast_contains_type?(child, type)
end
end
|
.at_xpath(node, expression, namespaces = {}) ⇒ Object
1783
1784
1785
1786
1787
1788
1789
|
# File 'lib/moxml/adapter/leptris.rb', line 1783
def at_xpath(node, expression, namespaces = {})
native = native_xpath(node, expression, namespaces, first_only: true)
return native unless native.nil?
result = engine_xpath(node, expression, namespaces)
result.is_a?(Array) ? result.first : result
end
|
.attachments ⇒ Object
580
581
582
|
# File 'lib/moxml/adapter/leptris.rb', line 580
def attachments
@attachments ||= Moxml::NativeAttachment.new
end
|
.attr_matches?(attr_name) ⇒ Boolean
904
905
906
|
# File 'lib/moxml/adapter/leptris.rb', line 904
def attr_matches?(attr_name)
%w[version encoding standalone].include?(attr_name.to_s)
end
|
.attribute_element(attr) ⇒ Object
1424
1425
1426
|
# File 'lib/moxml/adapter/leptris.rb', line 1424
def attribute_element(attr)
attr.element
end
|
.attribute_name(attr) ⇒ Object
1428
1429
1430
1431
1432
1433
1434
|
# File 'lib/moxml/adapter/leptris.rb', line 1428
def attribute_name(attr)
return attr.name.split(":", 2)[1] if attr.name.include?(":")
attr.name.to_s.dup.force_encoding("UTF-8")
end
|
.attribute_pairs(element) ⇒ Object
One C crossing per element on bindings with the bulk face
(leptris-ruby#278): interned names, fresh values, zero
Attr objects. Face-probed, not version-gated — the floor
(1.9.194.1) admits bindings without it, which take the
base default over #attributes.
1417
1418
1419
1420
1421
1422
|
# File 'lib/moxml/adapter/leptris.rb', line 1417
def attribute_pairs(element)
binding_element = to_binding(element)
return super unless binding_element.respond_to?(:attribute_pairs)
binding_element.attribute_pairs
end
|
.attributes(element) ⇒ Object
1407
1408
1409
1410
|
# File 'lib/moxml/adapter/leptris.rb', line 1407
def attributes(element)
element = to_binding(element)
element.attribute_nodes
end
|
.bare_attr_value(element, name) ⇒ Object
Fast bare-name read: the binding call plus marker
restoration when the document carries them (entity-free
documents — the common case — skip the scan; parentless
iterparse elements are never bearing).
Raw qualified-name read; the entity-restore decision is
the wrapper's (Element#[] rides its generation memo — the
document-plus-attachment probe here cost more than the
read itself).
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
|
# File 'lib/moxml/adapter/leptris.rb', line 631
def bare_attr_value(element, name)
value = if NATIVE_READ_LAYER && element.is_a?(NN)
NN_ATTRIBUTE.bind_call(element, name.to_s)
else
element[name.to_s]
end
if !NATIVE_STRINGS_UTF8 && NATIVE_READ_LAYER &&
element.is_a?(::Leptris::XML::NativeNode) &&
value.is_a?(String)
return value.dup.force_encoding(Encoding::UTF_8)
end
value
end
|
.bare_get_qname_safe? ⇒ Boolean
592
593
594
|
# File 'lib/moxml/adapter/leptris.rb', line 592
def bare_get_qname_safe?
true
end
|
.bare_set_qname_safe? ⇒ Boolean
588
589
590
|
# File 'lib/moxml/adapter/leptris.rb', line 588
def bare_set_qname_safe?
true
end
|
.bulk_binding_children(node) ⇒ Object
The binding-node twin of bulk_native_children (identity via
the binding's wrap cache).
1278
1279
1280
1281
1282
1283
1284
1285
1286
|
# File 'lib/moxml/adapter/leptris.rb', line 1278
def bulk_binding_children(node)
copied, pointers = bulk_child_buffers(node.c_ptr)
return node.children.to_a if copied.zero?
doc = node.document
Array.new(copied) do |i|
::Leptris::XML::Node.wrap(pointers.get_pointer(i * 8), doc)
end
end
|
.bulk_child_buffers(ptr) ⇒ Object
Shared count-then-copy fetch over moxml's grow-to-fit
thread-local scratch — the layer above the binding's
(buggy on 1.9.163.x) own buffers. Returns [copied, pointers].
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
|
# File 'lib/moxml/adapter/leptris.rb', line 1319
def bulk_child_buffers(ptr)
total = ::Leptris::XML::FFI.leptris_node_children_ex(ptr, nil, nil, 0)
return [0, nil] if total.zero?
scratch = (Thread.current[:moxml_leptris_children] ||= {})
pointers = scratch[:pointers]
if pointers.nil? || pointers.size / 8 < total
pointers&.free
pointers = scratch[:pointers] =
::FFI::MemoryPointer.new(:pointer, total)
end
kinds = scratch[:kinds]
if kinds.nil? || kinds.size / 4 < total
kinds&.free
kinds = scratch[:kinds] = ::FFI::MemoryPointer.new(:int, total)
end
copied = ::Leptris::XML::FFI.leptris_node_children_ex(
ptr, pointers, kinds, total
)
[copied, pointers]
end
|
.bulk_native_children(node) ⇒ Object
Bulk child fetch with moxml's own grow-to-fit thread-local
scratch: count first (exact), size the buffers to it, wrap
each pointer as a native-layer node. Correct on every
binding version — the binding's own scratch truncates at
512 on 1.9.163.x (leptris-ruby#202).
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
|
# File 'lib/moxml/adapter/leptris.rb', line 1293
def bulk_native_children(node)
copied, pointers = bulk_child_buffers(::FFI::Pointer.new(node.address))
return [] if copied.zero?
doc = doc_for(node)
if doc.nil?
return sibling_walk_children(node)
end
cache = doc.native_cache
Array.new(copied) do |i|
child_ptr = pointers.get_pointer(i * 8)
cache[child_ptr.address] ||=
::Leptris::XML::NativeNode.from(doc, child_ptr)
end
end
|
.bump_serialize_generation ⇒ Object
422
423
424
|
# File 'lib/moxml/adapter/leptris.rb', line 422
def self.bump_serialize_generation
@serialize_generation += 1
end
|
.canonical_native(doc, node) ⇒ Object
Native twin for a binding node through the document's
address-keyed native cache — minting on miss so every
accessor converges on one native per node (issue #219).
Binding natives (Attribute/Attr, synthetic wrappers) and
docless nodes pass through unchanged.
1268
1269
1270
1271
1272
1273
1274
|
# File 'lib/moxml/adapter/leptris.rb', line 1268
def canonical_native(doc, node)
return node unless node.is_a?(::Leptris::XML::Element)
cache = doc.native_cache
cache[node.c_ptr.address] ||=
::Leptris::XML::NativeNode.from(doc, node.c_ptr)
end
|
.cdata_content(node) ⇒ Object
1693
1694
1695
1696
1697
|
# File 'lib/moxml/adapter/leptris.rb', line 1693
def cdata_content(node)
return NN_CONTENT.bind_call(node) if NATIVE_READ_LAYER && node.is_a?(NN)
node.content
end
|
.children(node, entity_bearing: false) ⇒ Object
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
|
# File 'lib/moxml/adapter/leptris.rb', line 1142
def children(node, entity_bearing: false)
if NATIVE_READ_LAYER && NATIVE_ENTITY_REFS &&
native_keep_entity_refs?(node)
return to_binding(node).children.to_a
end
if NATIVE_READ_LAYER && node.is_a?(::Leptris::XML::NativeNode)
natives = if NATIVE_BULK_FIXED
NN_CHILDREN.bind_call(node).to_a
else
bulk_native_children(node)
end
return natives unless entity_bearing
doc = doc_for(node)
bound_children = natives.map { |child| to_binding(child) }
if doc && attachments.get(doc, :entity_markers) == false
return bound_children
end
return split_entity_markers(bound_children, to_binding(node))
end
case node
when ::Leptris::XML::Element
natives = node.children.to_a
if NATIVE_READ_LAYER && natives.size == 512
natives = bulk_binding_children(node)
end
return natives if node.document.nil? ||
attachments.get(node.document, :entity_markers) == false
split_entity_markers(natives, node)
when ::Leptris::XML::Document
assemble_document_children(node)
when CustomizedLeptris::Declaration, CustomizedLeptris::Doctype,
CustomizedLeptris::EntityReference, CustomizedLeptris::TextSegment,
CustomizedLeptris::DocumentPI,
::Leptris::XML::Text, ::Leptris::XML::,
::Leptris::XML::CDATA, ::Leptris::XML::ProcessingInstruction,
::Leptris::XML::Attr
[]
else
node.children.to_a
end
end
|
1704
1705
1706
1707
1708
|
# File 'lib/moxml/adapter/leptris.rb', line 1704
def (node)
return NN_CONTENT.bind_call(node) if NATIVE_READ_LAYER && node.is_a?(NN)
node.content
end
|
.contains_parent_axis?(ast) ⇒ Boolean
1896
1897
1898
1899
1900
1901
1902
1903
|
# File 'lib/moxml/adapter/leptris.rb', line 1896
def contains_parent_axis?(ast)
return true if ast.type == :parent
return true if ast.type == :axis && ast.children.first == "parent"
ast.children.any? do |child|
child.is_a?(XPath::AST::Node) && contains_parent_axis?(child)
end
end
|
.create_document(_native_doc = nil) ⇒ Object
835
836
837
|
# File 'lib/moxml/adapter/leptris.rb', line 835
def create_document(_native_doc = nil)
::Leptris::XML::Document.create
end
|
.create_native_cdata(content, owner_doc = nil) ⇒ Object
860
861
862
|
# File 'lib/moxml/adapter/leptris.rb', line 860
def create_native_cdata(content, owner_doc = nil)
(owner_doc || create_document).create_cdata(content)
end
|
864
865
866
|
# File 'lib/moxml/adapter/leptris.rb', line 864
def (content, owner_doc = nil)
(owner_doc || create_document).(content)
end
|
.create_native_declaration(version, encoding, standalone) ⇒ Object
877
878
879
|
# File 'lib/moxml/adapter/leptris.rb', line 877
def create_native_declaration(version, encoding, standalone)
CustomizedLeptris::Declaration.new(version, encoding, standalone)
end
|
.create_native_doctype(name, external_id, system_id) ⇒ Object
873
874
875
|
# File 'lib/moxml/adapter/leptris.rb', line 873
def create_native_doctype(name, external_id, system_id)
CustomizedLeptris::Doctype.new(name, external_id, system_id)
end
|
.create_native_element(name, owner_doc = nil) ⇒ Object
Native builder factories: one C call and a TypedData wrap.
Adopted from 1.9.163.5, where native mutations became
version-coherent (leptris-ruby#204/#208) — the earlier
rejection (stale binding memos + per-attach bridge cost)
no longer applies: add_child carries a native fast path.
844
845
846
847
848
849
850
|
# File 'lib/moxml/adapter/leptris.rb', line 844
def create_native_element(name, owner_doc = nil)
if NATIVE_MUTATIONS_COHERENT && owner_doc
return owner_doc.native_create_element(name.to_s)
end
(owner_doc || create_document).create_element(name.to_s)
end
|
.create_native_entity_reference(name, owner_doc = nil) ⇒ Object
881
882
883
884
885
886
887
|
# File 'lib/moxml/adapter/leptris.rb', line 881
def create_native_entity_reference(name, owner_doc = nil)
if NATIVE_ENTITY_REFS && owner_doc
return owner_doc.create_entity_reference(name.to_s)
end
CustomizedLeptris::EntityReference.new(name)
end
|
.create_native_namespace(element, prefix, uri) ⇒ Object
924
925
926
927
|
# File 'lib/moxml/adapter/leptris.rb', line 924
def create_native_namespace(element, prefix, uri)
element = to_binding(element) if NATIVE_READ_LAYER
element.add_namespace_definition(prefix, uri)
end
|
.create_native_processing_instruction(target, content) ⇒ Object
868
869
870
871
|
# File 'lib/moxml/adapter/leptris.rb', line 868
def create_native_processing_instruction(target, content)
doc = create_document
doc.create_processing_instruction(target.to_s, content.to_s)
end
|
.create_native_text(content, owner_doc = nil) ⇒ Object
852
853
854
855
856
857
858
|
# File 'lib/moxml/adapter/leptris.rb', line 852
def create_native_text(content, owner_doc = nil)
if NATIVE_MUTATIONS_COHERENT && owner_doc
return owner_doc.native_create_text(content)
end
(owner_doc || create_document).create_text_node(content)
end
|
.declaration_attribute(declaration, attr_name) ⇒ Object
896
897
898
|
# File 'lib/moxml/adapter/leptris.rb', line 896
def declaration_attribute(declaration, attr_name)
declaration.public_send(attr_name) if attr_matches?(attr_name)
end
|
.digest(node, drop_ws_text: false) ⇒ Object
Subtree digest over the binding node (issue #173). Only
binding Nodes carry a C node handle: the synthetic
wrappers (declarations, doctypes, entity markers) and the
lightweight Attr triples answer nil.
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
|
# File 'lib/moxml/adapter/leptris.rb', line 1090
def digest(node, drop_ws_text: false)
return nil unless DIGEST_SUPPORTED
if NATIVE_READ_LAYER && node.is_a?(::Leptris::XML::NativeNode)
return to_binding(node).digest(drop_ws: drop_ws_text)
end
return nil unless node.is_a?(::Leptris::XML::Node) &&
!node.is_a?(::Leptris::XML::ResultAttr)
node.digest(drop_ws: drop_ws_text)
end
|
.doc_for(node) ⇒ Object
Binding document for a NativeNode subtree: climb parents
to the root (C-bound reads) and look the doc up in the
root registry. Detached subtrees answer nil.
365
366
367
368
369
370
371
372
373
|
# File 'lib/moxml/adapter/leptris.rb', line 365
def doc_for(node)
return NN_DOCUMENT.bind_call(node) if NATIVE_MUTATIONS_COHERENT
current = node
current = current.parent while current&.parent
@native_doc_roots[current]
end
|
.doctype_external_id(node) ⇒ Object
1750
1751
1752
|
# File 'lib/moxml/adapter/leptris.rb', line 1750
def doctype_external_id(node)
node.is_a?(CustomizedLeptris::Doctype) ? node.external_id : node.public_id
end
|
.doctype_name(node) ⇒ Object
1746
1747
1748
|
# File 'lib/moxml/adapter/leptris.rb', line 1746
def doctype_name(node)
node.is_a?(CustomizedLeptris::Doctype) ? node.name : node.root_name
end
|
.doctype_system_id(node) ⇒ Object
1754
1755
1756
|
# File 'lib/moxml/adapter/leptris.rb', line 1754
def doctype_system_id(node)
node.system_id
end
|
.document(node) ⇒ Object
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
|
# File 'lib/moxml/adapter/leptris.rb', line 1363
def document(node)
return doc_for(node) if NATIVE_READ_LAYER &&
node.is_a?(::Leptris::XML::NativeNode)
case node
when ::Leptris::XML::Document then node
when CustomizedLeptris::Declaration, CustomizedLeptris::Doctype then node.parent_doc
else node.document
end
end
|
.duplicate_node(node) ⇒ Object
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
|
# File 'lib/moxml/adapter/leptris.rb', line 1126
def duplicate_node(node)
node = to_binding(node) if NATIVE_READ_LAYER
case node
when CustomizedLeptris::Declaration
CustomizedLeptris::Declaration.new(node.version, node.encoding, node.standalone)
when CustomizedLeptris::Doctype
CustomizedLeptris::Doctype.new(node.name, node.external_id, node.system_id)
when CustomizedLeptris::EntityReference
CustomizedLeptris::EntityReference.new(node.name)
when CustomizedLeptris::DocumentPI
CustomizedLeptris::DocumentPI.new(node.target, node.data, node.parent_doc)
else
node.dup
end
end
|
.engine_xpath(node, expression, namespaces = {}) ⇒ Object
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
|
# File 'lib/moxml/adapter/leptris.rb', line 1978
def engine_xpath(node, expression, namespaces = {})
unless node.is_a?(Moxml::Node)
node = Moxml::Node.wrap(node, Context.new(:leptris))
end
ast = XPath::Parser.parse(expression)
proc = XPath::Compiler.compile_with_cache(ast, namespaces: namespaces)
result = proc.call(node)
case result
when Array, NodeSet
nodes = result.is_a?(NodeSet) ? result.to_a : result
seen = {}.compare_by_identity
nodes.map { |n| n.is_a?(Moxml::Node) ? n.native : n }
.select do |native|
if seen.key?(native)
false
else
seen[native] = true
true
end
end
else
result
end
end
|
.entity_reference_name(node) ⇒ Object
889
890
891
892
893
894
|
# File 'lib/moxml/adapter/leptris.rb', line 889
def entity_reference_name(node)
return node.name if NATIVE_ENTITY_REFS &&
node.is_a?(::Leptris::XML::EntityReference)
node.name if node.is_a?(CustomizedLeptris::EntityReference)
end
|
.expanded_attr_reads? ⇒ Boolean
Expanded-name (URI + local) attribute VALUE lookup on the
engine — the same match rule as the resolver (xmlns
declarations invisible, no-namespace never matches a
prefixed name), without materializing the attribute list.
600
601
602
|
# File 'lib/moxml/adapter/leptris.rb', line 600
def expanded_attr_reads?
true
end
|
.expanded_attr_value(element, uri, local) ⇒ Object
613
614
615
616
617
618
619
620
621
|
# File 'lib/moxml/adapter/leptris.rb', line 613
def expanded_attr_value(element, uri, local)
to_binding(element).attribute_ns(uri, local)
end
|
.expression_uses_parent_axis?(expression) ⇒ Boolean
1889
1890
1891
1892
1893
1894
|
# File 'lib/moxml/adapter/leptris.rb', line 1889
def expression_uses_parent_axis?(expression)
ast = XPath::Parser.parse_with_cache(expression)
contains_parent_axis?(ast)
rescue XPath::SyntaxError
true
end
|
.get_attribute(element, name) ⇒ Object
1456
1457
1458
1459
|
# File 'lib/moxml/adapter/leptris.rb', line 1456
def get_attribute(element, name)
element = to_binding(element)
element.attribute_nodes.find { |attr| attr.name == name.to_s }
end
|
.get_attribute_value(element, name) ⇒ Object
1461
1462
1463
1464
|
# File 'lib/moxml/adapter/leptris.rb', line 1461
def get_attribute_value(element, name)
element = to_binding(element)
element[name.to_s]
end
|
.has_declaration?(native_doc, _wrapper) ⇒ Boolean
909
910
911
912
913
914
|
# File 'lib/moxml/adapter/leptris.rb', line 909
def has_declaration?(native_doc, _wrapper)
return true if attachments.get(native_doc, :declaration)
attachments.key?(native_doc, :had_source_declaration) &&
attachments.get(native_doc, :had_source_declaration)
end
|
.inner_text(node) ⇒ Object
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
|
# File 'lib/moxml/adapter/leptris.rb', line 1668
def inner_text(node)
node = to_binding(node) if NATIVE_READ_LAYER
children(node).filter_map do |child|
case child
when CustomizedLeptris::EntityReference then "&#{child.name};"
when ::Leptris::XML::CDATA then child.content
when ::Leptris::XML::Text, CustomizedLeptris::TextSegment
child.content.to_s.dup.force_encoding("UTF-8")
end
end.join
end
|
.iterparse(xml, mode = :top_level, _context = nil, &block) ⇒ Object
746
747
748
749
750
751
752
753
|
# File 'lib/moxml/adapter/leptris.rb', line 746
def iterparse(xml, mode = :top_level, _context = nil, &block)
raise ArgumentError, "iterparse requires a block" unless block
ctx = _context || Context.new(:leptris)
::Leptris::XML::Iterparse.parse(xml, mode: mode) do |element|
yield(Node.wrap(element, ctx))
end
end
|
.iterparse_file(path, mode = :top_level, _context = nil, &block) ⇒ Object
755
756
757
758
759
760
761
762
|
# File 'lib/moxml/adapter/leptris.rb', line 755
def iterparse_file(path, mode = :top_level, _context = nil, &block)
raise ArgumentError, "iterparse_file requires a block" unless block
ctx = _context || Context.new(:leptris)
::Leptris::XML::Iterparse.parse_file(path, mode: mode) do |element|
yield(Node.wrap(element, ctx))
end
end
|
.line_number(node) ⇒ Object
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
|
# File 'lib/moxml/adapter/leptris.rb', line 1395
def line_number(node)
if NATIVE_MUTATIONS_COHERENT &&
node.is_a?(::Leptris::XML::NativeNode)
line = node.line
return line.nil? || line.zero? ? nil : line
end
return nil unless node.is_a?(::Leptris::XML::Node)
line = node.line
line.zero? ? nil : line
end
|
.namespace(node) ⇒ Object
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
|
# File 'lib/moxml/adapter/leptris.rb', line 1000
def namespace(node)
node = to_binding(node) if NATIVE_READ_LAYER
if node.is_a?(::Leptris::XML::Attr) &&
(prefix = node.prefix || prefix_part(node.name))
resolved = resolve_prefix_ns(node.element, prefix)
return resolved if resolved
end
ns = node.namespace
return ns unless ns.nil?
owner = node.is_a?(::Leptris::XML::Attr) ? node.element : node
prefix = if node.is_a?(::Leptris::XML::Attr)
node.prefix || prefix_part(node.name)
else
prefix_part(node.name)
end
return nil if prefix.nil?
resolve_prefix_ns(owner, prefix)
end
|
.namespace_definitions(element) ⇒ Object
1741
1742
1743
1744
|
# File 'lib/moxml/adapter/leptris.rb', line 1741
def namespace_definitions(element)
element = to_binding(element) if NATIVE_READ_LAYER
element.namespace_definitions
end
|
.namespace_prefix(namespace) ⇒ Object
1726
1727
1728
1729
1730
1731
1732
1733
|
# File 'lib/moxml/adapter/leptris.rb', line 1726
def namespace_prefix(namespace)
return nil if namespace.is_a?(String)
namespace.prefix
end
|
.namespace_uri(namespace) ⇒ Object
1735
1736
1737
1738
1739
|
# File 'lib/moxml/adapter/leptris.rb', line 1735
def namespace_uri(namespace)
return namespace if namespace.is_a?(String)
namespace.href
end
|
.native_c14n_byte_safe? ⇒ Boolean
Native C14N delegation probe: the engine's C14N must
byte-match the Ruby reference (ported from canon) on a
namespace-sorting + attributes + comments shape before
Moxml::C14n hands the default path to it. The engine
currently emits namespace declarations in document order
instead of lexicographic (filed leptris/leptris#881); the
probe auto-adopts the native path once a fixed build lands —
no moxml release needed.
Lazy, not load-time: the probe exercises the wrapper layer
(Document/serialize/C14n), which is circular while THIS
adapter file is still loading — the load-time form rescued
to false on every build and masked a landed engine fix.
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
|
# File 'lib/moxml/adapter/leptris.rb', line 394
def self.native_c14n_byte_safe?
return @native_c14n_byte_safe unless @native_c14n_byte_safe.nil?
@native_c14n_byte_safe = begin
probe_xml = %(<?xml version="1.0"?><doc xmlns:p="urn:p" xmlns="urn:d" b="2" a="1"><e p:x="v" z="w">t & u</e><!-- c --></doc>)
native_doc = ::Leptris::XML::Document.parse(probe_xml)
native = native_doc.root.canonicalize(
::Leptris::XML::FFI::C14N_1_0, nil,
mode: ::Leptris::XML::FFI::C14N_MODE_CANONICAL
)
wrapper = Moxml::Wrappers::Document.new(native_doc, Moxml::Context.new(:leptris))
reference = Moxml::C14n::Inclusive10.new.canonicalize(wrapper.root)
native == reference
rescue StandardError
false
end
end
|
.native_context_node?(node) ⇒ Boolean
1880
1881
1882
1883
1884
1885
1886
1887
|
# File 'lib/moxml/adapter/leptris.rb', line 1880
def native_context_node?(node)
return false if node.is_a?(::Leptris::XML::Element) && node.document.nil?
node.is_a?(::Leptris::XML::Document) ||
node.is_a?(::Leptris::XML::Element)
end
|
.native_expression?(expression) ⇒ Boolean
Document-context queries without variable references,
attribute-node results, or the nokogiri-compat xmlns:
reserved prefix convention.
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
|
# File 'lib/moxml/adapter/leptris.rb', line 1908
def native_expression?(expression)
NATIVE_GATE_CACHE.get_or_set(expression) do
ast = XPath::Parser.parse_with_cache(expression)
next false if ast_contains_type?(ast, :variable)
next false if uses_xmlns_prefix?(ast)
next false if !PREFIXED_ATTR_PREDICATES_NATIVE && prefixed_attribute_test?(ast)
next true if selects_attribute_results?(ast) && ATTR_RESULT_NATIVE
!selects_attribute_results?(ast)
end
rescue XPath::SyntaxError
false
end
|
.native_identity_stable? ⇒ Boolean
649
650
651
|
# File 'lib/moxml/adapter/leptris.rb', line 649
def native_identity_stable?
true
end
|
.native_inclusive10(native) ⇒ Object
604
605
606
607
608
609
610
611
|
# File 'lib/moxml/adapter/leptris.rb', line 604
def native_inclusive10(native)
return nil unless native_c14n_byte_safe?
to_binding(native).canonicalize(
::Leptris::XML::FFI::C14N_1_0, nil,
mode: ::Leptris::XML::FFI::C14N_MODE_CANONICAL
)
end
|
.native_keep_entity_refs?(node) ⇒ Boolean
1215
1216
1217
1218
1219
1220
1221
|
# File 'lib/moxml/adapter/leptris.rb', line 1215
def native_keep_entity_refs?(node)
return false unless NATIVE_READ_LAYER &&
node.is_a?(::Leptris::XML::NativeNode)
doc = NN_DOCUMENT.bind_call(node)
attachments.get(doc, :keep_entity_refs) == true
end
|
.native_xpath(node, expression, namespaces, first_only: false) ⇒ Array, ...
Returns native results, or nil when the
query must run on the Ruby engine.
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
|
# File 'lib/moxml/adapter/leptris.rb', line 1793
def native_xpath(node, expression, namespaces, first_only: false)
if NATIVE_READ_LAYER && node.is_a?(::Leptris::XML::NativeNode)
node = to_binding(node)
end
return nil unless native_context_node?(node)
return nil unless native_expression?(expression)
if node.is_a?(::Leptris::XML::Element) &&
node.document&.root.equal?(node) &&
PARENT_AXIS_CACHE.get_or_set(expression) do
expression_uses_parent_axis?(expression)
end
return nil
end
compiled = NATIVE_XPATH_CACHE.get_or_set(expression) do
::Leptris::XML::XPath.compile(expression)
end
document = node.is_a?(::Leptris::XML::Document) ? node : node.document
if namespaces && !namespaces.empty?
result = if first_only
node.at_xpath(expression, namespaces)
else
compiled.eval(node, namespaces)
end
return case result
when ::Leptris::XML::NodeSet
first_only ? result.first : result.extend(Moxml::LazyNodeSet)
else
result
end
end
unless NATIVE_XPATH_SEAMS
result = compiled.eval(node)
return case result
when ::Leptris::XML::NodeSet
first_only ? result.first : result.extend(Moxml::LazyNodeSet)
else
result
end
end
doc_ptr = node.is_a?(::Leptris::XML::Document) ? node.c_ptr : document.c_ptr
context_ptr = node.is_a?(::Leptris::XML::Document) ? nil : node.c_ptr
result_ptr = compiled.eval_ptrs(doc_ptr, context_ptr)
return nil if result_ptr.null?
if first_only
return ::Leptris::XML::Searchable.wrap_xpath_first_result(
document, result_ptr
)
end
result = ::Leptris::XML::Searchable.wrap_xpath_result(
document, result_ptr
)
result.is_a?(::Leptris::XML::NodeSet) ? result.extend(Moxml::LazyNodeSet) : result
rescue ::Leptris::XML::XPathError
nil
end
|
.next_sibling(node) ⇒ Object
1347
1348
1349
1350
1351
1352
|
# File 'lib/moxml/adapter/leptris.rb', line 1347
def next_sibling(node)
return NN_NEXT_SIBLING.bind_call(node) if NATIVE_READ_LAYER &&
node.is_a?(::Leptris::XML::NativeNode)
node.next_sibling if node.is_a?(::Leptris::XML::Node)
end
|
.node_name(node) ⇒ Object
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
|
# File 'lib/moxml/adapter/leptris.rb', line 1101
def node_name(node)
return node.root_name if node.is_a?(::Leptris::XML::DocType)
return node.target if node.is_a?(CustomizedLeptris::DocumentPI)
if NATIVE_READ_LAYER && node.is_a?(::Leptris::XML::NativeNode)
return to_binding(node).target.to_s if NN_NODE_TYPE.bind_call(node) == :pi
name = NN_NAME.bind_call(node)
return name if NATIVE_STRINGS_UTF8
name.dup.force_encoding(Encoding::UTF_8)
end
node.name.to_s.dup.force_encoding("UTF-8")
end
|
.node_type(node) ⇒ Object
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
|
# File 'lib/moxml/adapter/leptris.rb', line 1055
def node_type(node)
if NATIVE_READ_LAYER &&
node.is_a?(::Leptris::XML::NativeNode)
type = NN_NODE_TYPE.bind_call(node)
return :processing_instruction if type == :pi
return type
end
case node
when ::Leptris::XML::Element then :element
when ::Leptris::XML::CDATA then :cdata
when ::Leptris::XML::Text, CustomizedLeptris::TextSegment then :text
when ::Leptris::XML::Attr, ::Leptris::XML::ResultAttr then :attribute
when ::Leptris::XML:: then :comment
when ::Leptris::XML::ProcessingInstruction, CustomizedLeptris::DocumentPI then :processing_instruction
when ::Leptris::XML::Document then :document
when ::Leptris::XML::DocType, CustomizedLeptris::Doctype then :doctype
when CustomizedLeptris::Declaration then :declaration
when CustomizedLeptris::EntityReference then :entity_reference
when ::Leptris::XML::EntityReference then :entity_reference if NATIVE_ENTITY_REFS
else :unknown
end
end
|
.parent(node) ⇒ Object
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
|
# File 'lib/moxml/adapter/leptris.rb', line 1223
def parent(node)
if NATIVE_READ_LAYER && node.is_a?(::Leptris::XML::NativeNode)
parent = NN_PARENT.bind_call(node)
return parent if parent
doc = doc_for(node)
return doc if doc&.root && doc.root.c_ptr.address == node.address
return nil
end
case node
when ::Leptris::XML::Element then root_parent(node)
when ::Leptris::XML::Document then nil
when CustomizedLeptris::Declaration, CustomizedLeptris::Doctype,
CustomizedLeptris::DocumentPI then node.parent_doc
when CustomizedLeptris::TextSegment, CustomizedLeptris::EntityReference then node.parent
else root_parent(node)
end
end
|
.parse(xml, options = {}, _context = nil) ⇒ Object
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
|
# File 'lib/moxml/adapter/leptris.rb', line 653
def parse(xml, options = {}, _context = nil)
xml_string = xml.is_a?(IO) || xml.is_a?(StringIO) ? xml.read : xml.to_s
entity_mode_keep =
NATIVE_ENTITY_REFS &&
(_context&.config&.entity_mode == :keep || options[:keep_entity_refs] == true)
processed, entity_markers =
if entity_mode_keep
[xml_string, false]
else
Entity.preprocess_with_marker_flag(xml_string)
end
native_doc = begin
::Leptris::XML::Document.parse(
processed,
readonly: options[:readonly] == true,
options: parse_flags(options, context: _context),
)
rescue ::Leptris::XML::ParseError => e
raise Moxml::ParseError.new(e.message) if options[:strict]
recover_errors = [e.message]
create_document
end
ctx = _context || Context.new(:leptris)
doc = Wrappers::Document.new(native_doc, ctx)
if options[:noblanks] && !ENGINE_NOBLANKS_SAFE
if options[:readonly] == true
raise ArgumentError,
"noblanks: true requires a mutable document (readonly: true freezes the tree at parse)"
end
strip_blank_text_nodes!(native_doc)
end
record_source_declaration(native_doc, processed)
attachments.set(native_doc, :entity_markers, entity_markers)
attachments.set(native_doc, :keep_entity_refs, entity_mode_keep)
bump_serialize_generation
attachments.set(native_doc, :parse_errors, recover_errors) if recover_errors
doc
end
|
.parse_errors(native_doc) ⇒ Object
831
832
833
|
# File 'lib/moxml/adapter/leptris.rb', line 831
def parse_errors(native_doc)
attachments.get(native_doc, :parse_errors) || NO_PARSE_ERRORS
end
|
.parse_flags(options, context: nil) ⇒ Object
nil when no parse flag is requested — the binding treats a
nil options hash as plain defaults (matching libxml2/
Nokogiri semantics: blanks kept, no ATTLIST defaults).
noblanks forwards only once the engine flag is libxml2-safe
(see ENGINE_NOBLANKS_SAFE); otherwise it is moxml-side.
781
782
783
784
785
786
787
788
789
|
# File 'lib/moxml/adapter/leptris.rb', line 781
def parse_flags(options, context: nil)
flags = 0
flags |= ::Leptris::XML::ParseOptions::DTDATTR if options[:dtdattr] == true
flags |= ::Leptris::XML::ParseOptions::KEEP_ENTITY_REFS if NATIVE_ENTITY_REFS &&
(context&.config&.entity_mode == :keep ||
options[:keep_entity_refs] == true)
flags |= ::Leptris::XML::ParseOptions::NOBLANKS if options[:noblanks] == true && ENGINE_NOBLANKS_SAFE
flags.zero? ? nil : ::Leptris::XML::ParseOptions.new(flags)
end
|
.parse_fragment(xml, _context = nil) ⇒ Object
Tolerant HTML4/5 parsing into the standard DOM (engine
leptris/leptris#659): implied end tags, void elements,
raw-text script/style, case-insensitive lowercased names,
the HTML named-entity table, synthesized html/head/body.
The engine decodes HTML entities directly into text — no
marker pipeline, so the marker split scan is stood down.
Incremental parse (libleptris v1.6/#586): yields completed
elements as the parse runs; each prior subtree is released.
Yielded elements are parentless (document nil) and valid
only inside the block — the wrapper mirrors that lifetime.
Wrapper-parse: the synthetic root's children are the
fragment's top-level nodes (the append_xml / inner_xml=
trick). Children come through the normal children path so
entity-marker splitting applies.
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
|
# File 'lib/moxml/adapter/leptris.rb', line 727
def parse_fragment(xml, _context = nil)
doc = parse("<m>#{xml}</m>")
root_wrapper = doc.root
root_wrapper.parent_node = doc
children(root_wrapper.native).map do |child|
wrapped = Node.wrap(child, doc.context)
wrapped.parent_node = root_wrapper
wrapped
end
end
|
.parse_html(html, _options = {}, _context = nil) ⇒ Object
764
765
766
767
768
769
770
771
772
773
774
|
# File 'lib/moxml/adapter/leptris.rb', line 764
def parse_html(html, _options = {}, _context = nil)
html_string = html.is_a?(IO) || html.is_a?(StringIO) ? html.read : html.to_s
native_doc = begin
::Leptris::XML.parse_html(html_string)
rescue ::Leptris::XML::ParseError => e
raise Moxml::ParseError.new(e.message)
end
attachments.set(native_doc, :entity_markers, false)
bump_serialize_generation
Wrappers::Document.new(native_doc, _context || Context.new(:leptris))
end
|
.plan_rows(native) ⇒ Object
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
|
# File 'lib/moxml/adapter/leptris.rb', line 554
def self.plan_rows(native)
return nil unless NATIVE_READ_LAYER
doc = if native.is_a?(::Leptris::XML::Document)
native
else
doc_for(native)
end
return nil if doc.nil? || attachments.get(doc, :entity_markers)
root_binding = if native.is_a?(::Leptris::XML::Document)
doc.root
else
to_binding(native)
end
return nil unless root_binding
rows = ::Leptris::XML::Native.snapshot_rows(doc,
root_binding.c_address)
rows.each do |row|
yield(row[0], row[1], row[2], row[3])
end
true
end
|
.plan_structs(native, spec) ⇒ Object
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
|
# File 'lib/moxml/adapter/leptris.rb', line 533
def self.plan_structs(native, spec)
return nil unless NATIVE_PLAN_STRUCTS
doc = if native.is_a?(::Leptris::XML::Document)
native
else
doc_for(native)
end
return nil if doc.nil? || attachments.get(doc, :entity_markers)
root_binding = if native.is_a?(::Leptris::XML::Document)
doc.root
else
to_binding(native)
end
return nil unless root_binding
::Leptris::XML::Native.plan_structs(doc, root_binding.c_address,
spec)
end
|
.prefix_part(name) ⇒ Object
1045
1046
1047
|
# File 'lib/moxml/adapter/leptris.rb', line 1045
def prefix_part(name)
name.include?(":") ? name.split(":", 2)[0] : nil
end
|
.prefixed_attribute_test?(ast, parent_axis = nil) ⇒ Boolean
Released native engines 1.9.37–1.9.39 do not match prefixed
attribute tests inside predicates (@p:kind='a'); the Ruby
engine does. An attribute test is a :test whose parent axis
is "attribute"; bare ones (namespace nil) stay native.
Retired above PREFIXED_ATTR_PREDICATES_NATIVE (1.9.40+).
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
|
# File 'lib/moxml/adapter/leptris.rb', line 1964
def prefixed_attribute_test?(ast, parent_axis = nil)
if ast.type == :test && parent_axis == "attribute"
ns = ast.value[:namespace]
return true if ns && !ns.empty? && ns != "xmlns"
end
axis = ast.children.first if ast.type == :axis
ast.children.any? do |child|
child.is_a?(XPath::AST::Node) &&
prefixed_attribute_test?(child, axis || (child.type == :axis ? nil : parent_axis))
end
end
|
.previous_sibling(node) ⇒ Object
1354
1355
1356
1357
1358
1359
1360
1361
|
# File 'lib/moxml/adapter/leptris.rb', line 1354
def previous_sibling(node)
node = to_binding(node)
node.previous_sibling if node.is_a?(::Leptris::XML::Node)
end
|
.processing_instruction_content(node) ⇒ Object
1715
1716
1717
1718
1719
|
# File 'lib/moxml/adapter/leptris.rb', line 1715
def processing_instruction_content(node)
return NN_CONTENT.bind_call(node) if NATIVE_READ_LAYER && node.is_a?(NN)
node.content
end
|
.processing_instruction_target(node) ⇒ Object
1049
1050
1051
1052
1053
|
# File 'lib/moxml/adapter/leptris.rb', line 1049
def processing_instruction_target(node)
return to_binding(node).target if NATIVE_READ_LAYER && node.is_a?(NN)
node.target
end
|
.record_native_doc(root_native, doc) ⇒ Object
339
340
341
|
# File 'lib/moxml/adapter/leptris.rb', line 339
def record_native_doc(root_native, doc)
@native_doc_roots[root_native] = doc
end
|
.remove(node) ⇒ Object
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
|
# File 'lib/moxml/adapter/leptris.rb', line 1570
def remove(node)
node = to_binding(node) if NATIVE_READ_LAYER
case node
when CustomizedLeptris::Declaration
remove_declaration(node.parent_doc) if node.parent_doc
when CustomizedLeptris::Doctype
attachments.delete(node.parent_doc, :doctype) if node.parent_doc
when ::Leptris::XML::DocType
if NATIVE_DOC_PARTS
doc = node.document || doc_for(node)
if doc
doc.remove_doctype
attachments.delete(doc, :doctype)
end
end
when CustomizedLeptris::EntityReference
marker_text_for(node.parent, node.name)&.unlink
when CustomizedLeptris::DocumentPI
if NATIVE_DOC_PARTS && node.parent_doc
pis = node.parent_doc.processing_instructions
index = pis.index do |target, data|
target == node.target && data == node.data
end
if index
node.parent_doc.remove_pi(index)
node.parent_doc = nil
end
else
raise Moxml::NotImplementedError.new(
"libleptris has no document-level PI removal",
adapter: :leptris,
feature: :remove,
)
end
else
node.unlink
end
end
|
.remove_attribute(element, name) ⇒ Object
1466
1467
1468
1469
|
# File 'lib/moxml/adapter/leptris.rb', line 1466
def remove_attribute(element, name)
element = to_binding(element)
element.remove_attribute(name.to_s)
end
|
.remove_attribute_native(attr) ⇒ Object
1471
1472
1473
1474
|
# File 'lib/moxml/adapter/leptris.rb', line 1471
def remove_attribute_native(attr)
attr.element.remove_attribute(attr.name)
attr
end
|
.remove_declaration(native_doc) ⇒ Object
916
917
918
919
920
921
922
|
# File 'lib/moxml/adapter/leptris.rb', line 916
def remove_declaration(native_doc)
attachments.delete(native_doc, :declaration)
attachments.delete(native_doc, :had_source_declaration)
if NATIVE_DOC_PARTS && native_doc.is_a?(::Leptris::XML::Document)
native_doc.clear_declaration
end
end
|
.replace(node, new_node) ⇒ Object
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
|
# File 'lib/moxml/adapter/leptris.rb', line 1609
def replace(node, new_node)
if NATIVE_READ_LAYER
node = to_binding(node)
new_node = to_binding(new_node)
end
return node.replace(new_node) if node.is_a?(::Leptris::XML::Element)
parent = node.parent
unless parent
raise Moxml::DocumentStructureError.new(
"cannot replace a detached node",
)
end
prev = node.previous_sibling
node.unlink
if prev.is_a?(::Leptris::XML::Element)
prev.add_next_sibling(new_node)
else
parent.add_child(new_node)
end
new_node
end
|
.replace_children(node, new_children) ⇒ Object
1637
1638
1639
1640
1641
1642
1643
|
# File 'lib/moxml/adapter/leptris.rb', line 1637
def replace_children(node, new_children)
if NATIVE_READ_LAYER
node = to_binding(node)
new_children = new_children.map { |child| to_binding(child) }
end
node.children = new_children
end
|
.resolve_prefix_ns(owner, prefix) ⇒ Object
Nearest in-scope declaration of prefix, walking owner then
ancestors. Unbound prefix → nil. Returns the Namespace object
so wrappers keep prefix and uri.
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
|
# File 'lib/moxml/adapter/leptris.rb', line 1034
def resolve_prefix_ns(owner, prefix)
current = owner
while current.is_a?(::Leptris::XML::Element)
hit = current.namespace_definitions.find { |ns| ns.prefix == prefix }
return hit if hit
current = current.parent
end
nil
end
|
.root(document) ⇒ Object
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
|
# File 'lib/moxml/adapter/leptris.rb', line 1374
def root(document)
r = document.root
return nil if r.nil?
if NATIVE_READ_LAYER
native = canonical_native(document, r)
record_native_doc(native, document)
return native
end
r
end
|
.root_parent(node) ⇒ Object
The binding reports the root element as parentless; the
moxml contract roots at the document.
1343
1344
1345
|
# File 'lib/moxml/adapter/leptris.rb', line 1343
def root_parent(node)
node.parent || (node.document&.root == node ? node.document : nil)
end
|
.same_node?(one, other) ⇒ Boolean
Protocol-level node equality: one engine can hand out
two wrapper classes over the same C node (native layer +
binding), so raw == is not identity across the seam.
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
|
# File 'lib/moxml/adapter/leptris.rb', line 346
def same_node?(one, other)
return true if one.equal?(other)
if NATIVE_READ_LAYER
native_one = one.is_a?(::Leptris::XML::NativeNode)
native_other = other.is_a?(::Leptris::XML::NativeNode)
if native_one || native_other
address_one = native_one ? one.address : one.c_ptr.address
address_other = native_other ? other.address : other.c_ptr.address
return address_one == address_other
end
end
one == other
end
|
.sax_parse(xml, handler) ⇒ Object
2005
2006
2007
2008
2009
2010
2011
|
# File 'lib/moxml/adapter/leptris.rb', line 2005
def sax_parse(xml, handler)
bridge = LeptrisSAXBridge.new(handler)
xml_string = xml.is_a?(IO) || xml.is_a?(::StringIO) ? xml.read : xml.to_s
::Leptris::XML::SAX::Parser.new(bridge).parse(xml_string)
rescue ::Leptris::XML::ParseError, ::Leptris::XML::Error => e
handler.on_error(Moxml::ParseError.new(e.message))
end
|
.selects_attribute_results?(ast) ⇒ Boolean
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
|
# File 'lib/moxml/adapter/leptris.rb', line 1946
def selects_attribute_results?(ast)
case ast.type
when :pipe, :union, :filter_expr
ast.children.any? { |child| selects_attribute_results?(child) }
when :absolute_path, :relative_path
step = ast.children.last
step = step.children.first if step.type == :step_with_predicates
step.type == :axis && step.children.first == "attribute"
else
false
end
end
|
.set_attribute(element, name, value) ⇒ Object
1436
1437
1438
1439
|
# File 'lib/moxml/adapter/leptris.rb', line 1436
def set_attribute(element, name, value)
element = to_binding(element) if NATIVE_READ_LAYER
element[name.to_s] = value.to_s
end
|
.set_attribute_name(attr, name) ⇒ Object
1441
1442
1443
1444
1445
1446
1447
1448
1449
|
# File 'lib/moxml/adapter/leptris.rb', line 1441
def set_attribute_name(attr, name)
element = attr.element
value = attr.value
element.remove_attribute(attr.name)
element[name.to_s] = value
element.attribute_nodes.reverse.find { |candidate| candidate.name == name.to_s }
end
|
.set_attribute_namespace(attr, namespace) ⇒ Object
Attr is an immutable value object: namespace changes go
through the owning element, recreating the attribute with a
qualified name, and yield a fresh native for the wrapper.
985
986
987
988
989
990
991
992
993
994
995
996
997
998
|
# File 'lib/moxml/adapter/leptris.rb', line 985
def set_attribute_namespace(attr, namespace)
element = attr.element
local = attr.name.include?(":") ? attr.name.split(":", 2)[1] : attr.name
value = attr.value
element.remove_attribute(attr.name)
prefix = namespace.is_a?(String) ? nil : namespace.prefix
uri = namespace.is_a?(String) ? namespace : namespace.href
qualified = prefix.nil? || prefix.empty? ? local : "#{prefix}:#{local}"
element[qualified] = value
if prefix && !prefix.empty? && !uri.to_s.empty? && !resolve_prefix_ns(element, prefix)
element.add_namespace_definition(prefix, uri.to_s)
end
element.attribute_nodes.reverse.find { |candidate| candidate.name == qualified }
end
|
.set_attribute_value(attr, value) ⇒ Object
1451
1452
1453
1454
|
# File 'lib/moxml/adapter/leptris.rb', line 1451
def set_attribute_value(attr, value)
attr.value = value.to_s
attr
end
|
.set_cdata_content(node, content) ⇒ Object
1699
1700
1701
1702
|
# File 'lib/moxml/adapter/leptris.rb', line 1699
def set_cdata_content(node, content)
node = to_binding(node) if NATIVE_READ_LAYER
node.content = content.to_s
end
|
1710
1711
1712
1713
|
# File 'lib/moxml/adapter/leptris.rb', line 1710
def (node, content)
node = to_binding(node) if NATIVE_READ_LAYER
node.content = content.to_s
end
|
.set_declaration_attribute(declaration, attr_name, value) ⇒ Object
900
901
902
|
# File 'lib/moxml/adapter/leptris.rb', line 900
def set_declaration_attribute(declaration, attr_name, value)
declaration.public_send("#{attr_name}=", value) if attr_matches?(attr_name)
end
|
.set_namespace(node, namespace) ⇒ Object
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
|
# File 'lib/moxml/adapter/leptris.rb', line 929
def set_namespace(node, namespace)
node = to_binding(node) if NATIVE_READ_LAYER
return set_attribute_namespace(node, namespace) if node.is_a?(::Leptris::XML::Attr)
element = node
if namespace.nil?
element.name = element.name.split(":", 2)[-1] if element.name.include?(":")
element.default_namespace = ""
return element
end
prefix = namespace.is_a?(String) ? nil : namespace.prefix
uri = namespace.is_a?(String) ? namespace : namespace.href
if prefix.nil? || prefix.empty?
element.default_namespace = uri.to_s
element.name = element.name.split(":", 2)[-1] if element.name.include?(":")
else
local = element.name.split(":", 2)[-1]
element.name = "#{prefix}:#{local}"
already = resolve_prefix_ns(element, prefix)
needs_decl = already.nil? || already.href.to_s != uri.to_s
if needs_decl && !element.parent.nil?
element.add_namespace_definition(prefix, uri.to_s)
end
end
element
end
|
.set_node_name(node, name) ⇒ Object
1118
1119
1120
1121
1122
1123
1124
|
# File 'lib/moxml/adapter/leptris.rb', line 1118
def set_node_name(node, name)
node = to_binding(node) if NATIVE_READ_LAYER
case node
when ::Leptris::XML::ProcessingInstruction, CustomizedLeptris::DocumentPI then node.target = name
else node.name = name
end
end
|
.set_processing_instruction_content(node, content) ⇒ Object
1721
1722
1723
1724
|
# File 'lib/moxml/adapter/leptris.rb', line 1721
def set_processing_instruction_content(node, content)
node = to_binding(node) if NATIVE_READ_LAYER
node.data = content.to_s
end
|
.set_root(doc, element) ⇒ Object
584
585
586
|
# File 'lib/moxml/adapter/leptris.rb', line 584
def set_root(doc, element)
to_binding(doc).root = to_binding(element)
end
|
.set_text_content(node, content) ⇒ Object
1683
1684
1685
1686
1687
1688
1689
1690
1691
|
# File 'lib/moxml/adapter/leptris.rb', line 1683
def set_text_content(node, content)
node = to_binding(node) if NATIVE_READ_LAYER
case node
when ::Leptris::XML::Document
node.root&.content = content.to_s
else
node.content = content.to_s
end
end
|
.sibling_walk_children(node) ⇒ Object
Fallback for natives without a registered document: walk
first_child/next_sibling at the FFI level and wrap binding
nodes over the raw pointers.
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
|
# File 'lib/moxml/adapter/leptris.rb', line 1251
def sibling_walk_children(node)
ptr = ::FFI::Pointer.new(node.address)
first = ::Leptris::XML::FFI.leptris_node_first_child(ptr)
children = []
current = first
until current.null?
children << ::Leptris::XML::Node.wrap(current, nil)
current = ::Leptris::XML::FFI.leptris_node_next_sibling(current)
end
children
end
|
.source_position(node) ⇒ Object
1389
1390
1391
1392
1393
|
# File 'lib/moxml/adapter/leptris.rb', line 1389
def source_position(node)
return nil unless NATIVE_SOURCE_POSITION
to_binding(node).source_position
end
|
.strip_blank_text_nodes!(doc) ⇒ Object
libxml2's XML_PARSE_NOBLANKS semantics (issues #153/#156):
drop WHOLLY-whitespace text nodes; the boundary spaces of
text-bearing nodes stay (mixed content cannot be
re-indented). Raw-pointer walk over the root subtree — the
same batch child-pointer pattern the materializer uses.
799
800
801
802
803
804
805
|
# File 'lib/moxml/adapter/leptris.rb', line 799
def strip_blank_text_nodes!(doc)
binding_ffi = ::Leptris::XML::FFI
root = binding_ffi.leptris_document_root(doc.c_ptr)
return unless root && !root.null?
strip_blanks_under(root, binding_ffi, ::FFI::MemoryPointer.new(:pointer, 64), 64)
end
|
.strip_blanks_under(ptr, binding_ffi, buf, capacity) ⇒ Object
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
|
# File 'lib/moxml/adapter/leptris.rb', line 807
def strip_blanks_under(ptr, binding_ffi, buf, capacity)
count = binding_ffi.leptris_node_children(ptr, buf, capacity)
while count == capacity
capacity *= 4
buf = ::FFI::MemoryPointer.new(:pointer, capacity)
count = binding_ffi.leptris_node_children(ptr, buf, capacity)
end
children = buf.read_array_of_pointer(count)
children.each do |child|
case binding_ffi.leptris_node_get_type(child)
when ::Leptris::XML::FFI::NODE_ELEMENT
strip_blanks_under(child, binding_ffi, buf, capacity)
when ::Leptris::XML::FFI::NODE_TEXT
content = binding_ffi.leptris_text_node_get_content(child)
next unless content.match?(BLANK_TEXT_RE)
binding_ffi.check_status(binding_ffi.leptris_node_unlink(child))
end
end
end
|
.text_content(node) ⇒ Object
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
|
# File 'lib/moxml/adapter/leptris.rb', line 1645
def text_content(node)
if NATIVE_READ_LAYER && node.is_a?(::Leptris::XML::NativeNode)
content = NN_CONTENT.bind_call(node)
return content if NATIVE_STRINGS_UTF8
content.dup.force_encoding(Encoding::UTF_8)
end
case node
when ::Leptris::XML::Element, ::Leptris::XML::Text
node.content.to_s
when ::Leptris::XML::Document then node.root ? node.root.content : ""
when CustomizedLeptris::Declaration, CustomizedLeptris::Doctype,
CustomizedLeptris::EntityReference
""
when CustomizedLeptris::TextSegment then node.content
else node.content.to_s
end
end
|
.to_binding(node) ⇒ Object
native -> bridged binding node. The bridge object is
identity-stable for the native's lifetime (the binding's
per-document wrap cache), so repeat bridges — the write
loop on a factory-created element, re-accessed attribute
lists — skip the document resolution and pointer wrap.
Binding node for any native: identity for binding nodes
(and on installs without the native layer — the constant
check short-circuits), a Node.wrap over the shared C
pointer for NativeNodes.
152
153
154
155
156
157
158
159
160
161
162
163
164
|
# File 'lib/moxml/adapter/leptris.rb', line 152
def to_binding(node)
return node unless NATIVE_READ_LAYER &&
node.is_a?(::Leptris::XML::NativeNode)
bridged = @binding_of[node]
return bridged if bridged
doc = doc_for(node)
ptr = ::FFI::Pointer.new(node.address)
bridged = ::Leptris::XML::Node.wrap(ptr, doc)
@binding_of[node] = bridged unless bridged.nil?
bridged
end
|
.uses_xmlns_prefix?(ast) ⇒ Boolean
xmlns:name is a nokogiri-compat convention addressing
elements in the default namespace; only the Ruby engine
implements it.
1938
1939
1940
1941
1942
1943
1944
|
# File 'lib/moxml/adapter/leptris.rb', line 1938
def uses_xmlns_prefix?(ast)
return true if ast.type == :test && ast.value[:namespace] == "xmlns"
ast.children.any? do |child|
child.is_a?(XPath::AST::Node) && uses_xmlns_prefix?(child)
end
end
|
.walk_descendants(native, context) ⇒ Object
One C visit walk (TODO.perf/27: post-order/abort state in
the C callback, rb_yield direct — no FFI closure per
node) instead of per-level children walks. Pre-order via
entering; depth 0 is the receiver and stays unyielded.
327
328
329
330
331
332
333
334
335
336
337
|
# File 'lib/moxml/adapter/leptris.rb', line 327
def walk_descendants(native, context)
return nil unless NATIVE_C_WALK
root = to_binding(native)
return nil unless root.is_a?(::Leptris::XML::Element) ||
root.is_a?(::Leptris::XML::Document)
root.visit do |node, entering, depth|
yield Moxml::Node.wrap(node, context) if entering && depth.positive?
end
end
|
.wrap_native(node, type, _context) ⇒ Object
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
|
# File 'lib/moxml/adapter/leptris.rb', line 300
def wrap_native(node, type, _context)
return nil unless NATIVE_IDENTITY &&
node.is_a?(::Leptris::XML::NativeNode)
return node if node.is_a?(::Moxml::Node)
klass = Identity::TYPES[type]
return nil unless klass
doc = doc_for(node)
return nil unless doc
klass.from(doc, node)
end
|
.xpath(node, expression, namespaces = {}) ⇒ Object
1775
1776
1777
1778
1779
1780
1781
|
# File 'lib/moxml/adapter/leptris.rb', line 1775
def xpath(node, expression, namespaces = {})
node = to_binding(node) if NATIVE_READ_LAYER
native = native_xpath(node, expression, namespaces)
return native unless native.nil?
engine_xpath(node, expression, namespaces)
end
|