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_PLAN_TYPED =
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.
The typed executor face (leptris-ruby: typed plan scalars —
[slot, tag] spec entries cast in C, no Ruby String for
numeric/boolean slots). Capability-probed, not
version-gated.
!defined?(::Leptris::XML::Native) ||
::Leptris::XML::Native.respond_to?(:plan_structs_typed?)
- NATIVE_DOC_PARTS =
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/
- VISIT_NODE_TYPES =
Walk-minted binding nodes carry the engine's kind int in
replaces the class-case chain on the per-wrap hot path.
%i[element text comment cdata
processing_instruction doctype].freeze
- 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.
420
421
422
|
# File 'lib/moxml/adapter/leptris.rb', line 420
def serialize_generation
@serialize_generation
end
|
Class Method Details
.actual_native(child_native, parent_native) ⇒ Object
1501
1502
1503
1504
1505
1506
1507
1508
|
# File 'lib/moxml/adapter/leptris.rb', line 1501
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
.add_next_sibling(node, new_node) ⇒ Object
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
|
# File 'lib/moxml/adapter/leptris.rb', line 1563
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
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
|
# File 'lib/moxml/adapter/leptris.rb', line 1538
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.
1587
1588
1589
1590
1591
1592
1593
|
# File 'lib/moxml/adapter/leptris.rb', line 1587
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
1952
1953
1954
1955
1956
1957
1958
|
# File 'lib/moxml/adapter/leptris.rb', line 1952
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
1808
1809
1810
1811
1812
1813
1814
|
# File 'lib/moxml/adapter/leptris.rb', line 1808
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
590
591
592
|
# File 'lib/moxml/adapter/leptris.rb', line 590
def attachments
@attachments ||= Moxml::NativeAttachment.new
end
|
.attr_matches?(attr_name) ⇒ Boolean
914
915
916
|
# File 'lib/moxml/adapter/leptris.rb', line 914
def attr_matches?(attr_name)
%w[version encoding standalone].include?(attr_name.to_s)
end
|
.attribute_element(attr) ⇒ Object
1449
1450
1451
|
# File 'lib/moxml/adapter/leptris.rb', line 1449
def attribute_element(attr)
attr.element
end
|
.attribute_name(attr) ⇒ Object
1453
1454
1455
1456
1457
1458
1459
|
# File 'lib/moxml/adapter/leptris.rb', line 1453
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.
1442
1443
1444
1445
1446
1447
|
# File 'lib/moxml/adapter/leptris.rb', line 1442
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
1432
1433
1434
1435
|
# File 'lib/moxml/adapter/leptris.rb', line 1432
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).
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
|
# File 'lib/moxml/adapter/leptris.rb', line 641
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
602
603
604
|
# File 'lib/moxml/adapter/leptris.rb', line 602
def bare_get_qname_safe?
true
end
|
.bare_set_qname_safe? ⇒ Boolean
598
599
600
|
# File 'lib/moxml/adapter/leptris.rb', line 598
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).
1303
1304
1305
1306
1307
1308
1309
1310
1311
|
# File 'lib/moxml/adapter/leptris.rb', line 1303
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].
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
|
# File 'lib/moxml/adapter/leptris.rb', line 1344
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).
1318
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 1318
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
424
425
426
|
# File 'lib/moxml/adapter/leptris.rb', line 424
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.
1293
1294
1295
1296
1297
1298
1299
|
# File 'lib/moxml/adapter/leptris.rb', line 1293
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
1718
1719
1720
1721
1722
|
# File 'lib/moxml/adapter/leptris.rb', line 1718
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
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
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
|
# File 'lib/moxml/adapter/leptris.rb', line 1167
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::Comment,
::Leptris::XML::CDATA, ::Leptris::XML::ProcessingInstruction,
::Leptris::XML::Attr
[]
else
node.children.to_a
end
end
|
1729
1730
1731
1732
1733
|
# File 'lib/moxml/adapter/leptris.rb', line 1729
def (node)
return NN_CONTENT.bind_call(node) if NATIVE_READ_LAYER && node.is_a?(NN)
node.content
end
|
.contains_parent_axis?(ast) ⇒ Boolean
1921
1922
1923
1924
1925
1926
1927
1928
|
# File 'lib/moxml/adapter/leptris.rb', line 1921
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
845
846
847
|
# File 'lib/moxml/adapter/leptris.rb', line 845
def create_document(_native_doc = nil)
::Leptris::XML::Document.create
end
|
.create_native_cdata(content, owner_doc = nil) ⇒ Object
870
871
872
|
# File 'lib/moxml/adapter/leptris.rb', line 870
def create_native_cdata(content, owner_doc = nil)
(owner_doc || create_document).create_cdata(content)
end
|
874
875
876
|
# File 'lib/moxml/adapter/leptris.rb', line 874
def (content, owner_doc = nil)
(owner_doc || create_document).(content)
end
|
.create_native_declaration(version, encoding, standalone) ⇒ Object
887
888
889
|
# File 'lib/moxml/adapter/leptris.rb', line 887
def create_native_declaration(version, encoding, standalone)
CustomizedLeptris::Declaration.new(version, encoding, standalone)
end
|
.create_native_doctype(name, external_id, system_id) ⇒ Object
883
884
885
|
# File 'lib/moxml/adapter/leptris.rb', line 883
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.
854
855
856
857
858
859
860
|
# File 'lib/moxml/adapter/leptris.rb', line 854
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
891
892
893
894
895
896
897
|
# File 'lib/moxml/adapter/leptris.rb', line 891
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
934
935
936
937
|
# File 'lib/moxml/adapter/leptris.rb', line 934
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
878
879
880
881
|
# File 'lib/moxml/adapter/leptris.rb', line 878
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
862
863
864
865
866
867
868
|
# File 'lib/moxml/adapter/leptris.rb', line 862
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
906
907
908
|
# File 'lib/moxml/adapter/leptris.rb', line 906
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.
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
|
# File 'lib/moxml/adapter/leptris.rb', line 1115
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.
367
368
369
370
371
372
373
374
375
|
# File 'lib/moxml/adapter/leptris.rb', line 367
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
1775
1776
1777
|
# File 'lib/moxml/adapter/leptris.rb', line 1775
def doctype_external_id(node)
node.is_a?(CustomizedLeptris::Doctype) ? node.external_id : node.public_id
end
|
.doctype_name(node) ⇒ Object
1771
1772
1773
|
# File 'lib/moxml/adapter/leptris.rb', line 1771
def doctype_name(node)
node.is_a?(CustomizedLeptris::Doctype) ? node.name : node.root_name
end
|
.doctype_system_id(node) ⇒ Object
1779
1780
1781
|
# File 'lib/moxml/adapter/leptris.rb', line 1779
def doctype_system_id(node)
node.system_id
end
|
.document(node) ⇒ Object
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
|
# File 'lib/moxml/adapter/leptris.rb', line 1388
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
.engine_xpath(node, expression, namespaces = {}) ⇒ Object
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
|
# File 'lib/moxml/adapter/leptris.rb', line 2003
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
899
900
901
902
903
904
|
# File 'lib/moxml/adapter/leptris.rb', line 899
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.
610
611
612
|
# File 'lib/moxml/adapter/leptris.rb', line 610
def expanded_attr_reads?
true
end
|
.expanded_attr_value(element, uri, local) ⇒ Object
623
624
625
626
627
628
629
630
631
|
# File 'lib/moxml/adapter/leptris.rb', line 623
def expanded_attr_value(element, uri, local)
to_binding(element).attribute_ns(uri, local)
end
|
.expression_uses_parent_axis?(expression) ⇒ Boolean
1914
1915
1916
1917
1918
1919
|
# File 'lib/moxml/adapter/leptris.rb', line 1914
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
1481
1482
1483
1484
|
# File 'lib/moxml/adapter/leptris.rb', line 1481
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
1486
1487
1488
1489
|
# File 'lib/moxml/adapter/leptris.rb', line 1486
def get_attribute_value(element, name)
element = to_binding(element)
element[name.to_s]
end
|
.has_declaration?(native_doc, _wrapper) ⇒ Boolean
919
920
921
922
923
924
|
# File 'lib/moxml/adapter/leptris.rb', line 919
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
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
|
# File 'lib/moxml/adapter/leptris.rb', line 1693
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
756
757
758
759
760
761
762
763
|
# File 'lib/moxml/adapter/leptris.rb', line 756
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
765
766
767
768
769
770
771
772
|
# File 'lib/moxml/adapter/leptris.rb', line 765
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
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
|
# File 'lib/moxml/adapter/leptris.rb', line 1420
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
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
|
# File 'lib/moxml/adapter/leptris.rb', line 1010
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
1766
1767
1768
1769
|
# File 'lib/moxml/adapter/leptris.rb', line 1766
def namespace_definitions(element)
element = to_binding(element) if NATIVE_READ_LAYER
element.namespace_definitions
end
|
.namespace_prefix(namespace) ⇒ Object
1751
1752
1753
1754
1755
1756
1757
1758
|
# File 'lib/moxml/adapter/leptris.rb', line 1751
def namespace_prefix(namespace)
return nil if namespace.is_a?(String)
namespace.prefix
end
|
.namespace_uri(namespace) ⇒ Object
1760
1761
1762
1763
1764
|
# File 'lib/moxml/adapter/leptris.rb', line 1760
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.
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
|
# File 'lib/moxml/adapter/leptris.rb', line 396
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
1905
1906
1907
1908
1909
1910
1911
1912
|
# File 'lib/moxml/adapter/leptris.rb', line 1905
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.
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
|
# File 'lib/moxml/adapter/leptris.rb', line 1933
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
659
660
661
|
# File 'lib/moxml/adapter/leptris.rb', line 659
def native_identity_stable?
true
end
|
.native_inclusive10(native) ⇒ Object
614
615
616
617
618
619
620
621
|
# File 'lib/moxml/adapter/leptris.rb', line 614
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
1240
1241
1242
1243
1244
1245
1246
|
# File 'lib/moxml/adapter/leptris.rb', line 1240
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.
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
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
|
# File 'lib/moxml/adapter/leptris.rb', line 1818
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
1372
1373
1374
1375
1376
1377
|
# File 'lib/moxml/adapter/leptris.rb', line 1372
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
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
|
# File 'lib/moxml/adapter/leptris.rb', line 1126
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
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
|
# File 'lib/moxml/adapter/leptris.rb', line 1071
def node_type(node)
if node.is_a?(::Leptris::XML::Node) &&
node.instance_variable_defined?(:@node_type)
kind = node.instance_variable_get(:@node_type)
if kind.is_a?(Integer)
mapped = VISIT_NODE_TYPES[kind]
return mapped if mapped
end
end
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::Comment 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
|
.parse(xml, options = {}, _context = nil) ⇒ Object
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
712
713
714
715
716
717
718
719
720
721
|
# File 'lib/moxml/adapter/leptris.rb', line 663
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
841
842
843
|
# File 'lib/moxml/adapter/leptris.rb', line 841
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.
791
792
793
794
795
796
797
798
799
|
# File 'lib/moxml/adapter/leptris.rb', line 791
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.
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
|
# File 'lib/moxml/adapter/leptris.rb', line 737
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
774
775
776
777
778
779
780
781
782
783
784
|
# File 'lib/moxml/adapter/leptris.rb', line 774
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
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
|
# File 'lib/moxml/adapter/leptris.rb', line 564
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
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
|
# File 'lib/moxml/adapter/leptris.rb', line 543
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
1055
1056
1057
|
# File 'lib/moxml/adapter/leptris.rb', line 1055
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+).
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
|
# File 'lib/moxml/adapter/leptris.rb', line 1989
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
1379
1380
1381
1382
1383
1384
1385
1386
|
# File 'lib/moxml/adapter/leptris.rb', line 1379
def previous_sibling(node)
node = to_binding(node)
node.previous_sibling if node.is_a?(::Leptris::XML::Node)
end
|
.processing_instruction_content(node) ⇒ Object
1740
1741
1742
1743
1744
|
# File 'lib/moxml/adapter/leptris.rb', line 1740
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
1059
1060
1061
1062
1063
|
# File 'lib/moxml/adapter/leptris.rb', line 1059
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
341
342
343
|
# File 'lib/moxml/adapter/leptris.rb', line 341
def record_native_doc(root_native, doc)
@native_doc_roots[root_native] = doc
end
|
.remove(node) ⇒ Object
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
|
# File 'lib/moxml/adapter/leptris.rb', line 1595
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
1491
1492
1493
1494
|
# File 'lib/moxml/adapter/leptris.rb', line 1491
def remove_attribute(element, name)
element = to_binding(element)
element.remove_attribute(name.to_s)
end
|
.remove_attribute_native(attr) ⇒ Object
1496
1497
1498
1499
|
# File 'lib/moxml/adapter/leptris.rb', line 1496
def remove_attribute_native(attr)
attr.element.remove_attribute(attr.name)
attr
end
|
.remove_declaration(native_doc) ⇒ Object
926
927
928
929
930
931
932
|
# File 'lib/moxml/adapter/leptris.rb', line 926
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
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
|
# File 'lib/moxml/adapter/leptris.rb', line 1634
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
1662
1663
1664
1665
1666
1667
1668
|
# File 'lib/moxml/adapter/leptris.rb', line 1662
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.
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
|
# File 'lib/moxml/adapter/leptris.rb', line 1044
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
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
|
# File 'lib/moxml/adapter/leptris.rb', line 1399
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.
1368
1369
1370
|
# File 'lib/moxml/adapter/leptris.rb', line 1368
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.
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
|
# File 'lib/moxml/adapter/leptris.rb', line 348
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
2030
2031
2032
2033
2034
2035
2036
|
# File 'lib/moxml/adapter/leptris.rb', line 2030
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
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
|
# File 'lib/moxml/adapter/leptris.rb', line 1971
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
1461
1462
1463
1464
|
# File 'lib/moxml/adapter/leptris.rb', line 1461
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
1466
1467
1468
1469
1470
1471
1472
1473
1474
|
# File 'lib/moxml/adapter/leptris.rb', line 1466
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.
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
|
# File 'lib/moxml/adapter/leptris.rb', line 995
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
1476
1477
1478
1479
|
# File 'lib/moxml/adapter/leptris.rb', line 1476
def set_attribute_value(attr, value)
attr.value = value.to_s
attr
end
|
.set_cdata_content(node, content) ⇒ Object
1724
1725
1726
1727
|
# File 'lib/moxml/adapter/leptris.rb', line 1724
def set_cdata_content(node, content)
node = to_binding(node) if NATIVE_READ_LAYER
node.content = content.to_s
end
|
1735
1736
1737
1738
|
# File 'lib/moxml/adapter/leptris.rb', line 1735
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
910
911
912
|
# File 'lib/moxml/adapter/leptris.rb', line 910
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
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
981
982
983
984
985
986
987
988
989
990
|
# File 'lib/moxml/adapter/leptris.rb', line 939
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
1143
1144
1145
1146
1147
1148
1149
|
# File 'lib/moxml/adapter/leptris.rb', line 1143
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
1746
1747
1748
1749
|
# File 'lib/moxml/adapter/leptris.rb', line 1746
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
594
595
596
|
# File 'lib/moxml/adapter/leptris.rb', line 594
def set_root(doc, element)
to_binding(doc).root = to_binding(element)
end
|
.set_text_content(node, content) ⇒ Object
1708
1709
1710
1711
1712
1713
1714
1715
1716
|
# File 'lib/moxml/adapter/leptris.rb', line 1708
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.
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
|
# File 'lib/moxml/adapter/leptris.rb', line 1276
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
1414
1415
1416
1417
1418
|
# File 'lib/moxml/adapter/leptris.rb', line 1414
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.
809
810
811
812
813
814
815
|
# File 'lib/moxml/adapter/leptris.rb', line 809
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
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
|
# File 'lib/moxml/adapter/leptris.rb', line 817
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
.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.
1963
1964
1965
1966
1967
1968
1969
|
# File 'lib/moxml/adapter/leptris.rb', line 1963
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
338
339
|
# 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|
if entering && depth.positive?
yield Moxml::Node.wrap_with(node, context, self)
end
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
1800
1801
1802
1803
1804
1805
1806
|
# File 'lib/moxml/adapter/leptris.rb', line 1800
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
|