Module: Leptris::XML::SAX::DomDispatch
- Defined in:
- lib/leptris/xml/sax/dom_dispatch.rb
Overview
DOM-backed SAX dispatch (leptris-ruby#95): the engine's streaming attribute buffer corrupts leading attribute pairs when several nested ancestor levels each carry attributes — the callback transport, the recorder's packed arena, AND the pull parser all deliver the loss on the issue fixture (verified), while the DOM parser reads the same bytes correctly. Until the engine fix lands, SAX::Parser delivers from a DOM parse: same call shapes as the streaming transports — attach-only-overridden kinds, one-argument start_element arity, QName element names ("p:a"), UTF-8 strings, PI-data normalization.
The walk is lean by construction: elements wrap (identity, name and prefix memos); every other child kind is read straight from its pointer — content getters per node type, never a wrapper.
Constant Summary collapse
- NODE =
Leptris::XML::FFI
- ELEMENT =
Leptris::XML::FFI::NODE_ELEMENT
- TEXT =
Leptris::XML::FFI::NODE_TEXT
- COMMENT =
Leptris::XML::FFI::NODE_COMMENT
- CDATA =
Leptris::XML::FFI::NODE_CDATA
- PI =
Leptris::XML::FFI::NODE_PI
Class Method Summary collapse
-
.attr_pairs(c_ptr) ⇒ Object
[name, value] pairs in source order, read straight from the attribute chain — no Attr objects, no hash.
-
.declared_pairs(element) ⇒ Object
start_element's pairs with xmlns declarations carried — the engine's streaming contract reports declarations among the attribute pairs at their byte positions.
- .dispatch(handler, dispatched, doc) ⇒ Object
- .parse(handler, dispatched, string) ⇒ Object
- .utf8(str) ⇒ Object
- .visit_children(c_ptr, doc, handler, dispatched, element_parent: true) ⇒ Object
- .visit_element(element, handler, dispatched) ⇒ Object
Class Method Details
.attr_pairs(c_ptr) ⇒ Object
[name, value] pairs in source order, read straight from the attribute chain — no Attr objects, no hash.
129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/leptris/xml/sax/dom_dispatch.rb', line 129 def attr_pairs(c_ptr) pairs = [] attr = Leptris::XML::FFI.leptris_element_first_attribute(c_ptr) until attr.nil? || attr.null? pairs << [utf8(Leptris::XML::FFI.leptris_attribute_get_name(attr)), utf8(Leptris::XML::FFI.leptris_attribute_get_value( c_ptr, attr))] attr = Leptris::XML::FFI.leptris_attribute_next(attr) end pairs end |
.declared_pairs(element) ⇒ Object
start_element's pairs with xmlns declarations carried — the engine's streaming contract reports declarations among the attribute pairs at their byte positions. Since libleptris 1.9.18 (upstream #635, closing leptris-ruby#99's documented difference), the raw qname-ordered list reproduces the exact interleave.
123 124 125 |
# File 'lib/leptris/xml/sax/dom_dispatch.rb', line 123 def declared_pairs(element) Leptris::XML::FFI.fetch_attributes_raw(element.c_ptr) end |
.dispatch(handler, dispatched, doc) ⇒ Object
29 30 31 32 33 34 35 36 37 38 |
# File 'lib/leptris/xml/sax/dom_dispatch.rb', line 29 def dispatch(handler, dispatched, doc) handler.start_document if dispatched[:start_document] doc_node = doc.node if doc_node visit_children(doc_node.c_ptr, doc, handler, dispatched, element_parent: false) end handler.end_document if dispatched[:end_document] self end |
.parse(handler, dispatched, string) ⇒ Object
20 21 22 23 24 25 26 27 |
# File 'lib/leptris/xml/sax/dom_dispatch.rb', line 20 def parse(handler, dispatched, string) doc = Leptris::XML::Document.parse(string) begin dispatch(handler, dispatched, doc) ensure doc.free end end |
.utf8(str) ⇒ Object
141 142 143 |
# File 'lib/leptris/xml/sax/dom_dispatch.rb', line 141 def utf8(str) str.nil? ? nil : str.force_encoding(Encoding::UTF_8) end |
.visit_children(c_ptr, doc, handler, dispatched, element_parent: true) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/leptris/xml/sax/dom_dispatch.rb', line 47 def visit_children(c_ptr, doc, handler, dispatched, element_parent: true) wants_text_kinds = dispatched[:characters] || dispatched[:comment] || dispatched[:cdata] || dispatched[:pi] if element_parent && !wants_text_kinds # Element-only handlers under an element: the element batch # hands out element pointers with no per-child get_type # (round XXIII's path) — text/comments/PIs are never fetched. # (The document node is not an element handle — the typed # walk serves it.) Leptris::XML::FFI.fetch_element_children(c_ptr).each do |child_ptr| visit_element( Leptris::XML::Node.wrap(child_ptr, doc, node_type: ELEMENT), handler, dispatched) end return end pointers, kinds = Leptris::XML::FFI.fetch_children(c_ptr) pointers.each_with_index do |child_ptr, i| case kinds[i] when ELEMENT visit_element( Leptris::XML::Node.wrap(child_ptr, doc, node_type: ELEMENT), handler, dispatched) when TEXT if dispatched[:characters] handler.characters(utf8(NODE.leptris_text_node_get_content(child_ptr))) end when CDATA if dispatched[:cdata] handler.cdata_block(utf8(NODE.leptris_cdata_node_get_content(child_ptr))) end when COMMENT if dispatched[:comment] handler.comment(utf8(NODE.leptris_comment_node_get_content(child_ptr))) end when PI if dispatched[:pi] handler.processing_instruction( utf8(NODE.leptris_pi_node_get_target(child_ptr)), Leptris::XML::FFI.read_pi_data( utf8(NODE.leptris_pi_node_get_data(child_ptr)))) end end end end |
.visit_element(element, handler, dispatched) ⇒ Object
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/leptris/xml/sax/dom_dispatch.rb', line 94 def visit_element(element, handler, dispatched) qname = element.prefix ? "#{element.prefix}:#{element.name}" : element.name if dispatched[:start_prefix] element.namespace_definitions.each do |ns| handler.start_prefix_mapping(ns.prefix.to_s, ns.href.to_s) end end if dispatched[:start_element] if dispatched[:start_element] == :one_arg handler.start_element(qname) else handler.start_element(qname, declared_pairs(element)) end end visit_children(element.c_ptr, element.document, handler, dispatched) handler.end_element(qname) if dispatched[:end_element] if dispatched[:end_prefix] element.namespace_definitions.reverse_each do |ns| handler.end_prefix_mapping(ns.prefix.to_s) end end end |