Class: WSDL::Schema::Node
- Inherits:
-
Object
- Object
- WSDL::Schema::Node
- Defined in:
- lib/wsdl/schema/node.rb
Overview
Unified representation of all XSD schema nodes.
Instead of a deep class hierarchy where most classes are empty, this single class represents all XSD constructs and uses the #kind attribute to determine behavior.
rubocop:disable Metrics/ClassLength
Constant Summary collapse
- ELEMENT_TERMINATORS =
Node kinds that terminate element collection (contain no child elements).
Set[ :attribute, :annotation, :simpleContent ].freeze
- ATTRIBUTE_TERMINATORS =
Node kinds that terminate attribute collection.
Set[ :annotation ].freeze
- ELEMENT_KINDS =
Node kinds representing actual schema elements.
Set[:element, :any].freeze
- ATTRIBUTE_KINDS =
Node kinds representing attributes.
Set[:attribute].freeze
- INLINE_TYPE_KINDS =
Node kinds that can have inline type definitions.
Set[:complexType, :simpleType].freeze
Instance Attribute Summary collapse
-
#kind ⇒ Symbol
readonly
The XSD element type (:element, :complexType, :sequence, etc.).
-
#xml_node ⇒ Nokogiri::XML::Node?
readonly
The underlying XML node (nil after #release_dom_references!).
Common Attributes collapse
-
#[](attr_name) ⇒ String?
Accesses any attribute from the underlying node.
-
#base ⇒ String?
The base type for restrictions/extensions.
-
#default ⇒ String?
The default value.
-
#fixed ⇒ String?
The fixed value.
-
#form ⇒ String
Returns the element form (qualified or unqualified).
-
#name ⇒ String?
The local name of this node.
-
#namespace ⇒ String?
The target namespace URI.
-
#nillable? ⇒ Boolean
Whether this element allows nil values (xsi:nil="true").
-
#ref ⇒ String?
The qualified element/attribute reference.
-
#type ⇒ String?
The qualified type reference.
-
#use ⇒ String
The use constraint ('optional' or 'required').
Children & Traversal collapse
-
#attributes(memo = [], limits: nil) ⇒ Array<Node>
Collects all attribute definitions from this node and descendants.
-
#children ⇒ Array<Node>
Returns the parsed child nodes.
-
#elements(memo = [], limits: nil) ⇒ Array<Node>
Collects all element definitions from this node and descendants.
-
#empty? ⇒ Boolean
Returns whether this node has no meaningful content.
Type Resolution collapse
-
#inline_type ⇒ Node?
Returns the inline type definition if present.
-
#list_item_type ⇒ String?
Returns the item type from a list derivation.
-
#restriction_base ⇒ String?
Returns the base type from a restriction child.
-
#type_id ⇒ String?
Returns a unique identifier for this type.
-
#union_member_types ⇒ String?
Returns the member types from a union derivation.
xs:any Wildcard Support collapse
-
#any? ⇒ Boolean
True if this is an xs:any wildcard.
-
#namespace_constraint ⇒ String
Returns the namespace constraint for wildcards.
-
#process_contents ⇒ String
Returns how wildcard content should be validated.
Cardinality collapse
-
#max_occurs ⇒ String
The maxOccurs value ('1', 'unbounded', etc.).
-
#min_occurs ⇒ String
The minOccurs value ('0', '1', etc.).
-
#multiple? ⇒ Boolean
Returns whether this element can appear multiple times.
-
#optional? ⇒ Boolean
Returns whether this element is optional.
-
#required? ⇒ Boolean
Returns whether this element is required.
Pattern Matching collapse
-
#deconstruct_keys(_keys) ⇒ Hash
Supports Ruby pattern matching.
Instance Method Summary collapse
-
#initialize(xml_node, collection, context = {}) ⇒ Node
constructor
Creates a new Node from an XSD element.
-
#inspect ⇒ String
Returns a string representation for debugging.
-
#namespaces ⇒ Hash{String => String}
Returns XML namespace declarations in scope.
-
#release_dom_references! ⇒ void
Releases all references to the Nokogiri DOM.
Constructor Details
#initialize(xml_node, collection, context = {}) ⇒ Node
Creates a new Node from an XSD element.
60 61 62 63 64 65 66 |
# File 'lib/wsdl/schema/node.rb', line 60 def initialize(xml_node, collection, context = {}) @xml_node = xml_node @collection = collection @context = context @kind = xml_node.name.to_sym end |
Instance Attribute Details
#kind ⇒ Symbol (readonly)
Returns the XSD element type (:element, :complexType, :sequence, etc.).
69 70 71 |
# File 'lib/wsdl/schema/node.rb', line 69 def kind @kind end |
#xml_node ⇒ Nokogiri::XML::Node? (readonly)
Returns the underlying XML node (nil after #release_dom_references!).
73 74 75 |
# File 'lib/wsdl/schema/node.rb', line 73 def xml_node @xml_node end |
Instance Method Details
#[](attr_name) ⇒ String?
Accesses any attribute from the underlying node.
168 169 170 |
# File 'lib/wsdl/schema/node.rb', line 168 def [](attr_name) @xml_node[attr_name] end |
#any? ⇒ Boolean
Returns true if this is an xs:any wildcard.
326 327 328 |
# File 'lib/wsdl/schema/node.rb', line 326 def any? kind == :any end |
#attributes(memo = [], limits: nil) ⇒ Array<Node>
Collects all attribute definitions from this node and descendants.
Traverses the type hierarchy to find all xs:attribute definitions. Handles attributeGroup references. Skips unresolvable references and continues collecting.
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/wsdl/schema/node.rb', line 255 def attributes(memo = [], limits: nil) return memo if ATTRIBUTE_TERMINATORS.include?(kind) return resolve_attribute_group_ref(memo, limits:) if kind == :attributeGroup && ref if @cached_attributes memo.concat(@cached_attributes) validate_attribute_count!(memo.size, limits) return memo end before = memo.size memo = collect_child_attributes(memo, limits:) @cached_attributes = memo[before..].freeze if kind == :complexType memo end |
#base ⇒ String?
Returns the base type for restrictions/extensions.
115 116 117 118 119 |
# File 'lib/wsdl/schema/node.rb', line 115 def base return @base if defined?(@base) @base = @xml_node['base'] end |
#children ⇒ Array<Node>
Returns the parsed child nodes.
197 198 199 |
# File 'lib/wsdl/schema/node.rb', line 197 def children @children ||= @xml_node.element_children.map { |n| Node.new(n, @collection, @context) } end |
#deconstruct_keys(_keys) ⇒ Hash
Supports Ruby pattern matching.
401 402 403 404 405 406 407 408 409 |
# File 'lib/wsdl/schema/node.rb', line 401 def deconstruct_keys(_keys) { kind:, name:, type:, ref:, namespace: } end |
#default ⇒ String?
Returns the default value.
129 130 131 132 133 |
# File 'lib/wsdl/schema/node.rb', line 129 def default return @default if defined?(@default) @default = @xml_node['default'] end |
#elements(memo = [], limits: nil) ⇒ Array<Node>
Collects all element definitions from this node and descendants.
Traverses the type hierarchy to find all xs:element definitions, which represent the actual content model of complex types. Handles extension base type inheritance. Skips unresolvable references and continues collecting.
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/wsdl/schema/node.rb', line 228 def elements(memo = [], limits: nil) return memo if ELEMENT_TERMINATORS.include?(kind) return resolve_group_ref(memo, limits:) if kind == :group && ref if @cached_elements memo.concat(@cached_elements) validate_element_count!(memo.size, limits) return memo end before = memo.size include_base_type_elements(memo, limits:) if kind == :extension collect_child_elements(memo, limits:) @cached_elements = memo[before..].freeze if kind == :complexType memo end |
#empty? ⇒ Boolean
Returns whether this node has no meaningful content.
A node is considered empty if it has no children, or if its only children are empty compositors (sequence, all, choice with no elements).
207 208 209 210 211 212 213 214 215 |
# File 'lib/wsdl/schema/node.rb', line 207 def empty? return true if children.empty? # Check if all children are empty compositors compositor_kinds = %i[sequence all choice] children.all? do |child| compositor_kinds.include?(child.kind) && child.children.empty? end end |
#fixed ⇒ String?
Returns the fixed value.
136 137 138 139 140 |
# File 'lib/wsdl/schema/node.rb', line 136 def fixed return @fixed if defined?(@fixed) @fixed = @xml_node['fixed'] end |
#form ⇒ String
Returns the element form (qualified or unqualified).
157 158 159 160 161 162 |
# File 'lib/wsdl/schema/node.rb', line 157 def form return @form if defined?(@form) @form = @xml_node['form'] || (@context[:element_form_default] == 'qualified' ? 'qualified' : 'unqualified') end |
#inline_type ⇒ Node?
Returns the inline type definition if present.
An inline type is a complex or simple type defined directly within the element rather than referenced by name. Skips annotation elements.
281 282 283 |
# File 'lib/wsdl/schema/node.rb', line 281 def inline_type children.find { |c| INLINE_TYPE_KINDS.include?(c.kind) } end |
#inspect ⇒ String
Returns a string representation for debugging.
416 417 418 419 |
# File 'lib/wsdl/schema/node.rb', line 416 def inspect attrs = @xml_node.attribute_nodes.map { |a| "#{a.name}=#{a.value.inspect}" }.join(' ') "#<Schema::Node:#{kind} #{attrs}>" end |
#list_item_type ⇒ String?
Returns the item type from a list derivation.
296 297 298 299 |
# File 'lib/wsdl/schema/node.rb', line 296 def list_item_type list = children.find { |c| c.kind == :list } list&.[]('itemType') end |
#max_occurs ⇒ String
Returns the maxOccurs value ('1', 'unbounded', etc.).
353 354 355 356 357 |
# File 'lib/wsdl/schema/node.rb', line 353 def max_occurs return @max_occurs if defined?(@max_occurs) @max_occurs = @xml_node['maxOccurs'] || '1' end |
#min_occurs ⇒ String
Returns the minOccurs value ('0', '1', etc.).
360 361 362 363 364 |
# File 'lib/wsdl/schema/node.rb', line 360 def min_occurs return @min_occurs if defined?(@min_occurs) @min_occurs = @xml_node['minOccurs'] || '1' end |
#multiple? ⇒ Boolean
Returns whether this element can appear multiple times.
369 370 371 |
# File 'lib/wsdl/schema/node.rb', line 369 def multiple? max_occurs == 'unbounded' || max_occurs.to_i > 1 end |
#name ⇒ String?
Returns the local name of this node.
94 95 96 97 98 |
# File 'lib/wsdl/schema/node.rb', line 94 def name return @name if defined?(@name) @name = @xml_node['name'] end |
#namespace ⇒ String?
Returns the target namespace URI.
150 151 152 |
# File 'lib/wsdl/schema/node.rb', line 150 def namespace @context[:target_namespace] end |
#namespace_constraint ⇒ String
Returns the namespace constraint for wildcards.
333 334 335 336 337 |
# File 'lib/wsdl/schema/node.rb', line 333 def namespace_constraint return @namespace_constraint if defined?(@namespace_constraint) @namespace_constraint = @xml_node['namespace'] || '##any' end |
#namespaces ⇒ Hash{String => String}
Returns XML namespace declarations in scope.
Computed lazily because many intermediate schema nodes (sequence, all, choice, etc.) never have their namespaces accessed externally. Reuses the cached schema-level namespaces when the node has no local namespace declarations, avoiding expensive Nokogiri traversal.
83 84 85 86 87 88 89 |
# File 'lib/wsdl/schema/node.rb', line 83 def namespaces @namespaces ||= if @context[:schema_namespaces] && @xml_node.namespace_definitions.empty? @context[:schema_namespaces] else @xml_node.namespaces.freeze end end |
#nillable? ⇒ Boolean
Returns whether this element allows nil values (xsi:nil="true").
143 144 145 146 147 |
# File 'lib/wsdl/schema/node.rb', line 143 def nillable? return @nillable if defined?(@nillable) @nillable = @xml_node['nillable'] == 'true' end |
#optional? ⇒ Boolean
Returns whether this element is optional.
376 377 378 |
# File 'lib/wsdl/schema/node.rb', line 376 def optional? min_occurs == '0' end |
#process_contents ⇒ String
Returns how wildcard content should be validated.
342 343 344 345 346 |
# File 'lib/wsdl/schema/node.rb', line 342 def process_contents return @process_contents if defined?(@process_contents) @process_contents = @xml_node['processContents'] || 'strict' end |
#ref ⇒ String?
Returns the qualified element/attribute reference.
108 109 110 111 112 |
# File 'lib/wsdl/schema/node.rb', line 108 def ref return @ref if defined?(@ref) @ref = @xml_node['ref'] end |
#release_dom_references! ⇒ void
This method returns an undefined value.
Releases all references to the Nokogiri DOM.
After Definition::Builder extracts data into the frozen Definition IR, the DOM nodes are no longer needed. Calling this method nils out the Nokogiri reference and any cached child/element/attribute arrays so the GC can reclaim the DOM while the Collection is still reachable.
Safe to call multiple times. Preserves #kind and any already-extracted
scalar attributes (+@name+, @type, etc.) since those are plain Ruby strings.
185 186 187 188 189 190 |
# File 'lib/wsdl/schema/node.rb', line 185 def release_dom_references! @xml_node = nil @children = nil @cached_elements = nil @cached_attributes = nil end |
#required? ⇒ Boolean
Returns whether this element is required.
383 384 385 |
# File 'lib/wsdl/schema/node.rb', line 383 def required? !optional? end |
#restriction_base ⇒ String?
Returns the base type from a restriction child.
288 289 290 291 |
# File 'lib/wsdl/schema/node.rb', line 288 def restriction_base restriction = children.find { |c| c.kind == :restriction } restriction&.base end |
#type ⇒ String?
Returns the qualified type reference.
101 102 103 104 105 |
# File 'lib/wsdl/schema/node.rb', line 101 def type return @type if defined?(@type) @type = @xml_node['type'] end |
#type_id ⇒ String?
Returns a unique identifier for this type.
Used to detect recursive type definitions during element building.
314 315 316 317 318 319 |
# File 'lib/wsdl/schema/node.rb', line 314 def type_id return nil unless %i[complexType element].include?(kind) return nil unless name "#{namespace}:#{name}" end |
#union_member_types ⇒ String?
Returns the member types from a union derivation.
304 305 306 307 |
# File 'lib/wsdl/schema/node.rb', line 304 def union_member_types union = children.find { |c| c.kind == :union } union&.[]('memberTypes') end |
#use ⇒ String
Returns the use constraint ('optional' or 'required').
122 123 124 125 126 |
# File 'lib/wsdl/schema/node.rb', line 122 def use return @use if defined?(@use) @use = @xml_node['use'] || 'optional' end |