Class: Lutaml::Xml::AdapterElement

Inherits:
XmlElement show all
Defined in:
lib/lutaml/xml/adapter_element.rb

Overview

Base element wrapper for moxml-backed XML adapters. NokogiriElement, Ox::Element, Oga::Element, Rexml::Element all inherit from this class.

Constant Summary collapse

NamespaceData =
Lutaml::Xml::Adapter::NamespaceData

Constants inherited from XmlElement

XmlElement::CDATA_NODE_NAME, XmlElement::EMPTY_CHILDREN_ARRAY, XmlElement::EMPTY_NAMESPACES, XmlElement::NODE_TYPES, XmlElement::TEXT_NODE_NAME, XmlElement::XMLNS_PREFIX, XmlElement::XML_NAMESPACE_URI

Instance Attribute Summary

Attributes inherited from XmlElement

#adapter_node, #attribute_order, #attributes, #namespace_prefix, #namespace_prefix_explicit, #node_type, #order_cache, #parent_document, #processing_instructions

Instance Method Summary collapse

Methods inherited from XmlElement

#[], #add_namespace, #attribute_is_namespace?, #cdata, #cdata?, #cdata_children, #comment?, #default_namespace, detect_explicit_no_namespace, #document, #element?, #element_children_index, #ensure_attribute_index, #ensure_children_index, #find_attribute_value, #find_child_by_name, #find_children_by_name, fpi?, fpi_to_urn, #name, #namespace, #namespace_scope_config, #namespace_uri, #namespaced_name, #namespaces, #nil_element?, #original_namespace_uri, #original_xml_element, #own_namespaces, #pretty_print_instance_variables, #processing_instruction?, #root, #text_children, #to_h, #unprefixed_name, #xml_declaration, #xml_namespace_prefix

Constructor Details

#initialize(node, parent: nil, default_namespace: nil) ⇒ AdapterElement

Returns a new instance of AdapterElement.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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
# File 'lib/lutaml/xml/adapter_element.rb', line 11

def initialize(node, parent: nil, default_namespace: nil)
  @moxml_node = node

  node_type = case node
              when Moxml::Cdata then :cdata
              when Moxml::Text then :text
              when Moxml::Comment then :comment
              when Moxml::ProcessingInstruction then :processing_instruction
              else :element
              end

  text = case node
         when Moxml::Element
           namespace_name = node.namespace&.prefix
           # moxml >= 0.5.33 contract: #namespaces is the full
           # in-scope map (parity with Nokogiri, #198/#201); the
           # own-declarations shape this parse path needs is
           # #namespace_definitions.
           ns_defs = node.namespace_definitions

           has_empty_xmlns = ns_defs.any? do |ns|
             ns.prefix.nil? && ns.uri == ""
           end

           explicit_no_namespace = XmlElement.detect_explicit_no_namespace(
             has_empty_xmlns: has_empty_xmlns,
             node_namespace_nil: node.namespace.nil? || node.namespace&.uri == "",
           )

           add_namespaces_from_defs(ns_defs, is_root: parent.nil?)

           if parent.nil? && !namespace_name && node.namespace&.uri &&
               node.namespace.uri != ""
             default_namespace = node.namespace.uri
           end

           children = parse_children(node,
                                     default_namespace: default_namespace)
           attributes, attr_order = node_attributes_with_order(node)
           @root = node
           EncodingNormalizer.normalize_to_utf8(node.inner_text)
         when Moxml::Text
           EncodingNormalizer.normalize_to_utf8(node.content)
         when Moxml::Cdata
           EncodingNormalizer.normalize_to_utf8(node.content)
         when Moxml::Comment
           EncodingNormalizer.normalize_to_utf8(node.content)
         when Moxml::ProcessingInstruction
           EncodingNormalizer.normalize_to_utf8(
             node.content.to_s.sub(/\A\s+/, ""),
           )
         end

  name = adapter_class.name_of(node)
  super(
    node,
    attributes || NO_ATTRIBUTES,
    children || NO_CHILDREN,
    text,
    name: name,
    parent_document: parent,
    namespace_prefix: namespace_name,
    default_namespace: default_namespace,
    explicit_no_namespace: explicit_no_namespace || false,
    node_type: node_type,
    attribute_order: attr_order
  )
end

Instance Method Details

#add_namespaces_from_defs(ns_defs, is_root: false) ⇒ Object



311
312
313
314
315
316
317
318
# File 'lib/lutaml/xml/adapter_element.rb', line 311

def add_namespaces_from_defs(ns_defs, is_root: false)
  has_default_xmlns = is_root || ns_defs.any? { |ns| ns.prefix.nil? }

  ns_defs.each do |namespace|
    ns = NamespaceData.new(namespace.uri, namespace.prefix)
    add_namespace(ns) if ns.prefix || has_default_xmlns
  end
end

#attr_is_namespace?(attr) ⇒ Boolean

Returns:

  • (Boolean)


320
321
322
323
# File 'lib/lutaml/xml/adapter_element.rb', line 320

def attr_is_namespace?(attr)
  attribute_is_namespace?(attr.name) ||
    namespaces[attr.name]&.uri == attr.value
end

#build_attributes(node, _options = {}) ⇒ Object



325
326
327
328
# File 'lib/lutaml/xml/adapter_element.rb', line 325

def build_attributes(node, _options = {})
  attrs = node.attributes.transform_values(&:value)
  attrs.merge(build_namespace_attributes(node))
