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,
    Hash(attributes),
    Array(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



291
292
293
294
295
296
297
298
# File 'lib/lutaml/xml/adapter_element.rb', line 291

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)


300
301
302
303
# File 'lib/lutaml/xml/adapter_element.rb', line 300

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

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



305
306
307
308
# File 'lib/lutaml/xml/adapter_element.rb', line 305

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

#build_namespace_attributes(node) ⇒ Object



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

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



199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/lutaml/xml/adapter_element.rb', line 199

def children
  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



213
214
215
216
# File 'lib/lutaml/xml/adapter_element.rb', line 213

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

#element_childrenObject



218
219
220
221
222
223
224
225
226
# File 'lib/lutaml/xml/adapter_element.rb', line 218

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

#orderObject

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



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
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
# File 'lib/lutaml/xml/adapter_element.rb', line 235

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)


228
229
230
# File 'lib/lutaml/xml/adapter_element.rb', line 228

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