Class: Lutaml::Xml::FormatChooser
- Inherits:
-
Object
- Object
- Lutaml::Xml::FormatChooser
- Defined in:
- lib/lutaml/xml/format_chooser.rb
Overview
Handles namespace format decision logic (default vs prefix)
Implements multi-tier priority system: Tier 1: Stored plan from parsed XML (format preservation) Tier 2: Explicit user preference (options) Tier 3: W3C rules (attributes require prefix) Tier 4: Default preference (cleaner)
Instance Method Summary collapse
-
#build_declaration(ns_class, format, options = {}, prefix_override: nil) ⇒ String
Build xmlns declaration string from XmlNamespace class.
-
#choose(mapping, needs, options) ⇒ Symbol
Choose format for namespace declaration.
-
#choose_with_override(mapping, effective_ns_class, needs, options, plan: nil) ⇒ Symbol
Choose format for namespace declaration with override support.
-
#initialize(register = nil) ⇒ FormatChooser
constructor
Initialize format chooser with register for type resolution.
Constructor Details
#initialize(register = nil) ⇒ FormatChooser
Initialize format chooser with register for type resolution
21 22 23 |
# File 'lib/lutaml/xml/format_chooser.rb', line 21 def initialize(register = nil) @register = register || Lutaml::Model::Config.default_register end |
Instance Method Details
#build_declaration(ns_class, format, options = {}, prefix_override: nil) ⇒ String
Build xmlns declaration string from XmlNamespace class
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/lutaml/xml/format_chooser.rb', line 189 def build_declaration(ns_class, format, = {}, prefix_override: nil) # CRITICAL: If namespace has no prefix, MUST use default format # Using prefix format without a prefix creates invalid xmlns:="" if format == :prefix && !ns_class.prefix_default && !prefix_override format = :default end if format == :default "xmlns=\"#{ns_class.uri}\"" else # PRIORITY ORDER: explicit override > options > class default prefix = prefix_override || ([:prefix].is_a?(String) ? [:prefix] : nil) || ([:use_prefix].is_a?(String) ? [:use_prefix] : nil) || ns_class.prefix_default "xmlns:#{prefix}=\"#{ns_class.uri}\"" end end |
#choose(mapping, needs, options) ⇒ Symbol
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 |
# File 'lib/lutaml/xml/format_chooser.rb', line 37 def choose(mapping, needs, ) return :default unless mapping.namespace_class # 1. Explicit user preference via prefix or use_prefix option # Check options[:prefix] first for backward compatibility if [:prefix].is_a?(String) return :prefix elsif [:use_prefix].is_a?(String) return :prefix end # 2. W3C rule: attributes in own namespace REQUIRE prefix # Check if this namespace is used for attributes (by key lookup) key = mapping.namespace_class.to_key if needs[:namespaces][key] ns_entry = needs[:namespaces][key] # Own namespace used in attributes → MUST use prefix return :prefix if ns_entry[:used_in].include?(:attributes) # Cascading prefix: If children need this namespace with prefix, provide it return :prefix if ns_entry[:children_need_prefix] end # 3. Check if any child elements use :inherit # If they do and we have a prefix, use prefixed format # so children can properly reference the namespace if mapping.namespace_class.prefix_default && mapping.respond_to?(:elements) has_inherit_children = mapping.elements.any? do |elem_rule| elem_rule.namespace_param == :inherit end return :prefix if has_inherit_children # Also check if any children have form: :qualified # They need prefixed format to reference parent namespace has_qualified_children = mapping.elements.any?(&:qualified?) return :prefix if has_qualified_children end # 4. Default: prefer default namespace (cleaner, no prefix needed) :default end |
#choose_with_override(mapping, effective_ns_class, needs, options, plan: nil) ⇒ Symbol
Choose format for namespace declaration with override support
Supports custom prefix overrides and stored plan format preservation. This is the main entry point that includes Tier 1 priority check.
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/lutaml/xml/format_chooser.rb', line 90 def choose_with_override(mapping, effective_ns_class, needs, , plan: nil) return :default unless effective_ns_class # Tier 1: Check for stored plan format (preserve from parsed XML) # Tier 1a: Check input_prefix_formats for doubly-defined prefix variants # When same URI appears with multiple prefixes (e.g., xmlns:a="..." and xmlns:b="..."), # we need to preserve the specific prefix format used at each element. # Only check if no explicit user preference is set (prefix: true/false overrides this). has_explicit_pref = .key?(:prefix) || .key?(:use_prefix) if !has_explicit_pref && [:stored_xml_declaration_plan] stored_plan = [:stored_xml_declaration_plan] if stored_plan.respond_to?(:input_prefix_formats) # Check canonical URI and all aliases uris_to_check = [effective_ns_class.uri] + effective_ns_class.uri_aliases stored_plan.input_prefix_formats.each do |key, format| matching_uri = uris_to_check.find { |u| key.end_with?(":#{u}") } next unless matching_uri prefix_in_key = key.split(":").first next if prefix_in_key.empty? # skip default namespace return format end end end # Tier 1b: Check stored plan format (legacy namespace-level format) # CRITICAL: This is the format preservation fix from Session 167 # Only check if no explicit user preference is set (prefix: true/false overrides this). unless has_explicit_pref if plan && [:stored_xml_declaration_plan] stored_plan = [:stored_xml_declaration_plan] input_ns_decl = stored_plan.namespaces.values.find do |decl| decl.from_input? && decl.uri == effective_ns_class.uri end return input_ns_decl.format if input_ns_decl elsif plan # Fallback: check current plan for input namespaces input_ns_decl = plan.namespaces.values.find do |decl| decl.from_input? && decl.uri == effective_ns_class.uri end return input_ns_decl.format if input_ns_decl end end # Tier 2: Explicit user preference via prefix or use_prefix option # Check both options[:prefix] (direct call) and options[:use_prefix] (from serialize.rb) if .key?(:prefix) case [:prefix] when true, String return :prefix when false, nil return :default end elsif .key?(:use_prefix) # options[:use_prefix] can be a string (custom prefix) or boolean case [:use_prefix] when true, String return :prefix when false, nil return :default end end # Tier 3: W3C rule: attributes in same namespace require prefix # Cascading prefix requirement from children key = effective_ns_class.to_key if needs[:namespaces][key] ns_entry = needs[:namespaces][key] # Own namespace used in attributes → MUST use prefix return :prefix if ns_entry[:used_in].include?(:attributes) # Cascading prefix: If children need this namespace with prefix, provide it return :prefix if ns_entry[:children_need_prefix] end # 3. Check if any child elements use :inherit or form: :qualified if effective_ns_class.prefix_default && mapping.respond_to?(:elements) has_inherit_children = mapping.elements.any? do |elem_rule| elem_rule.namespace_param == :inherit end return :prefix if has_inherit_children has_qualified_children = mapping.elements.any?(&:qualified?) return :prefix if has_qualified_children end # Tier 4: Default: prefer default namespace (cleaner, no prefix needed) :default end |