Module: Ea::Transformers::QeaToXmi::Cardinality

Defined in:
lib/ea/transformers/qea_to_xmi/cardinality.rb

Overview

Pure-function cardinality / multiplicity parser for EA's free-text bound fields (t_attribute.upperbound, t_connector.sourcecard, etc.).

EA stores cardinality as opaque strings: 1, 0..1, 1..*, *, occasionally unbounded or *-1. The XMI wire form needs two separate child elements (<lowerValue value="N"/> and <upperValue value="M"/>) — never a range string. This module translates the EA form to a { lower:, upper: } pair.

Default-when-empty: EA's "no bound specified" maps to UML's unspecified multiplicity, which Sparx renders as <lowerValue value="0"/> and <upperValue value="-1"/>. Always emitting both is required for round-trip parity with real Sparx XMI (see TODO 26).

Constant Summary collapse

UNLIMITED_TOKENS =

Tokens EA uses for "unbounded". Matched case-insensitively.

%w[* *-1 unbounded].freeze
DEFAULT_LOWER =

UML defaults when EA carries no explicit bound.

"0"
DEFAULT_UPPER =
"-1"

Class Method Summary collapse

Class Method Details

.defaultsObject

---- Internal helpers ----



75
76
77
# File 'lib/ea/transformers/qea_to_xmi/cardinality.rb', line 75

def defaults
  { lower: DEFAULT_LOWER, upper: DEFAULT_UPPER }
end

.normalize_bound(token) ⇒ Object

A single bound token (one side of .. or a bare scalar). Empty / * → UML unlimited (-1).



86
87
88
89
90
91
92
# File 'lib/ea/transformers/qea_to_xmi/cardinality.rb', line 86

def normalize_bound(token)
  return "-1" if token.nil?
  return "-1" if token.strip.empty?

  stripped = token.strip
  UNLIMITED_TOKENS.include?(stripped.downcase) ? "-1" : stripped
end

.normalize_lower(raw) ⇒ String

Normalise a lower-bound token: empty/nil → "0" (UML default).

Parameters:

  • raw (String, Integer, nil)

Returns:

  • (String)


66
67
68
69
70
71
# File 'lib/ea/transformers/qea_to_xmi/cardinality.rb', line 66

def normalize_lower(raw)
  return DEFAULT_LOWER if raw.nil?

  stripped = raw.to_s.strip
  stripped.empty? ? DEFAULT_LOWER : stripped
end

.normalize_upper(raw) ⇒ String

Normalise an upper-bound token: * / unbounded-1 (UML LiteralUnlimitedNatural wire form).

Parameters:

  • raw (String, Integer, nil)

Returns:

  • (String)


54
55
56
57
58
59
60
61
# File 'lib/ea/transformers/qea_to_xmi/cardinality.rb', line 54

def normalize_upper(raw)
  return DEFAULT_UPPER if raw.nil?

  stripped = raw.to_s.strip
  return DEFAULT_UPPER if stripped.empty?

  UNLIMITED_TOKENS.include?(stripped.downcase) ? "-1" : stripped
end

.parse(raw) ⇒ Hash{Symbol=>String}

Returns { lower:, upper: } always populated; never nil. Empty/nil input returns the UML default.

Parameters:

  • raw (String, nil)

    e.g. "1..", "0..1", "1", "", nil

Returns:

  • (Hash{Symbol=>String})

    { lower:, upper: } always populated; never nil. Empty/nil input returns the UML default.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/ea/transformers/qea_to_xmi/cardinality.rb', line 34

def parse(raw)
  return defaults if raw.nil? || raw.to_s.strip.empty?

  stripped = raw.to_s.strip
  return parse_range(stripped) if stripped.include?("..")

  # Bare unlimited token (e.g. "*") means "many" — lower bound
  # is unspecified, which UML renders as 0..-1. Returning
  # `{ lower: "-1", upper: "-1" }` here would be invalid:
  # LiteralInteger cannot hold -1.
  return defaults if UNLIMITED_TOKENS.include?(stripped.downcase)

  single = normalize_bound(stripped)
  { lower: single, upper: single }
end

.parse_range(stripped) ⇒ Object



79
80
81
82
# File 'lib/ea/transformers/qea_to_xmi/cardinality.rb', line 79

def parse_range(stripped)
  lower, upper = stripped.split("..", 2)
  { lower: normalize_bound(lower), upper: normalize_bound(upper) }
end