Class: Lutaml::Xml::NamespaceCollector

Inherits:
Object
  • Object
show all
Defined in:
lib/lutaml/xml/namespace_collector.rb

Overview

Phase 1: Bottom-Up Collection

Walks the model tree from leaves to root, collecting namespace needs. This provides full tree knowledge before any declaration decisions are made.

CRITICAL ARCHITECTURAL PRINCIPLE: XmlNamespace CLASS is the atomic unit of namespace configuration. Never track URI and prefix separately - they are inseparable.

REFACTORED (Session 176): Returns NamespaceNeeds object instead of schema-less hash Uses NamespaceUsage, TypeNamespace::Reference, NamespaceScopeConfig objects

Examples:

collector = NamespaceCollector.new(register)
needs = collector.collect(model_instance, mapping)

Instance Method Summary collapse

Constructor Details

#initialize(register = nil) ⇒ NamespaceCollector

Initialize collector with register for type resolution

Parameters:

  • register (Symbol) (defaults to: nil)

    the register ID for type resolution



26
27
28
29
30
31
32
# File 'lib/lutaml/xml/namespace_collector.rb', line 26

def initialize(register = nil)
  @register = register || Lutaml::Model::Config.default_register

  # VISITED TYPE TRACKING
  # Thread-local call stack for circular reference detection
  Thread.current[:namespace_collector_call_stack] ||= []
end

Instance Method Details

#all_namespaces(needs) ⇒ Set<Class>

Get all unique namespaces that need declaration

Parameters:

Returns:

  • (Set<Class>)

    set of XmlNamespace classes



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

def all_namespaces(needs)
  needs.all_namespace_classes
end

#collect(element, mapping, visited: nil) ⇒ NamespaceNeeds

Collect namespace needs for an element and its descendants

Parameters:

  • element (Object)

    the model instance (can be nil for type analysis)

  • mapping (Xml::Mapping)

    the XML mapping for this element

  • options (Hash)

    additional options including mapper_class for recursive calls

Returns:



40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/lutaml/xml/namespace_collector.rb', line 40

def collect(element, mapping, visited: nil, **)
  # If this is a top-level call (no visited yet), ensure cleanup
  if visited.nil?
    Thread.current[:namespace_collector_call_stack] = []
    begin
      collect_internal(element, mapping, visited: nil, **)
    ensure
      Thread.current[:namespace_collector_call_stack] = nil
    end
  else
    # Nested call - don't clean up yet
    collect_internal(element, mapping, visited: visited, **)
  end
end

#collect_collection(collection, mapping) ⇒ NamespaceNeeds

Collect namespace needs for a collection of instances

Parameters:

  • collection (Collection)

    the collection instance

  • mapping (Xml::Mapping)

    the XML mapping for the collection

Returns:



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
# File 'lib/lutaml/xml/namespace_collector.rb', line 60

def collect_collection(collection, mapping)
  needs = collect(nil, mapping)

  # If collection has instances, collect needs from instance type
  if collection.respond_to?(:instances) &&
      mapping.respond_to?(:find_element)
    instance_rule = mapping.find_element(:instances) ||
      mapping.elements.first
    if instance_rule
      attr_def = collection.class.attributes[instance_rule.to]
      if attr_def
        instance_type = attr_def.type(@register)
        if instance_type.respond_to?(:<) &&
            instance_type < Lutaml::Model::Serialize
          instance_mapping = instance_type.mappings_for(:xml)
          if instance_mapping
            instance_needs = collect(nil, instance_mapping)
            needs.merge(instance_needs)
          end
        end
      end
    end
  end

  needs
end

#namespace_used?(needs, namespace_class) ⇒ Boolean

Check if a namespace is used in descendants

Parameters:

  • needs (NamespaceNeeds)

    the collected namespace needs

  • namespace_class (Class)

    the XmlNamespace class to check

Returns:

  • (Boolean)

    true if namespace is used in tree



117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/lutaml/xml/namespace_collector.rb', line 117

def namespace_used?(needs, namespace_class)
  validate_namespace_class(namespace_class)

  # Look up by string key
  key = namespace_class.to_key
  return true if needs.namespace(key)

  needs.children.each_value do |child_needs|
    return true if namespace_used?(child_needs, namespace_class)
  end

  false
end

#needs_prefix?(needs, mapping) ⇒ Boolean

Check if namespace needs require prefix for root element

This implements the W3C rule: if any XML attributes are in the same namespace as the root element, the root must use a prefix.

Parameters:

Returns:

  • (Boolean)

    true if prefix is required



95
96
97
98
99
100
101
102
# File 'lib/lutaml/xml/namespace_collector.rb', line 95

def needs_prefix?(needs, mapping)
  return false unless mapping.namespace_class

  # Look up by string key
  key = mapping.namespace_class.to_key
  usage = needs.namespace(key)
  usage&.used_in_attributes?
end