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. It has no first-class XML declaration or
programmatic DOCTYPE, so those live in CustomizedLeptris value
objects stored 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.
"1.9.32"
- HTML_PARSE_SUPPORTED =
leptris_parse_html_string shipped in bindings 1.9.80
(libleptris 1.9.75, engine #659) as Leptris::XML.parse_html.
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.80")
- ATTR_RESULT_NATIVE =
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.
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.105")
- 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).
NATIVE_READ_LAYER &&
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.193.4")
- 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))
- 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
-
.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
-
.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, 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
actual_native, 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.
447
448
449
|
# File 'lib/moxml/adapter/leptris.rb', line 447
def serialize_generation
@serialize_generation
end
|
Class Method Details
.add_child(parent, child) ⇒ Object
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
|
# File 'lib/moxml/adapter/leptris.rb', line 1499
def add_child(parent, child)
if NATIVE_READ_LAYER && !(NATIVE_MUTATIONS_COHERENT &&
parent.is_a?(::Leptris::XML::NativeNode) &&
child.is_a?(::Leptris::XML::NativeNode))
parent = to_binding(parent)
child = to_binding(child)
end
case parent
when ::Leptris::XML::Document then add_document_child(parent, child)
else
if child.is_a?(CustomizedLeptris::EntityReference)
marker = parent.document.create_text_node("#{Entity::MARKER}#{child.name};")
parent.add_child(marker)
attachments.set(parent.document, :entity_markers, true)
bump_serialize_generation
return child
end
if NATIVE_MUTATIONS_COHERENT && parent.is_a?(NN) && child.is_a?(NN)
doc = NN_DOCUMENT.bind_call(parent)
child = doc.create_text_node(child) if child.is_a?(String)
return NN_ADD_CHILD.bind_call(parent, child)
end
child = parent.document.create_text_node(child) if child.is_a?(String)
parent.add_child(child)
end
end
|
.add_next_sibling(node, new_node) ⇒ Object
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
|
# File 'lib/moxml/adapter/leptris.rb', line 1552
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
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
|
# File 'lib/moxml/adapter/leptris.rb', line 1527
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.
1576
1577
1578
1579
1580
1581
1582
|
# File 'lib/moxml/adapter/leptris.rb', line 1576
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
1922
1923
1924
1925
1926
1927
1928
|
# File 'lib/moxml/adapter/leptris.rb', line 1922
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
1778
1779
1780
1781
1782
1783
1784
|
# File 'lib/moxml/adapter/leptris.rb', line 1778
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
611
612
613
|
# File 'lib/moxml/adapter/leptris.rb', line 611
def attachments
@attachments ||= Moxml::NativeAttachment.new
end
|
.attr_matches?(attr_name) ⇒ Boolean
942
943
944
|
# File 'lib/moxml/adapter/leptris.rb', line 942
def attr_matches?(attr_name)
%w[version encoding standalone].include?(attr_name.to_s)
end
|
.attribute_element(attr) ⇒ Object
1447
1448
1449
|
# File 'lib/moxml/adapter/leptris.rb', line 1447
def attribute_element(attr)
attr.element
end
|
.attribute_name(attr) ⇒ Object
1451
1452
1453
1454
1455
1456
1457
|
# File 'lib/moxml/adapter/leptris.rb', line 1451
def attribute_name(attr)
return attr.name.split(":", 2)[1] if attr.name.include?(":")
attr.name.to_s.dup.force_encoding("UTF-8")
end
|
.attributes(element) ⇒ Object
1442
1443
1444
1445
|
# File 'lib/moxml/adapter/leptris.rb', line 1442
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).
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
|
# File 'lib/moxml/adapter/leptris.rb', line 662
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
623
624
625
|
# File 'lib/moxml/adapter/leptris.rb', line 623
def bare_get_qname_safe?
true
end
|
.bare_set_qname_safe? ⇒ Boolean
619
620
621
|
# File 'lib/moxml/adapter/leptris.rb', line 619
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).
1313
1314
1315
1316
1317
1318
1319
1320
1321
|
# File 'lib/moxml/adapter/leptris.rb', line 1313
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].
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
|
# File 'lib/moxml/adapter/leptris.rb', line 1354
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).
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
|
# File 'lib/moxml/adapter/leptris.rb', line 1328
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
451
452
453
|
# File 'lib/moxml/adapter/leptris.rb', line 451
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.
1303
1304
1305
1306
1307
1308
1309
|
# File 'lib/moxml/adapter/leptris.rb', line 1303
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
1688
1689
1690
1691
1692
|
# File 'lib/moxml/adapter/leptris.rb', line 1688
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
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
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
|
# File 'lib/moxml/adapter/leptris.rb', line 1177
def children(node, entity_bearing: false)
if NATIVE_READ_LAYER && NATIVE_ENTITY_REFS &&
native_keep_entity_refs?(node)
return to_binding(node).children.to_a
end
if NATIVE_READ_LAYER && node.is_a?(::Leptris::XML::NativeNode)
natives = if NATIVE_BULK_FIXED
NN_CHILDREN.bind_call(node).to_a
else
bulk_native_children(node)
end
return natives unless entity_bearing
doc = doc_for(node)
bound_children = natives.map { |child| to_binding(child) }
if doc && attachments.get(doc, :entity_markers) == false
return bound_children
end
return split_entity_markers(bound_children, to_binding(node))
end
case node
when ::Leptris::XML::Element
natives = node.children.to_a
if NATIVE_READ_LAYER && natives.size == 512
natives = bulk_binding_children(node)
end
return natives if node.document.nil? ||
attachments.get(node.document, :entity_markers) == false
split_entity_markers(natives, node)
when ::Leptris::XML::Document
assemble_document_children(node)
when CustomizedLeptris::Declaration, CustomizedLeptris::Doctype,
CustomizedLeptris::EntityReference, CustomizedLeptris::TextSegment,
CustomizedLeptris::DocumentPI,
::Leptris::XML::Text, ::Leptris::XML::,
::Leptris::XML::CDATA, ::Leptris::XML::ProcessingInstruction,
::Leptris::XML::Attr
[]
else
node.children.to_a
end
end
|
1699
1700
1701
1702
1703
|
# File 'lib/moxml/adapter/leptris.rb', line 1699
def (node)
return NN_CONTENT.bind_call(node) if NATIVE_READ_LAYER && node.is_a?(NN)
node.content
end
|
.contains_parent_axis?(ast) ⇒ Boolean
1891
1892
1893
1894
1895
1896
1897
1898
|
# File 'lib/moxml/adapter/leptris.rb', line 1891
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
873
874
875
|
# File 'lib/moxml/adapter/leptris.rb', line 873
def create_document(_native_doc = nil)
::Leptris::XML::Document.create
end
|
.create_native_cdata(content, owner_doc = nil) ⇒ Object
898
899
900
|
# File 'lib/moxml/adapter/leptris.rb', line 898
def create_native_cdata(content, owner_doc = nil)
(owner_doc || create_document).create_cdata(content)
end
|
902
903
904
|
# File 'lib/moxml/adapter/leptris.rb', line 902
def (content, owner_doc = nil)
(owner_doc || create_document).(content)
end
|
.create_native_declaration(version, encoding, standalone) ⇒ Object
915
916
917
|
# File 'lib/moxml/adapter/leptris.rb', line 915
def create_native_declaration(version, encoding, standalone)
CustomizedLeptris::Declaration.new(version, encoding, standalone)
end
|
.create_native_doctype(name, external_id, system_id) ⇒ Object
911
912
913
|
# File 'lib/moxml/adapter/leptris.rb', line 911
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.
882
883
884
885
886
887
888
|
# File 'lib/moxml/adapter/leptris.rb', line 882
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
919
920
921
922
923
924
925
|
# File 'lib/moxml/adapter/leptris.rb', line 919
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
959
960
961
962
|
# File 'lib/moxml/adapter/leptris.rb', line 959
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
906
907
908
909
|
# File 'lib/moxml/adapter/leptris.rb', line 906
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
890
891
892
893
894
895
896
|
# File 'lib/moxml/adapter/leptris.rb', line 890
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
934
935
936
|
# File 'lib/moxml/adapter/leptris.rb', line 934
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.
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
|
# File 'lib/moxml/adapter/leptris.rb', line 1125
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.
394
395
396
397
398
399
400
401
402
|
# File 'lib/moxml/adapter/leptris.rb', line 394
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
1745
1746
1747
|
# File 'lib/moxml/adapter/leptris.rb', line 1745
def doctype_external_id(node)
node.is_a?(CustomizedLeptris::Doctype) ? node.external_id : node.public_id
end
|
.doctype_name(node) ⇒ Object
1741
1742
1743
|
# File 'lib/moxml/adapter/leptris.rb', line 1741
def doctype_name(node)
node.is_a?(CustomizedLeptris::Doctype) ? node.name : node.root_name
end
|
.doctype_system_id(node) ⇒ Object
1749
1750
1751
|
# File 'lib/moxml/adapter/leptris.rb', line 1749
def doctype_system_id(node)
node.system_id
end
|
.document(node) ⇒ Object
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
|
# File 'lib/moxml/adapter/leptris.rb', line 1398
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
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
|
# File 'lib/moxml/adapter/leptris.rb', line 1161
def duplicate_node(node)
node = to_binding(node) if NATIVE_READ_LAYER
case node
when CustomizedLeptris::Declaration
CustomizedLeptris::Declaration.new(node.version, node.encoding, node.standalone)
when CustomizedLeptris::Doctype
CustomizedLeptris::Doctype.new(node.name, node.external_id, node.system_id)
when CustomizedLeptris::EntityReference
CustomizedLeptris::EntityReference.new(node.name)
when CustomizedLeptris::DocumentPI
CustomizedLeptris::DocumentPI.new(node.target, node.data, node.parent_doc)
else
node.dup
end
end
|
.engine_xpath(node, expression, namespaces = {}) ⇒ Object
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
|
# File 'lib/moxml/adapter/leptris.rb', line 1973
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
927
928
929
930
931
932
|
# File 'lib/moxml/adapter/leptris.rb', line 927
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.
631
632
633
|
# File 'lib/moxml/adapter/leptris.rb', line 631
def expanded_attr_reads?
true
end
|
.expanded_attr_value(element, uri, local) ⇒ Object
644
645
646
647
648
649
650
651
652
|
# File 'lib/moxml/adapter/leptris.rb', line 644
def expanded_attr_value(element, uri, local)
to_binding(element).attribute_ns(uri, local)
end
|
.expression_uses_parent_axis?(expression) ⇒ Boolean
1884
1885
1886
1887
1888
1889
|
# File 'lib/moxml/adapter/leptris.rb', line 1884
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
1479
1480
1481
1482
|
# File 'lib/moxml/adapter/leptris.rb', line 1479
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
1484
1485
1486
1487
|
# File 'lib/moxml/adapter/leptris.rb', line 1484
def get_attribute_value(element, name)
element = to_binding(element)
element[name.to_s]
end
|
.has_declaration?(native_doc, _wrapper) ⇒ Boolean
947
948
949
950
951
952
|
# File 'lib/moxml/adapter/leptris.rb', line 947
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
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
|
# File 'lib/moxml/adapter/leptris.rb', line 1663
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
777
778
779
780
781
782
783
784
|
# File 'lib/moxml/adapter/leptris.rb', line 777
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
786
787
788
789
790
791
792
793
|
# File 'lib/moxml/adapter/leptris.rb', line 786
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
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
|
# File 'lib/moxml/adapter/leptris.rb', line 1430
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
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
|
# File 'lib/moxml/adapter/leptris.rb', line 1035
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
1736
1737
1738
1739
|
# File 'lib/moxml/adapter/leptris.rb', line 1736
def namespace_definitions(element)
element = to_binding(element) if NATIVE_READ_LAYER
element.namespace_definitions
end
|
.namespace_prefix(namespace) ⇒ Object
1721
1722
1723
1724
1725
1726
1727
1728
|
# File 'lib/moxml/adapter/leptris.rb', line 1721
def namespace_prefix(namespace)
return nil if namespace.is_a?(String)
namespace.prefix
end
|
.namespace_uri(namespace) ⇒ Object
1730
1731
1732
1733
1734
|
# File 'lib/moxml/adapter/leptris.rb', line 1730
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.
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
|
# File 'lib/moxml/adapter/leptris.rb', line 423
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
1875
1876
1877
1878
1879
1880
1881
1882
|
# File 'lib/moxml/adapter/leptris.rb', line 1875
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.
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
|
# File 'lib/moxml/adapter/leptris.rb', line 1903
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
680
681
682
|
# File 'lib/moxml/adapter/leptris.rb', line 680
def native_identity_stable?
true
end
|
.native_inclusive10(native) ⇒ Object
635
636
637
638
639
640
641
642
|
# File 'lib/moxml/adapter/leptris.rb', line 635
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
1250
1251
1252
1253
1254
1255
1256
|
# File 'lib/moxml/adapter/leptris.rb', line 1250
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.
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
|
# File 'lib/moxml/adapter/leptris.rb', line 1788
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
1382
1383
1384
1385
1386
1387
|
# File 'lib/moxml/adapter/leptris.rb', line 1382
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
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
|
# File 'lib/moxml/adapter/leptris.rb', line 1136
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
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
|
# File 'lib/moxml/adapter/leptris.rb', line 1090
def node_type(node)
if NATIVE_READ_LAYER &&
node.is_a?(::Leptris::XML::NativeNode)
type = NN_NODE_TYPE.bind_call(node)
return :processing_instruction if type == :pi
return type
end
case node
when ::Leptris::XML::Element then :element
when ::Leptris::XML::CDATA then :cdata
when ::Leptris::XML::Text, CustomizedLeptris::TextSegment then :text
when ::Leptris::XML::Attr, ::Leptris::XML::ResultAttr then :attribute
when ::Leptris::XML:: then :comment
when ::Leptris::XML::ProcessingInstruction, CustomizedLeptris::DocumentPI then :processing_instruction
when ::Leptris::XML::Document then :document
when ::Leptris::XML::DocType, CustomizedLeptris::Doctype then :doctype
when CustomizedLeptris::Declaration then :declaration
when CustomizedLeptris::EntityReference then :entity_reference
when ::Leptris::XML::EntityReference then :entity_reference if NATIVE_ENTITY_REFS
else :unknown
end
end
|
.parent(node) ⇒ Object
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
|
# File 'lib/moxml/adapter/leptris.rb', line 1258
def parent(node)
if NATIVE_READ_LAYER && node.is_a?(::Leptris::XML::NativeNode)
parent = NN_PARENT.bind_call(node)
return parent if parent
doc = doc_for(node)
return doc if doc&.root && doc.root.c_ptr.address == node.address
return nil
end
case node
when ::Leptris::XML::Element then root_parent(node)
when ::Leptris::XML::Document then nil
when CustomizedLeptris::Declaration, CustomizedLeptris::Doctype,
CustomizedLeptris::DocumentPI then node.parent_doc
when CustomizedLeptris::TextSegment, CustomizedLeptris::EntityReference then node.parent
else root_parent(node)
end
end
|
.parse(xml, options = {}, _context = nil) ⇒ Object
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
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
|
# File 'lib/moxml/adapter/leptris.rb', line 684
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
869
870
871
|
# File 'lib/moxml/adapter/leptris.rb', line 869
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.
819
820
821
822
823
824
825
826
827
|
# File 'lib/moxml/adapter/leptris.rb', line 819
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.
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
|
# File 'lib/moxml/adapter/leptris.rb', line 758
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
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
|
# File 'lib/moxml/adapter/leptris.rb', line 795
def parse_html(html, _options = {}, _context = nil)
unless HTML_PARSE_SUPPORTED
raise Moxml::AdapterError.new(
"HTML parsing requires leptris >= 1.9.80 (have #{::Leptris::VERSION})",
adapter: name, operation: "parse_html",
)
end
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
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
|
# File 'lib/moxml/adapter/leptris.rb', line 575
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 = if NATIVE_PLAN_ROWS
::Leptris::XML::Native.snapshot_rows(doc,
root_binding.c_address)
else
doc.snapshot(root_binding)
end
rows.each do |row|
if row.is_a?(::Array)
yield(row[0], row[1], row[2], row[3])
else
next unless row[:kind] == "element"
yield(row[:name], row[:attrs].flatten, row[:text], row[:depth])
end
end
true
end
|
.plan_structs(native, spec) ⇒ Object
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
|
# File 'lib/moxml/adapter/leptris.rb', line 554
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
1080
1081
1082
|
# File 'lib/moxml/adapter/leptris.rb', line 1080
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+).
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
|
# File 'lib/moxml/adapter/leptris.rb', line 1959
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
1389
1390
1391
1392
1393
1394
1395
1396
|
# File 'lib/moxml/adapter/leptris.rb', line 1389
def previous_sibling(node)
node = to_binding(node)
node.previous_sibling if node.is_a?(::Leptris::XML::Node)
end
|
.processing_instruction_content(node) ⇒ Object
1710
1711
1712
1713
1714
|
# File 'lib/moxml/adapter/leptris.rb', line 1710
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
1084
1085
1086
1087
1088
|
# File 'lib/moxml/adapter/leptris.rb', line 1084
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
368
369
370
|
# File 'lib/moxml/adapter/leptris.rb', line 368
def record_native_doc(root_native, doc)
@native_doc_roots[root_native] = doc
end
|
.remove(node) ⇒ Object
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
|
# File 'lib/moxml/adapter/leptris.rb', line 1584
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 CustomizedLeptris::EntityReference
marker_text_for(node.parent, node.name)&.unlink
when CustomizedLeptris::DocumentPI
raise Moxml::NotImplementedError.new(
"libleptris has no document-level PI removal",
adapter: :leptris,
feature: :remove,
)
else
node.unlink
end
end
|
.remove_attribute(element, name) ⇒ Object
1489
1490
1491
1492
|
# File 'lib/moxml/adapter/leptris.rb', line 1489
def remove_attribute(element, name)
element = to_binding(element)
element.remove_attribute(name.to_s)
end
|
.remove_attribute_native(attr) ⇒ Object
1494
1495
1496
1497
|
# File 'lib/moxml/adapter/leptris.rb', line 1494
def remove_attribute_native(attr)
attr.element.remove_attribute(attr.name)
attr
end
|
.remove_declaration(native_doc) ⇒ Object
954
955
956
957
|
# File 'lib/moxml/adapter/leptris.rb', line 954
def remove_declaration(native_doc)
attachments.delete(native_doc, :declaration)
attachments.delete(native_doc, :had_source_declaration)
end
|
.replace(node, new_node) ⇒ Object
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
|
# File 'lib/moxml/adapter/leptris.rb', line 1604
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
1632
1633
1634
1635
1636
1637
1638
|
# File 'lib/moxml/adapter/leptris.rb', line 1632
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.
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
|
# File 'lib/moxml/adapter/leptris.rb', line 1069
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
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
|
# File 'lib/moxml/adapter/leptris.rb', line 1409
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.
1378
1379
1380
|
# File 'lib/moxml/adapter/leptris.rb', line 1378
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.
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
|
# File 'lib/moxml/adapter/leptris.rb', line 375
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
2000
2001
2002
2003
2004
2005
2006
|
# File 'lib/moxml/adapter/leptris.rb', line 2000
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
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
|
# File 'lib/moxml/adapter/leptris.rb', line 1941
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
1459
1460
1461
1462
|
# File 'lib/moxml/adapter/leptris.rb', line 1459
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
1464
1465
1466
1467
1468
1469
1470
1471
1472
|
# File 'lib/moxml/adapter/leptris.rb', line 1464
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.
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
|
# File 'lib/moxml/adapter/leptris.rb', line 1020
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
1474
1475
1476
1477
|
# File 'lib/moxml/adapter/leptris.rb', line 1474
def set_attribute_value(attr, value)
attr.value = value.to_s
attr
end
|
.set_cdata_content(node, content) ⇒ Object
1694
1695
1696
1697
|
# File 'lib/moxml/adapter/leptris.rb', line 1694
def set_cdata_content(node, content)
node = to_binding(node) if NATIVE_READ_LAYER
node.content = content.to_s
end
|
1705
1706
1707
1708
|
# File 'lib/moxml/adapter/leptris.rb', line 1705
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
938
939
940
|
# File 'lib/moxml/adapter/leptris.rb', line 938
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
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
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
|
# File 'lib/moxml/adapter/leptris.rb', line 964
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
1153
1154
1155
1156
1157
1158
1159
|
# File 'lib/moxml/adapter/leptris.rb', line 1153
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
1716
1717
1718
1719
|
# File 'lib/moxml/adapter/leptris.rb', line 1716
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
615
616
617
|
# File 'lib/moxml/adapter/leptris.rb', line 615
def set_root(doc, element)
to_binding(doc).root = to_binding(element)
end
|
.set_text_content(node, content) ⇒ Object
1678
1679
1680
1681
1682
1683
1684
1685
1686
|
# File 'lib/moxml/adapter/leptris.rb', line 1678
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.
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
|
# File 'lib/moxml/adapter/leptris.rb', line 1286
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
1424
1425
1426
1427
1428
|
# File 'lib/moxml/adapter/leptris.rb', line 1424
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.
837
838
839
840
841
842
843
|
# File 'lib/moxml/adapter/leptris.rb', line 837
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
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
|
# File 'lib/moxml/adapter/leptris.rb', line 845
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
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
|
# File 'lib/moxml/adapter/leptris.rb', line 1640
def text_content(node)
if NATIVE_READ_LAYER && node.is_a?(::Leptris::XML::NativeNode)
content = NN_CONTENT.bind_call(node)
return content if NATIVE_STRINGS_UTF8
content.dup.force_encoding(Encoding::UTF_8)
end
case node
when ::Leptris::XML::Element, ::Leptris::XML::Text
node.content.to_s
when ::Leptris::XML::Document then node.root ? node.root.content : ""
when CustomizedLeptris::Declaration, CustomizedLeptris::Doctype,
CustomizedLeptris::EntityReference
""
when CustomizedLeptris::TextSegment then node.content
else node.content.to_s
end
end
|
.to_binding(node) ⇒ Object
native -> bridged binding node. The bridge object is
identity-stable for the native's lifetime (the binding's
per-document wrap cache), so repeat bridges — the write
loop on a factory-created element, re-accessed attribute
lists — skip the document resolution and pointer wrap.
Binding node for any native: identity for binding nodes
(and on installs without the native layer — the constant
check short-circuits), a Node.wrap over the shared C
pointer for NativeNodes.
150
151
152
153
154
155
156
157
158
159
160
161
162
|
# File 'lib/moxml/adapter/leptris.rb', line 150
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.
1933
1934
1935
1936
1937
1938
1939
|
# File 'lib/moxml/adapter/leptris.rb', line 1933
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.
356
357
358
359
360
361
362
363
364
365
366
|
# File 'lib/moxml/adapter/leptris.rb', line 356
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
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
|
# File 'lib/moxml/adapter/leptris.rb', line 329
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
1770
1771
1772
1773
1774
1775
1776
|
# File 'lib/moxml/adapter/leptris.rb', line 1770
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
|