ea 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/TODO.complete/01-t-xref-parsing.md +1 -1
- data/TODO.complete/02-per-label-geometry.md +1 -1
- data/TODO.complete/03-object-properties-constraints.md +1 -1
- data/TODO.complete/04-operation-params.md +1 -1
- data/TODO.complete/05-pdata-flag-matrix.md +1 -1
- data/TODO.complete/06-emf-image-rendering.md +1 -1
- data/TODO.complete/07-auxiliary-tables.md +1 -1
- data/TODO.complete/10-mdg-stereotype-registry.md +1 -1
- data/TODO.complete/11-gml-xsd-generation.md +1 -1
- data/TODO.complete/13-shapescript-interpreter.md +12 -23
- data/TODO.complete/20-cli-diff.md +1 -1
- data/TODO.complete/21-cli-render.md +1 -1
- data/TODO.complete/22-cli-export.md +1 -1
- data/TODO.complete/23-cli-mdg.md +1 -1
- data/TODO.complete/30-headerlines-ocp-refactor.md +1 -1
- data/TODO.complete/32-spec-coverage.md +1 -1
- data/TODO.complete/33-blocked-parity-gaps.md +1 -1
- data/TODO.complete/36-stereotype-icon-wiring.md +1 -1
- data/TODO.complete/37-cli-lint.md +1 -1
- data/TODO.complete/38-cli-query.md +1 -1
- data/TODO.complete/39-cli-info.md +1 -1
- data/TODO.complete/40-ocl-evaluator.md +1 -1
- data/TODO.complete/41-round-trip-spec.md +1 -1
- data/TODO.complete/42-html-diff-report.md +1 -1
- data/TODO.complete/43-json-schema-export.md +1 -1
- data/TODO.complete/44-remaining-qea-tables.md +1 -1
- data/TODO.complete/45-shapescript-extensions.md +1 -1
- data/TODO.complete/46-xsd-consolidation.md +1 -1
- data/TODO.complete/47-headerlines-legacy-removal.md +1 -1
- data/TODO.complete/51-remove-respond-to.md +42 -15
- data/TODO.complete/52-xsd-decouple-fixtures.md +1 -1
- data/TODO.complete/54-wire-stereotype-icon-renderer.md +1 -1
- data/TODO.complete/55-wire-shapescript.md +1 -1
- data/TODO.complete/56-wire-emf-renderer.md +1 -1
- data/TODO.complete/57-wire-ocl-evaluator.md +1 -1
- data/TODO.complete/58-diff-modifications.md +1 -1
- data/TODO.complete/59-delete-toy-xmi-export.md +1 -1
- data/TODO.complete/60-curate-json-export.md +1 -1
- data/TODO.complete/61-plantuml-relationships.md +1 -1
- data/TODO.complete/62-changelog.md +1 -1
- data/TODO.complete/63-dependabot-fix.md +1 -1
- data/TODO.complete/64-qeatoxmi-connectors.md +15 -0
- data/TODO.complete/65-qeatoxmi-diagrams.md +15 -0
- data/TODO.complete/66-qeatoxmi-style-tags-docs.md +35 -0
- data/TODO.complete/67-ocl-collection-ops.md +20 -0
- data/TODO.complete/68-ocl-comparison-ops.md +24 -0
- data/TODO.complete/69-spec-new-models.md +20 -0
- data/TODO.complete/70-specs-for-all-models.md +21 -0
- data/TODO.complete/71-autoload-audit.md +26 -0
- data/TODO.complete/72-extension-serializer-ocp-naming.md +26 -0
- data/TODO.complete/73-ocl-validator-latent-bugs.md +50 -0
- data/TODO.complete/74-visibility-symbol-dry.md +39 -0
- data/TODO.complete/75-database-table-collection-registry.md +32 -0
- data/TODO.complete/76-stale-todo-cleanup.md +52 -0
- data/TODO.complete/77-eliminate-banned-patterns-from-specs.md +55 -0
- data/TODO.complete/78-mdg-stereotype-emission.md +66 -0
- data/TODO.complete/79-equivalence-fixes.md +57 -0
- data/TODO.complete/80-performance-extension-serializer-indexes.md +43 -0
- data/TODO.complete/81-dry-scope-guid-extraction.md +33 -0
- data/TODO.complete/82-public-send-audit-conclusion.md +25 -0
- data/TODO.complete/83-dry-shared-xml-escape-guid-format.md +52 -0
- data/TODO.complete/84-ocp-markers-shape-registry.md +54 -0
- data/TODO.complete/85-frozen-literal-rubocop-normalize-type-spec.md +38 -0
- data/TODO.complete/86-frozen-sweep-lint-query-specs.md +55 -0
- data/TODO.complete/STATUS.md +26 -5
- data/lib/ea/cli/app.rb +1 -0
- data/lib/ea/cli/command/convert.rb +1 -1
- data/lib/ea/cli/command/export.rb +10 -1
- data/lib/ea/cli/command/mdg.rb +1 -10
- data/lib/ea/cli/command/render.rb +2 -1
- data/lib/ea/diff/html_reporter.rb +1 -5
- data/lib/ea/export/json/generator.rb +5 -6
- data/lib/ea/export/plantuml/generator.rb +5 -3
- data/lib/ea/guid_format.rb +91 -0
- data/lib/ea/mdg/document.rb +2 -1
- data/lib/ea/mdg/loader.rb +2 -1
- data/lib/ea/mdg/registry.rb +57 -2
- data/lib/ea/mdg/xml/applies_to.rb +1 -1
- data/lib/ea/mdg/xml/apply.rb +1 -1
- data/lib/ea/mdg/xml/content.rb +1 -1
- data/lib/ea/mdg/xml/documentation.rb +1 -1
- data/lib/ea/mdg/xml/stereotype.rb +1 -1
- data/lib/ea/mdg/xml/stereotypes.rb +1 -1
- data/lib/ea/mdg/xml/tag.rb +1 -1
- data/lib/ea/mdg/xml/tagged_values.rb +1 -1
- data/lib/ea/mdg/xml/technology.rb +1 -1
- data/lib/ea/mdg/xml/uml_profile.rb +1 -1
- data/lib/ea/mdg/xml/uml_profiles.rb +1 -1
- data/lib/ea/mdg/xml.rb +2 -1
- data/lib/ea/mdg.rb +2 -1
- data/lib/ea/model/ghost_label.rb +2 -1
- data/lib/ea/ocl/evaluator.rb +39 -0
- data/lib/ea/ocl/nodes.rb +9 -0
- data/lib/ea/ocl/parser.rb +60 -6
- data/lib/ea/qea/database.rb +57 -2
- data/lib/ea/qea/factory/reference_resolver.rb +1 -1
- data/lib/ea/qea/models/ea_attribute.rb +1 -17
- data/lib/ea/qea/models/ea_connector.rb +3 -2
- data/lib/ea/qea/models/ea_object.rb +14 -9
- data/lib/ea/qea/models/ea_operation.rb +1 -17
- data/lib/ea/qea/models/ea_xref.rb +3 -0
- data/lib/ea/qea/models/scope_predicate.rb +29 -0
- data/lib/ea/qea/models.rb +1 -0
- data/lib/ea/qea/repositories/base_repository.rb +4 -1
- data/lib/ea/qea/validation/base_validator.rb +3 -48
- data/lib/ea/qea/validation/database/ocl_constraint_validator.rb +23 -13
- data/lib/ea/shapescript/renderer.rb +1 -3
- data/lib/ea/sources/qea/diagram_builder.rb +1 -0
- data/lib/ea/sources/qea/id_normalizer.rb +2 -2
- data/lib/ea/svg/ea_emitter/background.rb +2 -1
- data/lib/ea/svg/ea_emitter/element/attribute_line_builder.rb +1 -6
- data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +2 -7
- data/lib/ea/svg/ea_emitter/elements.rb +1 -3
- data/lib/ea/svg/ea_emitter/ghost_labels.rb +2 -1
- data/lib/ea/svg/ea_emitter/label/end_label.rb +1 -6
- data/lib/ea/svg/ea_emitter/marker/package_import.rb +1 -1
- data/lib/ea/svg/ea_emitter/markers.rb +52 -23
- data/lib/ea/svg/ea_emitter/text_renderer.rb +1 -4
- data/lib/ea/svg/ea_emitter/visibility_symbol.rb +37 -0
- data/lib/ea/svg/ea_emitter.rb +1 -0
- data/lib/ea/svg/parity/source.rb +0 -2
- data/lib/ea/svg/parity/suite.rb +0 -1
- data/lib/ea/transformers/qea_to_xmi/cardinality.rb +40 -28
- data/lib/ea/transformers/qea_to_xmi/context.rb +35 -1
- data/lib/ea/transformers/qea_to_xmi/extension_serializer.rb +686 -0
- data/lib/ea/transformers/qea_to_xmi/guid_format.rb +5 -48
- data/lib/ea/transformers/qea_to_xmi/id_allocator.rb +55 -54
- data/lib/ea/transformers/qea_to_xmi/primitive_types.rb +105 -0
- data/lib/ea/transformers/qea_to_xmi/profile_serializer.rb +163 -0
- data/lib/ea/transformers/qea_to_xmi/run_state.rb +7 -6
- data/lib/ea/transformers/qea_to_xmi/transformer.rb +423 -76
- data/lib/ea/transformers/qea_to_xmi/visibility.rb +30 -19
- data/lib/ea/transformers/qea_to_xmi.rb +5 -2
- data/lib/ea/transformers.rb +8 -2
- data/lib/ea/validation.rb +1 -1
- data/lib/ea/version.rb +1 -1
- data/lib/ea/xmi/liquid_drops/attribute_drop.rb +12 -4
- data/lib/ea/xmi/liquid_drops/klass_drop.rb +1 -2
- data/lib/ea/xmi/lookup_service.rb +4 -0
- data/lib/ea/xmi/parser.rb +30 -1
- data/lib/ea/xml_escape.rb +21 -0
- data/lib/ea.rb +2 -0
- metadata +34 -11
- data/lutaml-uml-0.5.1.gem +0 -0
data/lib/ea/ocl/parser.rb
CHANGED
|
@@ -33,7 +33,7 @@ module Ea
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
# Parse one expression. Returns a single AST node.
|
|
36
|
-
def parse_expression(source)
|
|
36
|
+
def parse_expression(source) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
37
37
|
s = source.strip
|
|
38
38
|
|
|
39
39
|
# Strip outer parens
|
|
@@ -45,7 +45,14 @@ module Ea
|
|
|
45
45
|
operand: parse_expression(s[4..]))
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
# 'and' / 'or'
|
|
48
|
+
# 'and' / 'or' / comparison ops — comparison has higher
|
|
49
|
+
# precedence than logical, so check it first.
|
|
50
|
+
if (split = split_on_comparison_op(s))
|
|
51
|
+
op, left, right = split
|
|
52
|
+
return Nodes::Comparison.new(op: op,
|
|
53
|
+
left: parse_expression(left),
|
|
54
|
+
right: parse_expression(right))
|
|
55
|
+
end
|
|
49
56
|
if (split = split_on_logical_op(s))
|
|
50
57
|
op, left, right = split
|
|
51
58
|
return Nodes::BinaryOp.new(op: op,
|
|
@@ -58,6 +65,16 @@ module Ea
|
|
|
58
65
|
return collection_node(m[2].to_sym, m[1], m[3], m[4])
|
|
59
66
|
end
|
|
60
67
|
|
|
68
|
+
# Collection ->size() — integer cardinality
|
|
69
|
+
if (m = s.match(/^(.+?)->size\(\)\z/))
|
|
70
|
+
return Nodes::CollectionSize.new(collection: parse_expression(m[1]))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Collection ->isEmpty() — boolean
|
|
74
|
+
if (m = s.match(/^(.+?)->isEmpty\(\)\z/))
|
|
75
|
+
return Nodes::CollectionIsEmpty.new(collection: parse_expression(m[1]))
|
|
76
|
+
end
|
|
77
|
+
|
|
61
78
|
# String.matches('regex')
|
|
62
79
|
if (m = s.match(/^(.+?)\.matches\(\s*['"](.+?)['"]\s*\)\z/))
|
|
63
80
|
return Nodes::StringMatches.new(string: parse_expression(m[1]),
|
|
@@ -99,7 +116,6 @@ module Ea
|
|
|
99
116
|
# Find the topmost ` and ` or ` or ` outside of parens.
|
|
100
117
|
# Returns [op, left_str, right_str] or nil.
|
|
101
118
|
def split_on_logical_op(source)
|
|
102
|
-
depth = 0
|
|
103
119
|
# Look for ' or ' first (lower precedence than 'and' in OCL?)
|
|
104
120
|
# Actually OCL: 'or' < 'xor' < 'and' < 'implies'. We pick the
|
|
105
121
|
# rightmost top-level operator (left-associative).
|
|
@@ -113,6 +129,45 @@ module Ea
|
|
|
113
129
|
nil
|
|
114
130
|
end
|
|
115
131
|
|
|
132
|
+
# Split on top-level comparison operator (>, <, >=, <=, =, ==, !=).
|
|
133
|
+
# Comparison has higher precedence than and/or, so we split here
|
|
134
|
+
# BEFORE calling split_on_logical_op.
|
|
135
|
+
def split_on_comparison_op(source)
|
|
136
|
+
%w[>= <= == != = > <].each do |op|
|
|
137
|
+
# Need to be careful with `>=` vs `>`: scan in order so
|
|
138
|
+
# multi-char operators are detected first.
|
|
139
|
+
idx = find_top_level_op(source, op)
|
|
140
|
+
next unless idx
|
|
141
|
+
|
|
142
|
+
return [op, source[0...idx], source[(idx + op.length)..]]
|
|
143
|
+
end
|
|
144
|
+
nil
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def find_top_level_op(source, op) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
148
|
+
depth = 0
|
|
149
|
+
i = 0
|
|
150
|
+
while i < source.length
|
|
151
|
+
c = source[i]
|
|
152
|
+
depth += 1 if c == "("
|
|
153
|
+
depth -= 1 if c == ")"
|
|
154
|
+
if depth.zero? && source[i, op.length] == op
|
|
155
|
+
# Each `next` clause below MUST fall through to `i += 1`
|
|
156
|
+
# at the loop's tail — using `next` here would skip the
|
|
157
|
+
# increment and infinite-loop on the same matching char.
|
|
158
|
+
should_skip =
|
|
159
|
+
(op == ">" && source[i + 1, 1] == "=") || # >= matches as >
|
|
160
|
+
(op == "<" && source[i + 1, 1] == "=") || # <= matches as <
|
|
161
|
+
(op == "=" && source[i + 1, 1] == "=") || # == matches as =
|
|
162
|
+
(op == ">" && source[i - 1, 1] == "-") || # -> arrow
|
|
163
|
+
(op == "<" && source[i - 1, 1] == "-") # <- arrow
|
|
164
|
+
return i unless should_skip
|
|
165
|
+
end
|
|
166
|
+
i += 1
|
|
167
|
+
end
|
|
168
|
+
nil
|
|
169
|
+
end
|
|
170
|
+
|
|
116
171
|
# Find substring at depth 0 (not inside parens).
|
|
117
172
|
def find_top_level(source, substring)
|
|
118
173
|
depth = 0
|
|
@@ -121,9 +176,8 @@ module Ea
|
|
|
121
176
|
c = source[i]
|
|
122
177
|
depth += 1 if c == "("
|
|
123
178
|
depth -= 1 if c == ")"
|
|
124
|
-
if depth.zero? && source[i, substring.length] == substring
|
|
125
|
-
|
|
126
|
-
end
|
|
179
|
+
return i if depth.zero? && source[i, substring.length] == substring
|
|
180
|
+
|
|
127
181
|
i += 1
|
|
128
182
|
end
|
|
129
183
|
nil
|
data/lib/ea/qea/database.rb
CHANGED
|
@@ -65,6 +65,51 @@ module Ea
|
|
|
65
65
|
documents scripts
|
|
66
66
|
].freeze
|
|
67
67
|
|
|
68
|
+
# Maps EA SQL table names to collection accessor symbols.
|
|
69
|
+
# Centralised here (MECE) so validators and services don't
|
|
70
|
+
# each maintain their own case/when dispatch. Adding a new
|
|
71
|
+
# table = adding one hash entry.
|
|
72
|
+
TABLE_TO_COLLECTION = {
|
|
73
|
+
"t_package" => :packages,
|
|
74
|
+
"t_object" => :objects,
|
|
75
|
+
"t_attribute" => :attributes,
|
|
76
|
+
"t_operation" => :operations,
|
|
77
|
+
"t_operationparams" => :operation_params,
|
|
78
|
+
"t_connector" => :connectors,
|
|
79
|
+
"t_diagram" => :diagrams,
|
|
80
|
+
"t_diagramobjects" => :diagram_objects,
|
|
81
|
+
"t_diagramlinks" => :diagram_links,
|
|
82
|
+
"t_objectconstraint" => :object_constraints,
|
|
83
|
+
"t_objectproperties" => :object_properties,
|
|
84
|
+
"t_taggedvalue" => :tagged_values,
|
|
85
|
+
"t_attributetag" => :attribute_tags,
|
|
86
|
+
"t_xref" => :xrefs,
|
|
87
|
+
"t_stereotypes" => :stereotypes,
|
|
88
|
+
"t_datatypes" => :datatypes,
|
|
89
|
+
"t_constrainttypes" => :constraint_types,
|
|
90
|
+
"t_connectortypes" => :connector_types,
|
|
91
|
+
"t_diagramtypes" => :diagram_types,
|
|
92
|
+
"t_objecttypes" => :object_types,
|
|
93
|
+
"t_statustypes" => :status_types,
|
|
94
|
+
"t_complexitytypes" => :complexity_types,
|
|
95
|
+
"t_documents" => :documents,
|
|
96
|
+
"t_scripts" => :scripts,
|
|
97
|
+
}.freeze
|
|
98
|
+
|
|
99
|
+
# @param table_name [String] EA SQL table name (e.g., "t_object")
|
|
100
|
+
# @return [Array] the collection for that table, or [] if unknown
|
|
101
|
+
def collection_for_table(table_name)
|
|
102
|
+
name = TABLE_TO_COLLECTION[table_name]
|
|
103
|
+
return [] unless name
|
|
104
|
+
|
|
105
|
+
records = @collections[name]
|
|
106
|
+
return [] unless records
|
|
107
|
+
|
|
108
|
+
# t_object is wrapped in ObjectRepository — call .all to
|
|
109
|
+
# get the plain Array that validators expect.
|
|
110
|
+
records.is_a?(Repositories::ObjectRepository) ? records.all : records
|
|
111
|
+
end
|
|
112
|
+
|
|
68
113
|
COLLECTION_ACCESSORS.each do |name|
|
|
69
114
|
define_method(name) do
|
|
70
115
|
@collections[name] || []
|
|
@@ -167,9 +212,19 @@ module Ea
|
|
|
167
212
|
@diagram_links_by_id[id] || []
|
|
168
213
|
end
|
|
169
214
|
|
|
170
|
-
# Find an object by ID
|
|
215
|
+
# Find an object by ID. Uses the repository's primary-key index
|
|
216
|
+
# rather than a scan: the parentid walk in QeaToXmi's
|
|
217
|
+
# `cyclic_ancestry?` calls this once per ancestor hop per object.
|
|
218
|
+
#
|
|
219
|
+
# Hash lookup matches on eql?, so it would miss a Float id that a
|
|
220
|
+
# scan's == would have caught. No caller here can do that:
|
|
221
|
+
# t_object.Object_ID is declared Type::Integer and the model
|
|
222
|
+
# coerces on construction, and every caller passes either such an
|
|
223
|
+
# attribute or an explicit to_i. Deliberately left unguarded — a
|
|
224
|
+
# scan fallback on miss would put every absent-parent lookup in
|
|
225
|
+
# the ancestry walk back on a linear scan.
|
|
171
226
|
def find_object(id)
|
|
172
|
-
objects.
|
|
227
|
+
objects.find(id)
|
|
173
228
|
end
|
|
174
229
|
|
|
175
230
|
# Find object by ea_guid
|
|
@@ -81,23 +81,7 @@ module Ea
|
|
|
81
81
|
const == 1
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
# @return [Boolean]
|
|
86
|
-
def public?
|
|
87
|
-
scope&.downcase == "public"
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# Check if attribute is private
|
|
91
|
-
# @return [Boolean]
|
|
92
|
-
def private?
|
|
93
|
-
scope&.downcase == "private"
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Check if attribute is protected
|
|
97
|
-
# @return [Boolean]
|
|
98
|
-
def protected?
|
|
99
|
-
scope&.downcase == "protected"
|
|
100
|
-
end
|
|
84
|
+
include ScopePredicate
|
|
101
85
|
|
|
102
86
|
# @return [Integer] pos for attribute ordering within parent
|
|
103
87
|
def sort_position
|
|
@@ -119,10 +119,11 @@ module Ea
|
|
|
119
119
|
connector_type == "Aggregation"
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
-
# Check if connector is a realization
|
|
122
|
+
# Check if connector is a realization. EA writes both the US
|
|
123
|
+
# and UK spellings depending on version/edition.
|
|
123
124
|
# @return [Boolean]
|
|
124
125
|
def realization?
|
|
125
|
-
connector_type
|
|
126
|
+
%w[Realization Realisation].include?(connector_type)
|
|
126
127
|
end
|
|
127
128
|
|
|
128
129
|
# Check if source is aggregate
|
|
@@ -194,17 +194,22 @@ module Ea
|
|
|
194
194
|
# Note — diagram note. Same situation as Text: rendering
|
|
195
195
|
# hint, not a model element. Dropped.
|
|
196
196
|
#
|
|
197
|
+
TRANSFORMER_TYPES = {
|
|
198
|
+
"Enumeration" => :enumeration,
|
|
199
|
+
"DataType" => :data_type,
|
|
200
|
+
"PrimitiveType" => :data_type,
|
|
201
|
+
"Class" => :class,
|
|
202
|
+
"Interface" => :class,
|
|
203
|
+
"Signal" => :signal,
|
|
204
|
+
"Object" => :instance,
|
|
205
|
+
"Association" => :association_element,
|
|
206
|
+
}.freeze
|
|
207
|
+
|
|
197
208
|
# @return [Symbol, nil] Registry key or nil if not a UML model element
|
|
198
209
|
def transformer_type
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
:data_type
|
|
203
|
-
elsif uml_class? || interface?
|
|
204
|
-
:class
|
|
205
|
-
elsif instance?
|
|
206
|
-
:instance
|
|
207
|
-
end
|
|
210
|
+
return :enumeration if stereotype_is?("enumeration")
|
|
211
|
+
|
|
212
|
+
TRANSFORMER_TYPES[object_type]
|
|
208
213
|
end
|
|
209
214
|
|
|
210
215
|
def stereotype_is?(expected)
|
|
@@ -106,23 +106,7 @@ module Ea
|
|
|
106
106
|
styleex&.include?("Reception=1")
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
# @return [Boolean]
|
|
111
|
-
def public?
|
|
112
|
-
scope&.downcase == "public"
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# Check if operation is private
|
|
116
|
-
# @return [Boolean]
|
|
117
|
-
def private?
|
|
118
|
-
scope&.downcase == "private"
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# Check if operation is protected
|
|
122
|
-
# @return [Boolean]
|
|
123
|
-
def protected?
|
|
124
|
-
scope&.downcase == "protected"
|
|
125
|
-
end
|
|
109
|
+
include ScopePredicate
|
|
126
110
|
|
|
127
111
|
# @return [Integer] pos for operation ordering within parent
|
|
128
112
|
def sort_position
|
|
@@ -10,6 +10,9 @@ module Ea
|
|
|
10
10
|
attribute :xref_id, Lutaml::Model::Type::String
|
|
11
11
|
attribute :name, Lutaml::Model::Type::String
|
|
12
12
|
attribute :xref_type, Lutaml::Model::Type::String
|
|
13
|
+
attribute :visibility, Lutaml::Model::Type::String
|
|
14
|
+
attribute :behavior, Lutaml::Model::Type::String
|
|
15
|
+
attribute :partition, Lutaml::Model::Type::String
|
|
13
16
|
attribute :client, Lutaml::Model::Type::String
|
|
14
17
|
attribute :supplier, Lutaml::Model::Type::String
|
|
15
18
|
attribute :description, Lutaml::Model::Type::String
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Qea
|
|
5
|
+
module Models
|
|
6
|
+
# Mixin providing `public?`, `private?`, `protected?` scope
|
|
7
|
+
# predicates for any model that has a `scope` attribute
|
|
8
|
+
# (t_attribute.scope, t_operation.scope, t_object.scope).
|
|
9
|
+
#
|
|
10
|
+
# EA stores scope as a case-insensitive string ("Public",
|
|
11
|
+
# "Private", "Protected"). This module centralises the
|
|
12
|
+
# comparison so both EaAttribute and EaOperation share one
|
|
13
|
+
# implementation.
|
|
14
|
+
module ScopePredicate
|
|
15
|
+
def public?
|
|
16
|
+
scope&.downcase == "public"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def private?
|
|
20
|
+
scope&.downcase == "private"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def protected?
|
|
24
|
+
scope&.downcase == "protected"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/ea/qea/models.rb
CHANGED
|
@@ -6,6 +6,7 @@ module Ea
|
|
|
6
6
|
module Qea
|
|
7
7
|
module Models
|
|
8
8
|
autoload :BaseModel, "ea/qea/models/base_model"
|
|
9
|
+
autoload :ScopePredicate, "ea/qea/models/scope_predicate"
|
|
9
10
|
autoload :EaAttribute, "ea/qea/models/ea_attribute"
|
|
10
11
|
autoload :EaAttributeTag, "ea/qea/models/ea_attribute_tag"
|
|
11
12
|
autoload :EaComplexityType, "ea/qea/models/ea_complexity_type"
|
|
@@ -215,9 +215,12 @@ module Ea
|
|
|
215
215
|
|
|
216
216
|
private
|
|
217
217
|
|
|
218
|
+
# First occurrence wins, matching what a scan would return —
|
|
219
|
+
# `find` and `find_by_key` must not disagree about which record
|
|
220
|
+
# answers for a duplicated key.
|
|
218
221
|
def build_pk_index
|
|
219
222
|
@pk_index = {}
|
|
220
|
-
@records.each { |r| @pk_index[r.primary_key]
|
|
223
|
+
@records.each { |r| @pk_index[r.primary_key] ||= r }
|
|
221
224
|
end
|
|
222
225
|
end
|
|
223
226
|
end
|
|
@@ -128,54 +128,9 @@ module Ea
|
|
|
128
128
|
# Maps table names to their corresponding collections in Database
|
|
129
129
|
#
|
|
130
130
|
# @param table [String] Table name (e.g., "t_package", "t_object")
|
|
131
|
-
# @return [Array
|
|
132
|
-
def get_collection_for_table(table)
|
|
133
|
-
|
|
134
|
-
when "t_package"
|
|
135
|
-
database.packages
|
|
136
|
-
when "t_object"
|
|
137
|
-
database.objects.all
|
|
138
|
-
when "t_attribute"
|
|
139
|
-
database.attributes
|
|
140
|
-
when "t_operation"
|
|
141
|
-
database.operations
|
|
142
|
-
when "t_operationparams"
|
|
143
|
-
database.operation_params
|
|
144
|
-
when "t_connector"
|
|
145
|
-
database.connectors
|
|
146
|
-
when "t_diagram"
|
|
147
|
-
database.diagrams
|
|
148
|
-
when "t_diagramobjects"
|
|
149
|
-
database.diagram_objects
|
|
150
|
-
when "t_diagramlinks"
|
|
151
|
-
database.diagram_links
|
|
152
|
-
when "t_objectconstraint"
|
|
153
|
-
database.object_constraints
|
|
154
|
-
when "t_objectproperties"
|
|
155
|
-
database.object_properties
|
|
156
|
-
when "t_taggedvalue"
|
|
157
|
-
database.tagged_values
|
|
158
|
-
when "t_attributetag"
|
|
159
|
-
database.attribute_tags
|
|
160
|
-
when "t_xref"
|
|
161
|
-
database.xrefs
|
|
162
|
-
when "t_stereotypes"
|
|
163
|
-
database.stereotypes
|
|
164
|
-
when "t_datatypes"
|
|
165
|
-
database.datatypes
|
|
166
|
-
when "t_constrainttypes"
|
|
167
|
-
database.constraint_types
|
|
168
|
-
when "t_connectortypes"
|
|
169
|
-
database.connector_types
|
|
170
|
-
when "t_diagramtypes"
|
|
171
|
-
database.diagram_types
|
|
172
|
-
when "t_objecttypes"
|
|
173
|
-
database.object_types
|
|
174
|
-
when "t_statustypes"
|
|
175
|
-
database.status_types
|
|
176
|
-
when "t_complexitytypes"
|
|
177
|
-
database.complexity_types
|
|
178
|
-
end
|
|
131
|
+
# @return [Array] Collection array or [] if table not mapped
|
|
132
|
+
def get_collection_for_table(table)
|
|
133
|
+
database.collection_for_table(table)
|
|
179
134
|
end
|
|
180
135
|
|
|
181
136
|
# Adds an error to the validation result
|
|
@@ -24,18 +24,30 @@ module Ea
|
|
|
24
24
|
return unless ast
|
|
25
25
|
|
|
26
26
|
if evaluate(ast, owner)
|
|
27
|
-
result.
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
result.add_info(
|
|
28
|
+
category: :ocl_constraint,
|
|
29
|
+
entity_type: :class,
|
|
30
|
+
entity_id: owner.ea_object_id.to_s,
|
|
31
|
+
entity_name: owner.name,
|
|
32
|
+
message: "OCL constraint '#{constraint.constraint_type}' passed",
|
|
33
|
+
)
|
|
30
34
|
else
|
|
31
|
-
result.
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
result.add_warning(
|
|
36
|
+
category: :ocl_constraint,
|
|
37
|
+
entity_type: :class,
|
|
38
|
+
entity_id: owner.ea_object_id.to_s,
|
|
39
|
+
entity_name: owner.name,
|
|
40
|
+
message: "OCL constraint '#{constraint.constraint_type}' failed: #{constraint.constraint}",
|
|
41
|
+
)
|
|
34
42
|
end
|
|
35
43
|
rescue Ea::Ocl::UnsupportedError => e
|
|
36
|
-
result.
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
result.add_info(
|
|
45
|
+
category: :ocl_constraint,
|
|
46
|
+
entity_type: :class,
|
|
47
|
+
entity_id: owner&.ea_object_id.to_s,
|
|
48
|
+
entity_name: owner&.name,
|
|
49
|
+
message: "Skipping OCL constraint (unsupported syntax): #{e.message}",
|
|
50
|
+
)
|
|
39
51
|
end
|
|
40
52
|
|
|
41
53
|
# Parse the OCL body. Returns nil for empty / non-invariant
|
|
@@ -51,13 +63,11 @@ module Ea
|
|
|
51
63
|
end
|
|
52
64
|
|
|
53
65
|
def owner_for(constraint)
|
|
54
|
-
object_id = constraint.
|
|
55
|
-
constraint.ea_object_id :
|
|
56
|
-
constraint.object_id
|
|
66
|
+
object_id = constraint.ea_object_id
|
|
57
67
|
return nil unless object_id
|
|
58
68
|
|
|
59
69
|
(database.collections[:objects] || [])
|
|
60
|
-
.find { |o| o.
|
|
70
|
+
.find { |o| o.ea_object_id == object_id }
|
|
61
71
|
end
|
|
62
72
|
end
|
|
63
73
|
end
|
|
@@ -17,7 +17,7 @@ module Ea
|
|
|
17
17
|
def from_guid(ea_guid)
|
|
18
18
|
return nil if ea_guid.nil? || ea_guid.empty?
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Ea::GuidFormat.strip_braces(ea_guid)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
# Convert a GUID to the EAID_ filename format used by EA's
|
|
@@ -26,7 +26,7 @@ module Ea
|
|
|
26
26
|
def to_eaid(ea_guid)
|
|
27
27
|
return nil if ea_guid.nil? || ea_guid.empty?
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
Ea::GuidFormat.ea_guid_to_xmi_id(ea_guid, prefix: "EAID")
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def synthetic(prefix, *parts)
|
|
@@ -55,12 +55,7 @@ module Ea
|
|
|
55
55
|
private
|
|
56
56
|
|
|
57
57
|
def visibility_marker
|
|
58
|
-
|
|
59
|
-
when "private" then "-"
|
|
60
|
-
when "protected" then "#"
|
|
61
|
-
when "package" then "~"
|
|
62
|
-
else "+"
|
|
63
|
-
end
|
|
58
|
+
VisibilitySymbol.for(property.visibility)
|
|
64
59
|
end
|
|
65
60
|
|
|
66
61
|
# Property name, prefixed with the declaring classifier's
|
|
@@ -55,12 +55,7 @@ module Ea
|
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def self.visibility_prefix(operation)
|
|
58
|
-
|
|
59
|
-
when "private" then "- "
|
|
60
|
-
when "protected" then "# "
|
|
61
|
-
when "package" then "~ "
|
|
62
|
-
else "+ "
|
|
63
|
-
end
|
|
58
|
+
VisibilitySymbol.for(operation.visibility, with_space: true)
|
|
64
59
|
end
|
|
65
60
|
private_class_method :visibility_prefix
|
|
66
61
|
|
|
@@ -71,7 +66,7 @@ module Ea
|
|
|
71
66
|
|
|
72
67
|
"#{text}: #{operation.return_type_name}"
|
|
73
68
|
end
|
|
74
|
-
|
|
69
|
+
public_class_method :operation_text
|
|
75
70
|
|
|
76
71
|
def self.namespace_double_colon(type_name)
|
|
77
72
|
return "" if type_name.nil? || type_name.empty?
|
|
@@ -185,9 +185,6 @@ module Ea
|
|
|
185
185
|
# alphabetical "+ Name" rows in the body compartment, but
|
|
186
186
|
# only when the diagram's t_diagram.ShowPackageContents flag
|
|
187
187
|
# is non-zero. Returns [] when the diagram suppresses
|
|
188
|
-
# package contents or the model element isn't a Package.
|
|
189
|
-
# Each entry is a Struct(name:, kind:) so the renderer can
|
|
190
|
-
# pick the correct per-type icon (Enumeration vs default).
|
|
191
188
|
def package_content_lines_for(model_element)
|
|
192
189
|
return [] unless model_element.is_a?(Ea::Model::Package)
|
|
193
190
|
return [] unless diagram.show_package_contents
|
|
@@ -199,6 +196,7 @@ module Ea
|
|
|
199
196
|
PackageContentRow.new(name: c.name.to_s, kind: row_icon_kind(c))
|
|
200
197
|
end
|
|
201
198
|
end
|
|
199
|
+
public :package_content_lines_for
|
|
202
200
|
|
|
203
201
|
def package_children(package)
|
|
204
202
|
return [] unless model_index
|
|
@@ -236,12 +236,7 @@ module Ea
|
|
|
236
236
|
end
|
|
237
237
|
|
|
238
238
|
def visibility_prefix(property)
|
|
239
|
-
|
|
240
|
-
when "private" then "-"
|
|
241
|
-
when "protected" then "#"
|
|
242
|
-
when "package" then "~"
|
|
243
|
-
else "+"
|
|
244
|
-
end
|
|
239
|
+
VisibilitySymbol.for(property.visibility)
|
|
245
240
|
end
|
|
246
241
|
|
|
247
242
|
def text_at(x, y, content)
|