end

#build_namespace_attributes(node) ⇒ Object



330
331
332
333
334
335
336
337
338
339
340
# File 'lib/lutaml/xml/adapter_element.rb', line 330

def build_namespace_attributes(node)
  namespace_attrs = {}

  node.own_namespaces.each_value do |namespace|
    uri = namespace.uri
    uri = XmlElement.fpi_to_urn(uri) if XmlElement.fpi?(uri)
    namespace_attrs[namespace.attr_name] = uri
  end

  namespace_attrs
end

#build_xml(builder = nil) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/lutaml/xml/adapter_element.rb', line 92

def build_xml(builder = nil)
  if cdata?
    builder.add_text(builder.current_node, @text.to_s, cdata: true)
  elsif comment?
    builder.add_comment(builder.current_node, @text.to_s)
  elsif processing_instruction?
    builder.add_processing_instruction(name, @text.to_s)
  elsif text? && !element?
    builder.add_text(builder.current_node, build_text_for_xml.to_s)
  else
    build_element_xml(builder)
  end

  builder
end

#childrenObject



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/lutaml/xml/adapter_element.rb', line 217

def children
  return @children if @children.empty?

  unless @non_element_children_wrapped
    @children.map! do |child|
      if child.is_a?(Moxml::Node) && !child.is_a?(Moxml::Element)
        self.class.new(child, parent: self)
      else
        child
      end
    end
    @non_element_children_wrapped = true
  end
  @children
end

#children=(new_children) ⇒ Object



233
234
235
236
# File 'lib/lutaml/xml/adapter_element.rb', line 233

def children=(new_children)
  @non_element_children_wrapped = true
  super
end

#element_childrenObject



238
239
240
241
242
243
244
245
246
# File 'lib/lutaml/xml/adapter_element.rb', line 238

def element_children
  return @element_children if defined?(@element_children)

  @element_children = @children.reject do |child|
    raw_non_element_child?(child) ||
      (child.is_a?(XmlElement) &&
        (child.text? || child.processing_instruction?))
  end
end

#inner_xmlObject



108
109
110
# File 'lib/lutaml/xml/adapter_element.rb', line 108

def inner_xml
  children.map(&:to_xml).join
end

#inner_xml_or_selfObject

The element including its own tags — the building block for scoped raw capture (#223): an unclaimed child contributes its whole subtree verbatim.



115
116
117
# File 'lib/lutaml/xml/adapter_element.rb', line 115

def inner_xml_or_self
  to_xml
end

#orderObject

Order consumes raw non-element children inline: wrapping them just to read .text/.content would materialize the whole lazy layer on every parse.



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/lutaml/xml/adapter_element.rb', line 255

def order
  return @order_cache if @order_cache

  @order_cache = @children.filter_map do |child|
    case child
    when Moxml::Cdata
      Lutaml::Xml::Element.new("Text", "#cdata-section",
                               text_content: child.content,
                               node_type: :cdata)
    when Moxml::Text
      next if child.content.nil?

      Lutaml::Xml::Element.new("Text", "text",
                               text_content: child.content,
                               node_type: :text)
    when Moxml::Comment
      Lutaml::Xml::Element.new("Comment", "comment",
                               text_content: child.content,
                               node_type: :comment)
    when Moxml::ProcessingInstruction
      Lutaml::Xml::Element.new("ProcessingInstruction",
                               child.target,
                               text_content: child.content.to_s.sub(/\A\s+/, ""),
                               node_type: :processing_instruction)
    else
      next if child.is_a?(Moxml::Node)

      if child.cdata?
        Lutaml::Xml::Element.new("Text", "#cdata-section",
                                 text_content: child.text,
                                 node_type: :cdata)
      elsif child.text?
        next if child.text.nil?

        Lutaml::Xml::Element.new("Text", "text",
                                 text_content: child.text,
                                 node_type: :text)
      elsif child.comment?
        Lutaml::Xml::Element.new("Comment", "comment",
                                 text_content: child.text,
                                 node_type: :comment)
      elsif child.processing_instruction?
        Lutaml::Xml::Element.new("ProcessingInstruction",
                                 child.unprefixed_name,
                                 text_content: child.text,
                                 node_type: :processing_instruction)
      else
        Lutaml::Xml::Element.new("Element", child.unprefixed_name,
                                 node_type: :element,
                                 namespace_uri: child.namespace_uri,
                                 namespace_prefix: child.namespace_prefix)
      end
    end
  end.each(&:freeze).freeze
end

#raw_non_element_child?(child) ⇒ Boolean

Returns:

  • (Boolean)


248
249
250
# File 'lib/lutaml/xml/adapter_element.rb', line 248

def raw_non_element_child?(child)
  child.is_a?(Moxml::Node) && !child.is_a?(Moxml::Element)
end

#textObject



84
85
86
# File 'lib/lutaml/xml/adapter_element.rb', line 84

def text
  super || @text
end

#text?Boolean

Returns:

  • (Boolean)


80
81
82
# File 'lib/lutaml/xml/adapter_element.rb', line 80

def text?
  i[text cdata].include?(@node_type)
end

#to_xml(_builder = nil) ⇒ Object



88
89
90
# File 'lib/lutaml/xml/adapter_element.rb', line 88

def to_xml(_builder = nil)
  @moxml_node.to_xml(declaration: false, expand_empty: false)
end