Class: WSDL::Schema::Node

Inherits:
Object
  • Object
show all
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.

Examples:

Basic usage

node = Schema::Node.new(nokogiri_node, collection, context)
node.kind        # => :complexType
node.name        # => "UserType"
node.elements    # => [Node, Node, ...]

Pattern matching (Ruby 3.0+)

case node
in { kind: :element, name: }
  puts "Element: #{name}"
in { kind: :complexType }
  puts "Complex type with #{node.elements.count} elements"
end

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

Common Attributes collapse

Children & Traversal collapse

Type Resolution collapse

xs:any Wildcard Support collapse

Cardinality collapse

Pattern Matching collapse

Instance Method Summary collapse

Constructor Details

#initialize(xml_node, collection, context = {}) ⇒ Node

Creates a new Node from an XSD element.

Parameters:

  • xml_node (Nokogiri::XML::Node)

    the XSD element node

  • collection (Collection)

    the schema collection for resolving refs

  • context (Hash) (defaults to: {})

    schema context

Options Hash (context):

  • :target_namespace (String)

    the target namespace URI

  • :element_form_default (String)

    'qualified' or 'unqualified'



58
59
60
61
62
63
64
65
66
# File 'lib/wsdl/schema/node.rb', line 58

def initialize(xml_node, collection, context = {})
  @xml_node = xml_node
  @collection = collection
  @context = context

  @kind = xml_node.name.to_sym
  @attributes_hash = extract_attributes(xml_node)
  @namespaces = xml_node.namespaces.freeze
end

Instance Attribute Details

#kindSymbol (readonly)

Returns the XSD element type (:element, :complexType, :sequence, etc.).

Returns:

  • (Symbol)

    the XSD element type (:element, :complexType, :sequence, etc.)



69
70
71
# File 'lib/wsdl/schema/node.rb', line 69

def kind
  @kind
end

#namespacesHash{String => String} (readonly)

Returns XML namespace declarations in scope.

Returns:

  • (Hash{String => String})

    XML namespace declarations in scope



75
76
77
# File 'lib/wsdl/schema/node.rb', line 75

def namespaces
  @namespaces
end

#xml_nodeNokogiri::XML::Node (readonly)

Returns the underlying XML node.

Returns:

  • (Nokogiri::XML::Node)

    the underlying XML node



72
73
74
# File 'lib/wsdl/schema/node.rb', line 72

def xml_node
  @xml_node
end

Instance Method Details

#[](attr_name) ⇒ String?

Accesses any attribute from the underlying node.

Parameters:

  • attr_name (String)

    the attribute name

Returns:

  • (String, nil)

    the attribute value



136
137
138
# File 'lib/wsdl/schema/node.rb', line 136

def [](attr_name)
  @attributes_hash[attr_name]
end

#any?Boolean

Returns true if this is an xs:any wildcard.

Returns:

  • (Boolean)

    true if this is an xs:any wildcard



258
259
260
# File 'lib/wsdl/schema/node.rb', line 258

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.

Parameters:

  • memo (Array<Node>) (defaults to: [])

    accumulator for recursive traversal

  • limits (Limits, nil) (defaults to: nil)

    resource limits for validation

Returns:

  • (Array<Node>)

    all attribute definitions found

Raises:



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/wsdl/schema/node.rb', line 203

def attributes(memo = [], limits: nil)
  return memo if ATTRIBUTE_TERMINATORS.include?(kind)

  return resolve_attribute_group_ref(memo, limits:) if kind == :attributeGroup && ref

  children.each do |child|
    if ATTRIBUTE_KINDS.include?(child.kind)
      memo << child
      validate_attribute_count!(memo.size, limits)
    else
      memo = child.attributes(memo, limits:)
    end
  end

  memo
end

#baseString?

Returns the base type for restrictions/extensions.

Returns:

  • (String, nil)

    the base type for restrictions/extensions



95
96
97
# File 'lib/wsdl/schema/node.rb', line 95

def base
  @attributes_hash['base']
end

#childrenArray<Node>

Returns the parsed child nodes.

Returns:

  • (Array<Node>)

    the child nodes



147
148
149
# File 'lib/wsdl/schema/node.rb', line 147

def children
  @children ||= @xml_node.element_children.map { |n| Node.new(n, @collection, @context) }
end

#deconstruct_keys(_keys) ⇒ Hash

Supports Ruby pattern matching.

Examples:

case node
in { kind: :element, name: "User" }
  # matches element named "User"
end

Parameters:

  • _keys (Array<Symbol>, nil)

    keys to extract (unused, returns all keys)

Returns:

  • (Hash)

    deconstructed key-value pairs



325
326
327
328
329
330
331
332
333
# File 'lib/wsdl/schema/node.rb', line 325

def deconstruct_keys(_keys)
  {
    kind: kind,
    name: name,
    type: type,
    ref: ref,
    namespace: namespace
  }
end

#defaultString?

Returns the default value.

Returns:

  • (String, nil)

    the default value



105
106
107
# File 'lib/wsdl/schema/node.rb', line 105

def default
  @attributes_hash['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.

Parameters:

  • memo (Array<Node>) (defaults to: [])

    accumulator for recursive traversal

  • limits (Limits, nil) (defaults to: nil)

    resource limits for validation

Returns:

  • (Array<Node>)

    all element definitions found

Raises:



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/wsdl/schema/node.rb', line 177

def elements(memo = [], limits: nil)
  return memo if ELEMENT_TERMINATORS.include?(kind)

  include_base_type_elements(memo, limits:) if kind == :extension

  children.each do |child|
    if ELEMENT_KINDS.include?(child.kind)
      memo << child
      validate_element_count!(memo.size, limits)
    else
      memo = child.elements(memo, limits:)
    end
  end

  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).

