Module: Lutaml::Model::Schema::RngCompiler::RngHelpers

Defined in:
lib/lutaml/model/schema/rng_compiler/rng_helpers.rb

Overview

RNG-specific helpers used by ElementVisitor, DefineClassifier, and ValueTypeResolver. Centralising these here avoids the three-way semantic drift the first refactor introduced (e.g. inconsistent pure_value_choice? implementations).

Constant Summary collapse

STRUCTURAL_CHILD_NAMES =
%i[
  element ref attribute choice group
  optional zeroOrMore oneOrMore
].freeze

Class Method Summary collapse

Class Method Details

.apply_param(facet, name, value) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/lutaml/model/schema/rng_compiler/rng_helpers.rb', line 124

def apply_param(facet, name, value)
  case name
  when "minInclusive" then facet.min_inclusive = numeric_or_string(value)
  when "maxInclusive" then facet.max_inclusive = numeric_or_string(value)
  when "minExclusive" then facet.min_exclusive = numeric_or_string(value)
  when "maxExclusive" then facet.max_exclusive = numeric_or_string(value)
  when "minLength"    then facet.min_length = value.to_i
  when "maxLength"    then facet.max_length = value.to_i
  when "length"       then facet.length = value.to_i
  when "pattern"      then facet.pattern = value
  end
end

.branching_structural?(node) ⇒ Boolean

True when node has branching structure that needs its own class. Pure value-choices and <ref>s to simple types are NOT branching — they're typed leaves.

Returns:

  • (Boolean)


52
53
54
55
56
57
58
59
60
61
# File 'lib/lutaml/model/schema/rng_compiler/rng_helpers.rb', line 52

def branching_structural?(node)
  return false unless node

  non_leaf = STRUCTURAL_CHILD_NAMES - %i[choice ref]
  return true if present_children?(node, non_leaf)

  return false unless node.respond_to?(:choice)

  Array(node.choice).any? { |c| !pure_value_choice?(c) }
end

.facet_from_data(data) ⇒ Object

Build a Definitions::Facet from an RNG 's children.



85
86
87
88
89
90
91
# File 'lib/lutaml/model/schema/rng_compiler/rng_helpers.rb', line 85

def facet_from_data(data)
  facet = Definitions::Facet.new
  Array(data.param).each do |param|
    apply_param(facet, param.name, param.value.to_s)
  end
  facet
end

.facet_from_values(values) ⇒ Object

Build a Definitions::Facet from an array of elements.



94
95
96
97
98
# File 'lib/lutaml/model/schema/rng_compiler/rng_helpers.rb', line 94

def facet_from_values(values)
  Definitions::Facet.new(
    enumerations: Array(values).map(&:value),
  )
end

.fragment_model?(klass) ⇒ Boolean

True iff klass is a fragment model (no rooted xml element).

Returns:

  • (Boolean)


23
24
25
# File 'lib/lutaml/model/schema/rng_compiler/rng_helpers.rb', line 23

def fragment_model?(klass)
  klass.is_a?(Definitions::Model) && klass.xml_root.kind == :fragment
end

.numeric_or_string(value) ⇒ Object



137
138
139
140
141
142
# File 'lib/lutaml/model/schema/rng_compiler/rng_helpers.rb', line 137

def numeric_or_string(value)
  return value.to_i if /\A-?\d+\z/.match?(value)
  return value.to_f if /\A-?\d+\.\d+\z/.match?(value)

  value
end

.parent_class_for(base_symbol) ⇒ Object

Maps an RNG/XSD base symbol to its Ruby parent class string, defaulting to the string type when unknown.



118
119
120
121
122
# File 'lib/lutaml/model/schema/rng_compiler/rng_helpers.rb', line 118

def parent_class_for(base_symbol)
  Lutaml::Model::Type::TYPE_CODES.fetch(
    base_symbol, Lutaml::Model::Type::TYPE_CODES[:string]
  ).to_s
end

.present_children?(node, attrs) ⇒ Boolean

True iff any of attrs is a populated array-child of node.

Returns:

  • (Boolean)


43
44
45
46
47
# File 'lib/lutaml/model/schema/rng_compiler/rng_helpers.rb', line 43

def present_children?(node, attrs)
  attrs.any? do |attr|
    node.respond_to?(attr) && Array(node.public_send(attr)).any?
  end
end

.pure_union_choice?(choice) ⇒ Boolean

True iff choice is a union: at least 2 alternatives and no other content.

Returns:

  • (Boolean)


77
78
79
80
81
82
# File 'lib/lutaml/model/schema/rng_compiler/rng_helpers.rb', line 77

def pure_union_choice?(choice)
  return false unless choice
  return false unless choice.respond_to?(:data) && Array(choice.data).size >= 2

  !present_children?(choice, %i[element ref value])
end

.pure_value_choice?(choice) ⇒ Boolean

True iff choice is a consisting purely of alternatives (no , , , or ). A alternative subsumes the literals, so text | "x" is a plain string, not an enum restricted to "x".

Returns:

  • (Boolean)


67
68
69
70
71
72
73
# File 'lib/lutaml/model/schema/rng_compiler/rng_helpers.rb', line 67

def pure_value_choice?(choice)
  return false unless choice
  return false unless choice.respond_to?(:value) && Array(choice.value).any?
  return false if choice.respond_to?(:text) && Utils.present?(choice.text)

  !present_children?(choice, %i[element ref data])
end

.simple_type?(klass) ⇒ Boolean

True if the compiled define renders as a value type (RestrictedType or UnionType) rather than its own Serializable class. Single source of truth — used by ElementVisitor and ValueTypeResolver.

Returns:

  • (Boolean)


17
18
19
20
# File 'lib/lutaml/model/schema/rng_compiler/rng_helpers.rb', line 17

def simple_type?(klass)
  klass.is_a?(Definitions::RestrictedType) ||
    klass.is_a?(Definitions::UnionType)
end

.single(collection) ⇒ Object

Returns the only element of a collection, or nil if the collection is empty or has more than one element. Treats nil as an empty collection.



30
31
32
33
# File 'lib/lutaml/model/schema/rng_compiler/rng_helpers.rb', line 30

def single(collection)
  arr = Array(collection)
  arr.size == 1 ? arr.first : nil
end

.structural_content?(node) ⇒ Boolean

An RNG node has structural content when any of its element / ref / attribute / choice / group / optional / repetition children are populated.

Returns:

  • (Boolean)


38
39
40
# File 'lib/lutaml/model/schema/rng_compiler/rng_helpers.rb', line 38

def structural_content?(node)
  node && present_children?(node, STRUCTURAL_CHILD_NAMES)
end

.type_symbol(class_name) ⇒ Object

Snake_case symbol used as the registry key for a generated simple-type/union-type/namespace class.



102
103
104
# File 'lib/lutaml/model/schema/rng_compiler/rng_helpers.rb', line 102

def type_symbol(class_name)
  Utils.snake_case(class_name).to_sym
end

.unique_class_name(classes, base_name) ⇒ Object

A class name derived from base_name that does not collide with an existing key in classes (appends 2, 3, ... on collision).



108
109
110
111
112
113
114
# File 'lib/lutaml/model/schema/rng_compiler/rng_helpers.rb', line 108

def unique_class_name(classes, base_name)
  return base_name unless classes.key?(base_name)

  counter = 2
  counter += 1 while classes.key?("#{base_name}#{counter}")
  "#{base_name}#{counter}"
end