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/
- 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
1484
1485
1486
1487
1488
1489
1490
1491
|
# File 'lib/moxml/adapter/leptris.rb', line 1484
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
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
|
# File 'lib/moxml/adapter/leptris.rb', line 1546
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
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
|
# File 'lib/moxml/adapter/leptris.rb', line 1521
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.
1570
1571
1572
1573
1574
1575
1576
|
# File 'lib/moxml/adapter/leptris.rb', line 1570
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
1935
1936
1937
1938
1939
1940
1941
|
# File 'lib/moxml/adapter/leptris.rb', line 1935
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
1791
1792
1793
1794
1795
1796
1797
|
# File 'lib/moxml/adapter/leptris.rb', line 1791
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
588
589
590
|
# File 'lib/moxml/adapter/leptris.rb', line 588
def attachments
@attachments ||= Moxml::NativeAttachment.new
end
|
.attr_matches?(attr_name) ⇒ Boolean
912
913
914
|
# File 'lib/moxml/adapter/leptris.rb', line 912
def attr_matches?(attr_name)
%w[version encoding standalone].include?(attr_name.to_s)
end
|
.attribute_element(attr) ⇒ Object
1432
1433
1434
|
# File 'lib/moxml/adapter/leptris.rb', line 1432
def attribute_element(attr)
attr.element
end
|
.attribute_name(attr) ⇒ Object
1436
1437
1438
1439
1440
1441
1442
|
# File 'lib/moxml/adapter/leptris.rb', line 1436
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.
1425
1426
1427
1428
1429
1430
|
# File 'lib/moxml/adapter/leptris.rb', line 1425
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
1415
1416
1417
1418
|
# File 'lib/moxml/adapter/leptris.rb', line 1415
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).
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
|
# File 'lib/moxml/adapter/leptris.rb', line 639
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
600
601
602
|
# File 'lib/moxml/adapter/leptris.rb', line 600
def bare_get_qname_safe?
true
end
|
.bare_set_qname_safe? ⇒ Boolean
596
597
598
|
# File 'lib/moxml/adapter/leptris.rb', line 596
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).
1286
1287
1288
1289
1290
1291
1292
1293
1294
|
# File 'lib/moxml/adapter/leptris.rb', line 1286
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].
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
|
# File 'lib/moxml/adapter/leptris.rb', line 1327
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).
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
|
# File 'lib/moxml/adapter/leptris.rb', line 1301
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.
1276
1277
1278
1279
1280
1281
1282
|
# File 'lib/moxml/adapter/leptris.rb', line 1276
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
1701
1702
1703
1704
1705
|
# File 'lib/moxml/adapter/leptris.rb', line 1701
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
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
1214
1215
1216
1217
1218
1219
1220
1221
|
# File 'lib/moxml/adapter/leptris.rb', line 1150
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
|
1712
1713
1714
1715
1716
|
# File 'lib/moxml/adapter/leptris.rb', line 1712
def (node)
return NN_CONTENT.bind_call(node) if NATIVE_READ_LAYER && node.is_a?(NN)
node.content
end
|
.contains_parent_axis?(ast) ⇒ Boolean
1904
1905
1906
1907
1908
1909
1910
1911
|
# File 'lib/moxml/adapter/leptris.rb', line 1904
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
843
844
845
|
# File 'lib/moxml/adapter/leptris.rb', line 843
def create_document(_native_doc = nil)
::Leptris::XML::Document.create
end
|
.create_native_cdata(content, owner_doc = nil) ⇒ Object
868
869
870
|
# File 'lib/moxml/adapter/leptris.rb', line 868
def create_native_cdata(content, owner_doc = nil)
(owner_doc || create_document).create_cdata(content)
end
|
872
873
874
|
# File 'lib/moxml/adapter/leptris.rb', line 872
def (content, owner_doc = nil)
(owner_doc || create_document).(content)
end
|
.create_native_declaration(version, encoding, standalone) ⇒ Object
885
886
887
|
# File 'lib/moxml/adapter/leptris.rb', line 885
def create_native_declaration(version, encoding, standalone)
CustomizedLeptris::Declaration.new(version, encoding, standalone)
end
|
.create_native_doctype(name, external_id, system_id) ⇒ Object
881
882
883
|
# File 'lib/moxml/adapter/leptris.rb', line 881
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.
852
853
854
855
856
857
858
|
# File 'lib/moxml/adapter/leptris.rb', line 852
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
889
890
891
892
893
894
895
|
# File 'lib/moxml/adapter/leptris.rb', line 889
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
932
933
934
935
|
# File 'lib/moxml/adapter/leptris.rb', line 932
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
876
877
878
879
|
# File 'lib/moxml/adapter/leptris.rb', line 876
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
860
861
862
863
864
865
866
|
# File 'lib/moxml/adapter/leptris.rb', line 860
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
904
905
906
|
# File 'lib/moxml/adapter/leptris.rb', line 904
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.
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
|
# File 'lib/moxml/adapter/leptris.rb', line 1098
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
1758
1759
1760
|
# File 'lib/moxml/adapter/leptris.rb', line 1758
def doctype_external_id(node)
node.is_a?(CustomizedLeptris::Doctype) ? node.external_id : node.public_id
end
|
.doctype_name(node) ⇒ Object
1754
1755
1756
|
# File 'lib/moxml/adapter/leptris.rb', line 1754
def doctype_name(node)
node.is_a?(CustomizedLeptris::Doctype) ? node.name : node.root_name
end
|
.doctype_system_id(node) ⇒ Object
1762
1763
1764
|
# File 'lib/moxml/adapter/leptris.rb', line 1762
def doctype_system_id(node)
node.system_id
end
|
.document(node) ⇒ Object
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
|
# File 'lib/moxml/adapter/leptris.rb', line 1371
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
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
|
# File 'lib/moxml/adapter/leptris.rb', line 1986
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
897
898
899
900
901
902
|
# File 'lib/moxml/adapter/leptris.rb', line 897
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.
608
609
610
|
# File 'lib/moxml/adapter/leptris.rb', line 608
def expanded_attr_reads?
true
end
|
.expanded_attr_value(element, uri, local) ⇒ Object
621
622
623
624
625
626
627
628
629
|
# File 'lib/moxml/adapter/leptris.rb', line 621
def expanded_attr_value(element, uri, local)
to_binding(element).attribute_ns(uri, local)
end
|
.expression_uses_parent_axis?(expression) ⇒ Boolean
1897
1898
1899
1900
1901
1902
|
# File 'lib/moxml/adapter/leptris.rb', line 1897
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
1464
1465
1466
1467
|
# File 'lib/moxml/adapter/leptris.rb', line 1464
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
1469
1470
1471
1472
|
# File 'lib/moxml/adapter/leptris.rb', line 1469
def get_attribute_value(element, name)
element = to_binding(element)
element[name.to_s]
end
|
.has_declaration?(native_doc, _wrapper) ⇒ Boolean
917
918
919
920
921
922
|
# File 'lib/moxml/adapter/leptris.rb', line 917
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
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
|
# File 'lib/moxml/adapter/leptris.rb', line 1676
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
754
755
756
757
758
759
760
761
|
# File 'lib/moxml/adapter/leptris.rb', line 754
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
763
764
765
766
767
768
769
770
|
# File 'lib/moxml/adapter/leptris.rb', line 763
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
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
|
# File 'lib/moxml/adapter/leptris.rb', line 1403
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
1008
1009
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
|
# File 'lib/moxml/adapter/leptris.rb', line 1008
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
1749
1750
1751
1752
|
# File 'lib/moxml/adapter/leptris.rb', line 1749
def namespace_definitions(element)
element = to_binding(element) if NATIVE_READ_LAYER
element.namespace_definitions
end
|
.namespace_prefix(namespace) ⇒ Object
1734
1735
1736
1737
1738
1739
1740
1741
|
# File 'lib/moxml/adapter/leptris.rb', line 1734
def namespace_prefix(namespace)
return nil if namespace.is_a?(String)
namespace.prefix
end
|
.namespace_uri(namespace) ⇒ Object
1743
1744
1745
1746
1747
|
# File 'lib/moxml/adapter/leptris.rb', line 1743
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
1888
1889
1890
1891
1892
1893
1894
1895
|
# File 'lib/moxml/adapter/leptris.rb', line 1888
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.
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
|
# File 'lib/moxml/adapter/leptris.rb', line 1916
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
657
658
659
|
# File 'lib/moxml/adapter/leptris.rb', line 657
def native_identity_stable?
true
end
|
.native_inclusive10(native) ⇒ Object
612
613
614
615
616
617
618
619
|
# File 'lib/moxml/adapter/leptris.rb', line 612
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
1223
1224
1225
1226
1227
1228
1229
|
# File 'lib/moxml/adapter/leptris.rb', line 1223
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.
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
1879
1880
1881
1882
1883
1884
1885
1886
|
# File 'lib/moxml/adapter/leptris.rb', line 1801
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
1355
1356
1357
1358
1359
1360
|
# File 'lib/moxml/adapter/leptris.rb', line 1355
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
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
|
# File 'lib/moxml/adapter/leptris.rb', line 1109
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
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
|
# File 'lib/moxml/adapter/leptris.rb', line 1063
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::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
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
712
713
714
715
716
717
718
719
|
# File 'lib/moxml/adapter/leptris.rb', line 661
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
839
840
841
|
# File 'lib/moxml/adapter/leptris.rb', line 839
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.
789
790
791
792
793
794
795
796
797
|
# File 'lib/moxml/adapter/leptris.rb', line 789
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.
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
|
# File 'lib/moxml/adapter/leptris.rb', line 735
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
772
773
774
775
776
777
778
779
780
781
782
|
# File 'lib/moxml/adapter/leptris.rb', line 772
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
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
|
# File 'lib/moxml/adapter/leptris.rb', line 562
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
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
|
# File 'lib/moxml/adapter/leptris.rb', line 541
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
1053
1054
1055
|
# File 'lib/moxml/adapter/leptris.rb', line 1053
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+).
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
|
# File 'lib/moxml/adapter/leptris.rb', line 1972
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
1362
1363
1364
1365
1366
1367
1368
1369
|
# File 'lib/moxml/adapter/leptris.rb', line 1362
def previous_sibling(node)
node = to_binding(node)
node.previous_sibling if node.is_a?(::Leptris::XML::Node)
end
|
.processing_instruction_content(node) ⇒ Object
1723
1724
1725
1726
1727
|
# File 'lib/moxml/adapter/leptris.rb', line 1723
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
1057
1058
1059
1060
1061
|
# File 'lib/moxml/adapter/leptris.rb', line 1057
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
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
1608
1609
1610
1611
1612
1613
1614
1615
|
# File 'lib/moxml/adapter/leptris.rb', line 1578
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
1474
1475
1476
1477
|
# File 'lib/moxml/adapter/leptris.rb', line 1474
def remove_attribute(element, name)
element = to_binding(element)
element.remove_attribute(name.to_s)
end
|
.remove_attribute_native(attr) ⇒ Object
1479
1480
1481
1482
|
# File 'lib/moxml/adapter/leptris.rb', line 1479
def remove_attribute_native(attr)
attr.element.remove_attribute(attr.name)
attr
end
|
.remove_declaration(native_doc) ⇒ Object
924
925
926
927
928
929
930
|
# File 'lib/moxml/adapter/leptris.rb', line 924
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
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
|
# File 'lib/moxml/adapter/leptris.rb', line 1617
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
1645
1646
1647
1648
1649
1650
1651
|
# File 'lib/moxml/adapter/leptris.rb', line 1645
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.
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
|
# File 'lib/moxml/adapter/leptris.rb', line 1042
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
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
|
# File 'lib/moxml/adapter/leptris.rb', line 1382
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.
1351
1352
1353
|
# File 'lib/moxml/adapter/leptris.rb', line 1351
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
2013
2014
2015
2016
2017
2018
2019
|
# File 'lib/moxml/adapter/leptris.rb', line 2013
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
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
|
# File 'lib/moxml/adapter/leptris.rb', line 1954
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
1444
1445
1446
1447
|
# File 'lib/moxml/adapter/leptris.rb', line 1444
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
1449
1450
1451
1452
1453
1454
1455
1456
1457
|
# File 'lib/moxml/adapter/leptris.rb', line 1449
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.
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
|
# File 'lib/moxml/adapter/leptris.rb', line 993
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
1459
1460
1461
1462
|
# File 'lib/moxml/adapter/leptris.rb', line 1459
def set_attribute_value(attr, value)
attr.value = value.to_s
attr
end
|
.set_cdata_content(node, content) ⇒ Object
1707
1708
1709
1710
|
# File 'lib/moxml/adapter/leptris.rb', line 1707
def set_cdata_content(node, content)
node = to_binding(node) if NATIVE_READ_LAYER
node.content = content.to_s
end
|
1718
1719
1720
1721
|
# File 'lib/moxml/adapter/leptris.rb', line 1718
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
908
909
910
|
# File 'lib/moxml/adapter/leptris.rb', line 908
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
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
981
982
983
984
985
986
987
988
|
# File 'lib/moxml/adapter/leptris.rb', line 937
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
1126
1127
1128
1129
1130
1131
1132
|
# File 'lib/moxml/adapter/leptris.rb', line 1126
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
1729
1730
1731
1732
|
# File 'lib/moxml/adapter/leptris.rb', line 1729
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
592
593
594
|
# File 'lib/moxml/adapter/leptris.rb', line 592
def set_root(doc, element)
to_binding(doc).root = to_binding(element)
end
|
.set_text_content(node, content) ⇒ Object
1691
1692
1693
1694
1695
1696
1697
1698
1699
|
# File 'lib/moxml/adapter/leptris.rb', line 1691
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.
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
|
# File 'lib/moxml/adapter/leptris.rb', line 1259
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
1397
1398
1399
1400
1401
|
# File 'lib/moxml/adapter/leptris.rb', line 1397
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.
807
808
809
810
811
812
813
|
# File 'lib/moxml/adapter/leptris.rb', line 807
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
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
|
# File 'lib/moxml/adapter/leptris.rb', line 815
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.
1946
1947
1948
1949
1950
1951
1952
|
# File 'lib/moxml/adapter/leptris.rb', line 1946
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
1783
1784
1785
1786
1787
1788
1789
|
# File 'lib/moxml/adapter/leptris.rb', line 1783
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
|