Returns:

  • (Boolean)

    true if the node has no meaningful content



157
158
159
160
161
162
163
164
165
# File 'lib/wsdl/schema/node.rb', line 157

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

#fixedString?

Returns the fixed value.

Returns:

  • (String, nil)

    the fixed value



110
111
112
# File 'lib/wsdl/schema/node.rb', line 110

def fixed
  @attributes_hash['fixed']
end

#formString

Returns the element form (qualified or unqualified).

Returns:

  • (String)

    'qualified' or 'unqualified'



127
128
129
130
# File 'lib/wsdl/schema/node.rb', line 127

def form
  @attributes_hash['form'] ||
    (@context[:element_form_default] == 'qualified' ? 'qualified' : 'unqualified')
end

#inline_typeNode?

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.

Returns:

  • (Node, nil)

    the inline type, or nil if none



230
231
232
# File 'lib/wsdl/schema/node.rb', line 230

def inline_type
  children.find { |c| INLINE_TYPE_KINDS.include?(c.kind) }
end

#inspectString

Returns a string representation for debugging.

Returns:

  • (String)

    formatted debug string



340
341
342
343
# File 'lib/wsdl/schema/node.rb', line 340

def inspect
  attrs = @attributes_hash.map { |k, v| "#{k}=#{v.inspect}" }.join(' ')
  "#<Schema::Node:#{kind} #{attrs}>"
end

#max_occursString

Returns the maxOccurs value ('1', 'unbounded', etc.).

Returns:

  • (String)

    the maxOccurs value ('1', 'unbounded', etc.)



281
282
283
# File 'lib/wsdl/schema/node.rb', line 281

def max_occurs
  @attributes_hash['maxOccurs'] || '1'
end

#min_occursString

Returns the minOccurs value ('0', '1', etc.).

Returns:

  • (String)

    the minOccurs value ('0', '1', etc.)



286
287
288
# File 'lib/wsdl/schema/node.rb', line 286

def min_occurs
  @attributes_hash['minOccurs'] || '1'
end

#multiple?Boolean

Returns whether this element can appear multiple times.

Returns:

  • (Boolean)

    true if maxOccurs is unbounded or > 1



293
294
295
# File 'lib/wsdl/schema/node.rb', line 293

def multiple?
  max_occurs == 'unbounded' || max_occurs.to_i > 1
end

#nameString?

Returns the local name of this node.

Returns:

  • (String, nil)

    the local name of this node



80
81
82
# File 'lib/wsdl/schema/node.rb', line 80

def name
  @attributes_hash['name']
end

#namespaceString?

Returns the target namespace URI.

Returns:

  • (String, nil)

    the target namespace URI



120
121
122
# File 'lib/wsdl/schema/node.rb', line 120

def namespace
  @context[:target_namespace]
end

#namespace_constraintString

Returns the namespace constraint for wildcards.

Returns:

  • (String)

    '##any', '##other', '##local', '##targetNamespace', or a URI



265
266
267
# File 'lib/wsdl/schema/node.rb', line 265

def namespace_constraint
  @attributes_hash['namespace'] || '##any'
end

#nillable?Boolean

Returns whether this element allows nil values (xsi:nil="true").

Returns:

  • (Boolean)

    whether this element allows nil values (xsi:nil="true")



115
116
117
# File 'lib/wsdl/schema/node.rb', line 115

def nillable?
  @attributes_hash['nillable'] == 'true'
end

#optional?Boolean

Returns whether this element is optional.

Returns:

  • (Boolean)

    true if minOccurs is 0



300
301
302
# File 'lib/wsdl/schema/node.rb', line 300

def optional?
  min_occurs == '0'
end

#process_contentsString

Returns how wildcard content should be validated.

Returns:

  • (String)

    'strict', 'lax', or 'skip'



272
273
274
# File 'lib/wsdl/schema/node.rb', line 272

def process_contents
  @attributes_hash['processContents'] || 'strict'
end

#refString?

Returns the qualified element/attribute reference.

Returns:

  • (String, nil)

    the qualified element/attribute reference



90
91
92
# File 'lib/wsdl/schema/node.rb', line 90

def ref
  @attributes_hash['ref']
end

#required?Boolean

Returns whether this element is required.

Returns:

  • (Boolean)

    true if minOccurs is not 0



307
308
309
# File 'lib/wsdl/schema/node.rb', line 307

def required?
  !optional?
end

#restriction_baseString?

Returns the base type from a restriction child.

Returns:

  • (String, nil)

    the base type name



237
238
239
240
# File 'lib/wsdl/schema/node.rb', line 237

def restriction_base
  restriction = children.find { |c| c.kind == :restriction }
  restriction&.base
end

#typeString?

Returns the qualified type reference.

Returns:

  • (String, nil)

    the qualified type reference



85
86
87
# File 'lib/wsdl/schema/node.rb', line 85

def type
  @attributes_hash['type']
end

#type_idString?

Returns a unique identifier for this type.

Used to detect recursive type definitions during element building.

Returns:

  • (String, nil)

    the type ID in "namespace:name" format



247
248
249
250
251
# File 'lib/wsdl/schema/node.rb', line 247

def type_id
  return nil unless i[complexType element].include?(kind)

  "#{namespace}:#{name}"
end

#useString

Returns the use constraint ('optional' or 'required').

Returns:

  • (String)

    the use constraint ('optional' or 'required')



100
101
102
# File 'lib/wsdl/schema/node.rb', line 100

def use
  @attributes_hash['use'] || 'optional'
end