Class: Moxml::C14n::DataModel

Inherits:
Object
  • Object
show all
Defined in:
lib/moxml/c14n/data_model.rb

Overview

Builds a C14N data model from a Moxml::Node tree (or XML string).

The data model exists because canonicalization needs:

- sorted namespace and attribute axes (spec §2.3, §2.4)
- in_node_set flags for subset canonicalization (spec §3)
- xml:* inheritable attribute resolution

Ported from canon (lutaml/canon) — adapted to build directly from Moxml::Node rather than via a separate Nokogiri pass. Matches canon's document-level node iteration (PIs and comments outside the document root element).

Constant Summary collapse

BUILDABLE_TYPES =
[::Moxml::Element, ::Moxml::Text, ::Moxml::Cdata,
::Moxml::Comment,
::Moxml::ProcessingInstruction].freeze

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.source_map ⇒ Object (readonly)

Returns the value of attribute source_map.



38
39
40
# File 'lib/moxml/c14n/data_model.rb', line 38

def source_map
  @source_map
end

Class Method Details

.buildable?(node) ⇒ Boolean

Returns:

  • (Boolean)


45
46
47
# File 'lib/moxml/c14n/data_model.rb', line 45

def self.buildable?(node)
  BUILDABLE_TYPES.any? { |type| node.is_a?(type) }
end

.from_node(moxml_node) ⇒ Object



21
22
23
24
25
26
27
28
29
# File 'lib/moxml/c14n/data_model.rb', line 21

def self.from_node(moxml_node)
  @source_map = {}
  return build_from_document(moxml_node) if moxml_node.is_a?(::Moxml::Document)

  root = Nodes::RootNode.new
  built = build_node(moxml_node, [0])
  root.add_child(built) if built
  root
end

.from_xml(xml_string) ⇒ Object



17
18
19
# File 'lib/moxml/c14n/data_model.rb', line 17

def self.from_xml(xml_string)
  from_node(::Moxml.parse(xml_string))
end

.mark_subset_paths(root_node, paths) ⇒ Object

Mark an XPath-selected subset: reset the whole model, then flag each matched node by position path. Node-set semantics follow the enveloped-signature interop (libxml2/xmlsec): an included element renders with its namespaces, attributes and DIRECT character data, but child ELEMENTS render only if matched too — an unmatched Signature subtree stays out even though its ancestors are matched. Comments and PIs render only when the expression itself selects them (spec §3: the node-set is literal).



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/moxml/c14n/data_model.rb', line 243

def self.mark_subset_paths(root_node, paths)
  mark_all(root_node, false)
  paths.each do |path|
    node = lookup_path(root_node, path)
    next unless node

    if node.is_a?(Nodes::ElementNode)
      node.in_node_set = true
      node.namespace_nodes.each { |ns| ns.in_node_set = true }
      node.attribute_nodes.each { |attr| attr.in_node_set = true }
      node.children.each do |child|
        child.in_node_set = true if child.is_a?(Nodes::TextNode)
      end
    else
      node.in_node_set = true
    end
  end
end

.node_path(node, source) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/moxml/c14n/data_model.rb', line 79

def self.node_path(node, source)
  parts = []
  cur = node
  loop do
    if !source.is_a?(::Moxml::Document) && cur.equal?(source)
      parts.unshift(0)
      return parts
    end

    parent = cur.parent
    return nil unless parent

    idx = parent.children.select { |c| buildable?(c) }.index { |c| c == cur }
    return nil unless idx

    parts.unshift(idx)
    return parts if source.is_a?(::Moxml::Document) && parent.is_a?(::Moxml::Document)

    cur = parent
  end
end

.path_key_for(wrapper, source, adapter) ⇒ Object

Path key of a matched wrapper relative to the source the data model was built from; nil when the wrapper is outside it. Attributes extend their owner's path with [:attribute, index]. The adapter comes from the caller: the Attribute contract keeps its own adapter hop protected.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/moxml/c14n/data_model.rb', line 54

def self.path_key_for(wrapper, source, adapter)
  case wrapper
  when ::Moxml::Attribute
    owner = wrapper.parent
    unless owner.is_a?(::Moxml::Element)
      # XPath results are re-wrapped from natives: attribute
      # wrappers carry no minted parent. The adapter answers
      # the owner element instead.
      owner_native = adapter.attribute_element(wrapper.native)
      owner = owner_native && ::Moxml::Node.wrap(owner_native, wrapper.context)
    end
    return nil unless owner.is_a?(::Moxml::Element)

    owner_path = node_path(owner, source)
    return nil unless owner_path

    idx = owner.attributes.index { |a| a == wrapper }
    return nil unless idx

    owner_path + [:attribute, idx]
  when *BUILDABLE_TYPES
    node_path(wrapper, source)
  end
end