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#266 (binding 1.9.197.1): Native.plan_structs —
the C struct executor behind Moxml::StructPlan.
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.197.1")
- 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
.add_next_sibling(node, new_node) ⇒ Object
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
|
# File 'lib/moxml/adapter/leptris.rb', line 1543
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
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
|
# File 'lib/moxml/adapter/leptris.rb', line 1518
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.
1567
1568
1569
1570
1571
1572
1573
|
# File 'lib/moxml/adapter/leptris.rb', line 1567
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
1913
1914
1915
1916
1917
1918
1919
|
# File 'lib/moxml/adapter/leptris.rb', line 1913
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
1769
1770
1771
1772
1773
1774
1775
|
# File 'lib/moxml/adapter/leptris.rb', line 1769
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
602
603
604
|
# File 'lib/moxml/adapter/leptris.rb', line 602
def attachments
@attachments ||= Moxml::NativeAttachment.new
end
|
.attr_matches?(attr_name) ⇒ Boolean
933
934
935
|
# File 'lib/moxml/adapter/leptris.rb', line 933
def attr_matches?(attr_name)
%w[version encoding standalone].include?(attr_name.to_s)
end
|
.attribute_element(attr) ⇒ Object
1438
1439
1440
|
# File 'lib/moxml/adapter/leptris.rb', line 1438
def attribute_element(attr)
attr.element
end
|
.attribute_name(attr) ⇒ Object
1442
1443
1444
1445
1446
1447
1448
|
# File 'lib/moxml/adapter/leptris.rb', line 1442
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
1433
1434
1435
1436
|
# File 'lib/moxml/adapter/leptris.rb', line 1433
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).
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
|
# File 'lib/moxml/adapter/leptris.rb', line 653
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
614
615
616
|
# File 'lib/moxml/adapter/leptris.rb', line 614
def bare_get_qname_safe?
true
end
|
.bare_set_qname_safe? ⇒ Boolean
610
611
612
|
# File 'lib/moxml/adapter/leptris.rb', line 610
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).
1304
1305
1306
1307
1308
1309
1310
1311
1312
|
# File 'lib/moxml/adapter/leptris.rb', line 1304
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].
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
|
# File 'lib/moxml/adapter/leptris.rb', line 1345
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).
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
|
# File 'lib/moxml/adapter/leptris.rb', line 1319
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.
1294
1295
1296
1297
1298
1299
1300
|
# File 'lib/moxml/adapter/leptris.rb', line 1294
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
1679
1680
1681
1682
1683
|
# File 'lib/moxml/adapter/leptris.rb', line 1679
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
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
|
# File 'lib/moxml/adapter/leptris.rb', line 1168
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
|
1690
1691
1692
1693
1694
|
# File 'lib/moxml/adapter/leptris.rb', line 1690
def (node)
return NN_CONTENT.bind_call(node) if NATIVE_READ_LAYER && node.is_a?(NN)
node.content
end
|
.contains_parent_axis?(ast) ⇒ Boolean
1882
1883
1884
1885
1886
1887
1888
1889
|
# File 'lib/moxml/adapter/leptris.rb', line 1882
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
864
865
866
|
# File 'lib/moxml/adapter/leptris.rb', line 864
def create_document(_native_doc = nil)
::Leptris::XML::Document.create
end
|
.create_native_cdata(content, owner_doc = nil) ⇒ Object
889
890
891
|
# File 'lib/moxml/adapter/leptris.rb', line 889
def create_native_cdata(content, owner_doc = nil)
(owner_doc || create_document).create_cdata(content)
end
|
893
894
895
|
# File 'lib/moxml/adapter/leptris.rb', line 893
def (content, owner_doc = nil)
(owner_doc || create_document).(content)
end
|
.create_native_declaration(version, encoding, standalone) ⇒ Object
906
907
908
|
# File 'lib/moxml/adapter/leptris.rb', line 906
def create_native_declaration(version, encoding, standalone)
CustomizedLeptris::Declaration.new(version, encoding, standalone)
end
|
.create_native_doctype(name, external_id, system_id) ⇒ Object
902
903
904
|
# File 'lib/moxml/adapter/leptris.rb', line 902
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.
873
874
875
876
877
878
879
|
# File 'lib/moxml/adapter/leptris.rb', line 873
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
910
911
912
913
914
915
916
|
# File 'lib/moxml/adapter/leptris.rb', line 910
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
950
951
952
953
|
# File 'lib/moxml/adapter/leptris.rb', line 950
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
897
898
899
900
|
# File 'lib/moxml/adapter/leptris.rb', line 897
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
881
882
883
884
885
886
887
|
# File 'lib/moxml/adapter/leptris.rb', line 881
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
925
926
927
|
# File 'lib/moxml/adapter/leptris.rb', line 925
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.
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
|
# File 'lib/moxml/adapter/leptris.rb', line 1116
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
1736
1737
1738
|
# File 'lib/moxml/adapter/leptris.rb', line 1736
def doctype_external_id(node)
node.is_a?(CustomizedLeptris::Doctype) ? node.external_id : node.public_id
end
|
.doctype_name(node) ⇒ Object
1732
1733
1734
|
# File 'lib/moxml/adapter/leptris.rb', line 1732
def doctype_name(node)
node.is_a?(CustomizedLeptris::Doctype) ? node.name : node.root_name
end
|
.doctype_system_id(node) ⇒ Object
1740
1741
1742
|
# File 'lib/moxml/adapter/leptris.rb', line 1740
def doctype_system_id(node)
node.system_id
end
|
.document(node) ⇒ Object
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
|
# File 'lib/moxml/adapter/leptris.rb', line 1389
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
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
|
# File 'lib/moxml/adapter/leptris.rb', line 1964
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
918
919
920
921
922
923
|
# File 'lib/moxml/adapter/leptris.rb', line 918
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.
622
623
624
|
# File 'lib/moxml/adapter/leptris.rb', line 622
def expanded_attr_reads?
true
end
|
.expanded_attr_value(element, uri, local) ⇒ Object
635
636
637
638
639
640
641
642
643
|
# File 'lib/moxml/adapter/leptris.rb', line 635
def expanded_attr_value(element, uri, local)
to_binding(element).attribute_ns(uri, local)
end
|
.expression_uses_parent_axis?(expression) ⇒ Boolean
1875
1876
1877
1878
1879
1880
|
# File 'lib/moxml/adapter/leptris.rb', line 1875
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
1470
1471
1472
1473
|
# File 'lib/moxml/adapter/leptris.rb', line 1470
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
1475
1476
1477
1478
|
# File 'lib/moxml/adapter/leptris.rb', line 1475
def get_attribute_value(element, name)
element = to_binding(element)
element[name.to_s]
end
|
.has_declaration?(native_doc, _wrapper) ⇒ Boolean
938
939
940
941
942
943
|
# File 'lib/moxml/adapter/leptris.rb', line 938
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
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
|
# File 'lib/moxml/adapter/leptris.rb', line 1654
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
768
769
770
771
772
773
774
775
|
# File 'lib/moxml/adapter/leptris.rb', line 768
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
777
778
779
780
781
782
783
784
|
# File 'lib/moxml/adapter/leptris.rb', line 777
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
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
|
# File 'lib/moxml/adapter/leptris.rb', line 1421
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
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
|
# File 'lib/moxml/adapter/leptris.rb', line 1026
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
1727
1728
1729
1730
|
# File 'lib/moxml/adapter/leptris.rb', line 1727
def namespace_definitions(element)
element = to_binding(element) if NATIVE_READ_LAYER
element.namespace_definitions
end
|
.namespace_prefix(namespace) ⇒ Object
1712
1713
1714
1715
1716
1717
1718
1719
|
# File 'lib/moxml/adapter/leptris.rb', line 1712
def namespace_prefix(namespace)
return nil if namespace.is_a?(String)
namespace.prefix
end
|
.namespace_uri(namespace) ⇒ Object
1721
1722
1723
1724
1725
|
# File 'lib/moxml/adapter/leptris.rb', line 1721
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
1866
1867
1868
1869
1870
1871
1872
1873
|
# File 'lib/moxml/adapter/leptris.rb', line 1866
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.
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
|
# File 'lib/moxml/adapter/leptris.rb', line 1894
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
671
672
673
|
# File 'lib/moxml/adapter/leptris.rb', line 671
def native_identity_stable?
true
end
|
.native_inclusive10(native) ⇒ Object
626
627
628
629
630
631
632
633
|
# File 'lib/moxml/adapter/leptris.rb', line 626
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
1241
1242
1243
1244
1245
1246
1247
|
# File 'lib/moxml/adapter/leptris.rb', line 1241
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.
1779
1780
1781
1782
1783
1784
1785
1786
1787
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
|
# File 'lib/moxml/adapter/leptris.rb', line 1779
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
1373
1374
1375
1376
1377
1378
|
# File 'lib/moxml/adapter/leptris.rb', line 1373
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
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
|
# File 'lib/moxml/adapter/leptris.rb', line 1127
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
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
|
# File 'lib/moxml/adapter/leptris.rb', line 1081
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
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
|
# File 'lib/moxml/adapter/leptris.rb', line 675
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
860
861
862
|
# File 'lib/moxml/adapter/leptris.rb', line 860
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.
810
811
812
813
814
815
816
817
818
|
# File 'lib/moxml/adapter/leptris.rb', line 810
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.
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
|
# File 'lib/moxml/adapter/leptris.rb', line 749
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
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
|
# File 'lib/moxml/adapter/leptris.rb', line 786
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
566
567
568
569
570
571
572
573
574
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
|
# File 'lib/moxml/adapter/leptris.rb', line 566
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
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
|
# File 'lib/moxml/adapter/leptris.rb', line 545
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
1071
1072
1073
|
# File 'lib/moxml/adapter/leptris.rb', line 1071
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+).
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
|
# File 'lib/moxml/adapter/leptris.rb', line 1950
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
1380
1381
1382
1383
1384
1385
1386
1387
|
# File 'lib/moxml/adapter/leptris.rb', line 1380
def previous_sibling(node)
node = to_binding(node)
node.previous_sibling if node.is_a?(::Leptris::XML::Node)
end
|
.processing_instruction_content(node) ⇒ Object
1701
1702
1703
1704
1705
|
# File 'lib/moxml/adapter/leptris.rb', line 1701
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
1075
1076
1077
1078
1079
|
# File 'lib/moxml/adapter/leptris.rb', line 1075
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_attribute(element, name) ⇒ Object
1480
1481
1482
1483
|
# File 'lib/moxml/adapter/leptris.rb', line 1480
def remove_attribute(element, name)
element = to_binding(element)
element.remove_attribute(name.to_s)
end
|
.remove_attribute_native(attr) ⇒ Object
1485
1486
1487
1488
|
# File 'lib/moxml/adapter/leptris.rb', line 1485
def remove_attribute_native(attr)
attr.element.remove_attribute(attr.name)
attr
end
|
.remove_declaration(native_doc) ⇒ Object
945
946
947
948
|
# File 'lib/moxml/adapter/leptris.rb', line 945
def remove_declaration(native_doc)
attachments.delete(native_doc, :declaration)
attachments.delete(native_doc, :had_source_declaration)
end
|
.replace(node, new_node) ⇒ Object
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
|
# File 'lib/moxml/adapter/leptris.rb', line 1595
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
1623
1624
1625
1626
1627
1628
1629
|
# File 'lib/moxml/adapter/leptris.rb', line 1623
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.
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
|
# File 'lib/moxml/adapter/leptris.rb', line 1060
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
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
|
# File 'lib/moxml/adapter/leptris.rb', line 1400
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.
1369
1370
1371
|
# File 'lib/moxml/adapter/leptris.rb', line 1369
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
1991
1992
1993
1994
1995
1996
1997
|
# File 'lib/moxml/adapter/leptris.rb', line 1991
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
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
|
# File 'lib/moxml/adapter/leptris.rb', line 1932
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
1450
1451
1452
1453
|
# File 'lib/moxml/adapter/leptris.rb', line 1450
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
1455
1456
1457
1458
1459
1460
1461
1462
1463
|
# File 'lib/moxml/adapter/leptris.rb', line 1455
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.
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
|
# File 'lib/moxml/adapter/leptris.rb', line 1011
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
1465
1466
1467
1468
|
# File 'lib/moxml/adapter/leptris.rb', line 1465
def set_attribute_value(attr, value)
attr.value = value.to_s
attr
end
|
.set_cdata_content(node, content) ⇒ Object
1685
1686
1687
1688
|
# File 'lib/moxml/adapter/leptris.rb', line 1685
def set_cdata_content(node, content)
node = to_binding(node) if NATIVE_READ_LAYER
node.content = content.to_s
end
|
1696
1697
1698
1699
|
# File 'lib/moxml/adapter/leptris.rb', line 1696
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
929
930
931
|
# File 'lib/moxml/adapter/leptris.rb', line 929
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
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
|
# File 'lib/moxml/adapter/leptris.rb', line 955
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
1144
1145
1146
1147
1148
1149
1150
|
# File 'lib/moxml/adapter/leptris.rb', line 1144
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
1707
1708
1709
1710
|
# File 'lib/moxml/adapter/leptris.rb', line 1707
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
606
607
608
|
# File 'lib/moxml/adapter/leptris.rb', line 606
def set_root(doc, element)
to_binding(doc).root = to_binding(element)
end
|
.set_text_content(node, content) ⇒ Object
1669
1670
1671
1672
1673
1674
1675
1676
1677
|
# File 'lib/moxml/adapter/leptris.rb', line 1669
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.
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
|
# File 'lib/moxml/adapter/leptris.rb', line 1277
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
1415
1416
1417
1418
1419
|
# File 'lib/moxml/adapter/leptris.rb', line 1415
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.
828
829
830
831
832
833
834
|
# File 'lib/moxml/adapter/leptris.rb', line 828
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
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
|
# File 'lib/moxml/adapter/leptris.rb', line 836
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.
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.
1924
1925
1926
1927
1928
1929
1930
|
# File 'lib/moxml/adapter/leptris.rb', line 1924
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
1761
1762
1763
1764
1765
1766
1767
|
# File 'lib/moxml/adapter/leptris.rb', line 1761
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
|