ea 0.1.0 → 0.1.4
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/CLAUDE.md +125 -0
- data/Rakefile +12 -4
- data/TODO.next/00-publish-blocking-bugs.md +74 -0
- data/TODO.next/01-standalone-ea-gem-identity.md +76 -0
- data/TODO.next/02-optional-lutaml-uml-dependency.md +47 -0
- data/TODO.next/03-slim-lutaml-uml.md +79 -0
- data/TODO.next/04-loader-registry-for-uml-repository.md +49 -0
- data/TODO.next/05-extract-shared-transformer-methods.md +14 -0
- data/TODO.next/06-deduplicate-stereotype-loading.md +17 -0
- data/TODO.next/07-transformer-registry-in-factory.md +20 -0
- data/TODO.next/08-connector-type-registry.md +27 -0
- data/TODO.next/09-element-renderer-registry.md +29 -0
- data/TODO.next/10-connector-renderer-lsp.md +18 -0
- data/TODO.next/11-consolidate-style-knowledge.md +33 -0
- data/TODO.next/12-data-driven-from-db-row.md +24 -0
- data/TODO.next/13-extract-duplicated-methods.md +17 -0
- data/TODO.next/14-remove-dead-code.md +10 -0
- data/TODO.next/15-narrow-exception-handling.md +39 -0
- data/TODO.next/16-repository-indexes.md +28 -0
- data/TODO.next/17-fix-spec-quality-and-coverage.md +32 -0
- data/TODO.next/18-xmi-tool-specific-parser-architecture.md +172 -0
- data/TODO.next/19-fix-ea-gemspec-dependency-declarations.md +56 -0
- data/TODO.next/20-ci-requires-unreleased-lutaml-uml.md +63 -0
- data/TODO.next/21-qeatoxmi-via-xmi-gem.md +340 -0
- data/TODO.next/22-strip-respond-to-from-qeatoxmi-specs.md +32 -0
- data/TODO.next/23-cleanup-idallocator.md +41 -0
- data/TODO.next/24-tighten-parity-specs.md +42 -0
- data/TODO.next/25-sparx-eaid-format-for-synthesized-ids.md +62 -0
- data/TODO.next/26-fix-uppervalue-lowervalue-count-gap.md +51 -0
- data/TODO.next/27-extract-cardinality-module.md +68 -0
- data/TODO.next/28-extract-xml-sanitizer.md +51 -0
- data/TODO.next/29-ocp-registry-for-classifier-builders.md +58 -0
- data/TODO.next/30-struct-return-for-association-end.md +37 -0
- data/TODO.next/31-idallocator-specs.md +27 -0
- data/TODO.next/32-phase2-gap-sentinel-specs.md +53 -0
- data/TODO.next/33-normalize-lower-cleanup.md +30 -0
- data/TODO.next/34-document-member-end-order-rt-prefix.md +29 -0
- data/TODO.next/35-walk-runstate-for-instance-slots.md +76 -0
- data/TODO.next/36-wire-interface-realization.md +50 -0
- data/TODO.next/37-visibility-returns-real-booleans.md +36 -0
- data/config/diagram_styles.yml +200 -0
- data/config/model_transformations.yml +266 -0
- data/config/qea_schema.yml +1024 -0
- data/docs/ea_to_uml_type_mapping.md +89 -0
- data/docs/xmi_qea_conversion_capabilities.md +99 -0
- data/examples/lur/20251010_current_plateau_v5.1.lur +0 -0
- data/examples/lur/basic.lur +0 -0
- data/examples/lur/test-output.lur +0 -0
- data/examples/lur/test.lur +0 -0
- data/examples/lur_basic_usage.rb +221 -0
- data/examples/lur_cli_workflow.rb +263 -0
- data/examples/lur_statistics.rb +326 -0
- data/examples/qea/20251010_current_plateau_v5.1.qea +0 -0
- data/examples/qea/ArcGISWorkspace_template.qea +0 -0
- data/examples/qea/README_qea_parser.adoc +230 -0
- data/examples/qea/UmlModel_template.qea +0 -0
- data/examples/qea/basic.qea +0 -0
- data/examples/qea/simple.qea +0 -0
- data/examples/qea/simple_example.qea +0 -0
- data/examples/qea/test.qea +0 -0
- data/examples/qea_standalone_query.rb +73 -0
- data/examples/qea_to_repository.rb +51 -0
- data/examples/smoke_test_real_qea.rb +81 -0
- data/exe/ea +7 -0
- data/lib/ea/cli/app.rb +72 -0
- data/lib/ea/cli/command/base.rb +80 -0
- data/lib/ea/cli/command/convert.rb +62 -0
- data/lib/ea/cli/command/diagrams.rb +81 -0
- data/lib/ea/cli/command/list.rb +61 -0
- data/lib/ea/cli/command/parse.rb +29 -0
- data/lib/ea/cli/command/stats.rb +20 -0
- data/lib/ea/cli/command/validate.rb +41 -0
- data/lib/ea/cli/command.rb +15 -0
- data/lib/ea/cli/error.rb +34 -0
- data/lib/ea/cli/output/formatter.rb +34 -0
- data/lib/ea/cli/output/json_formatter.rb +20 -0
- data/lib/ea/cli/output/table_formatter.rb +42 -0
- data/lib/ea/cli/output/yaml_formatter.rb +20 -0
- data/lib/ea/cli/output.rb +56 -0
- data/lib/ea/cli.rb +17 -0
- data/lib/ea/diagram/configuration.rb +379 -0
- data/lib/ea/diagram/element_renderers/base_renderer.rb +77 -0
- data/lib/ea/diagram/element_renderers/class_renderer.rb +323 -0
- data/lib/ea/diagram/element_renderers/connector_renderer.rb +41 -0
- data/lib/ea/diagram/element_renderers/package_renderer.rb +61 -0
- data/lib/ea/diagram/element_renderers.rb +43 -0
- data/lib/ea/diagram/extractor.rb +560 -0
- data/lib/ea/diagram/layout_engine.rb +170 -0
- data/lib/ea/diagram/path_builder.rb +202 -0
- data/lib/ea/diagram/style_parser.rb +42 -0
- data/lib/ea/diagram/style_resolver.rb +276 -0
- data/lib/ea/diagram/svg_renderer.rb +274 -0
- data/lib/ea/diagram/util.rb +73 -0
- data/lib/ea/diagram.rb +47 -0
- data/lib/ea/qea/benchmark.rb +210 -0
- data/lib/ea/qea/database.rb +308 -0
- data/lib/ea/qea/factory/association_builder.rb +203 -0
- data/lib/ea/qea/factory/association_transformer.rb +91 -0
- data/lib/ea/qea/factory/attribute_tag_transformer.rb +57 -0
- data/lib/ea/qea/factory/attribute_transformer.rb +93 -0
- data/lib/ea/qea/factory/base_transformer.rb +177 -0
- data/lib/ea/qea/factory/class_transformer.rb +116 -0
- data/lib/ea/qea/factory/constraint_transformer.rb +75 -0
- data/lib/ea/qea/factory/data_type_transformer.rb +77 -0
- data/lib/ea/qea/factory/diagram_transformer.rb +157 -0
- data/lib/ea/qea/factory/document_builder.rb +283 -0
- data/lib/ea/qea/factory/ea_to_uml_factory.rb +229 -0
- data/lib/ea/qea/factory/enum_transformer.rb +74 -0
- data/lib/ea/qea/factory/generalization_builder.rb +227 -0
- data/lib/ea/qea/factory/generalization_transformer.rb +98 -0
- data/lib/ea/qea/factory/instance_transformer.rb +68 -0
- data/lib/ea/qea/factory/object_property_transformer.rb +58 -0
- data/lib/ea/qea/factory/operation_transformer.rb +66 -0
- data/lib/ea/qea/factory/package_transformer.rb +145 -0
- data/lib/ea/qea/factory/reference_resolver.rb +99 -0
- data/lib/ea/qea/factory/stereotype_loader.rb +39 -0
- data/lib/ea/qea/factory/tagged_value_transformer.rb +38 -0
- data/lib/ea/qea/factory/transformer_registry.rb +80 -0
- data/lib/ea/qea/factory.rb +37 -0
- data/lib/ea/qea/file_detector.rb +178 -0
- data/lib/ea/qea/infrastructure/database_connection.rb +100 -0
- data/lib/ea/qea/infrastructure/schema_reader.rb +136 -0
- data/lib/ea/qea/infrastructure/table_reader.rb +224 -0
- data/lib/ea/qea/infrastructure.rb +12 -0
- data/lib/ea/qea/models/base_model.rb +59 -0
- data/lib/ea/qea/models/ea_attribute.rb +109 -0
- data/lib/ea/qea/models/ea_attribute_tag.rb +100 -0
- data/lib/ea/qea/models/ea_complexity_type.rb +79 -0
- data/lib/ea/qea/models/ea_connector.rb +160 -0
- data/lib/ea/qea/models/ea_connector_type.rb +60 -0
- data/lib/ea/qea/models/ea_constraint_type.rb +63 -0
- data/lib/ea/qea/models/ea_datatype.rb +104 -0
- data/lib/ea/qea/models/ea_diagram.rb +115 -0
- data/lib/ea/qea/models/ea_diagram_link.rb +78 -0
- data/lib/ea/qea/models/ea_diagram_object.rb +73 -0
- data/lib/ea/qea/models/ea_diagram_type.rb +56 -0
- data/lib/ea/qea/models/ea_document.rb +63 -0
- data/lib/ea/qea/models/ea_object.rb +223 -0
- data/lib/ea/qea/models/ea_object_constraint.rb +53 -0
- data/lib/ea/qea/models/ea_object_property.rb +87 -0
- data/lib/ea/qea/models/ea_object_type.rb +73 -0
- data/lib/ea/qea/models/ea_operation.rb +127 -0
- data/lib/ea/qea/models/ea_operation_param.rb +76 -0
- data/lib/ea/qea/models/ea_package.rb +78 -0
- data/lib/ea/qea/models/ea_script.rb +62 -0
- data/lib/ea/qea/models/ea_status_type.rb +66 -0
- data/lib/ea/qea/models/ea_stereotype.rb +57 -0
- data/lib/ea/qea/models/ea_tagged_value.rb +99 -0
- data/lib/ea/qea/models/ea_xref.rb +165 -0
- data/lib/ea/qea/models.rb +35 -0
- data/lib/ea/qea/repositories/base_repository.rb +225 -0
- data/lib/ea/qea/repositories/object_repository.rb +219 -0
- data/lib/ea/qea/repositories.rb +10 -0
- data/lib/ea/qea/services/configuration.rb +211 -0
- data/lib/ea/qea/services/database_loader.rb +191 -0
- data/lib/ea/qea/services.rb +10 -0
- data/lib/ea/qea/validation/association_validator.rb +73 -0
- data/lib/ea/qea/validation/attribute_validator.rb +91 -0
- data/lib/ea/qea/validation/base_validator.rb +331 -0
- data/lib/ea/qea/validation/class_validator.rb +121 -0
- data/lib/ea/qea/validation/database/circular_reference_validator.rb +109 -0
- data/lib/ea/qea/validation/database/orphan_validator.rb +153 -0
- data/lib/ea/qea/validation/database/referential_integrity_validator.rb +128 -0
- data/lib/ea/qea/validation/database.rb +16 -0
- data/lib/ea/qea/validation/diagram_validator.rb +112 -0
- data/lib/ea/qea/validation/formatters/json_formatter.rb +137 -0
- data/lib/ea/qea/validation/formatters/text_formatter.rb +235 -0
- data/lib/ea/qea/validation/formatters.rb +12 -0
- data/lib/ea/qea/validation/operation_validator.rb +71 -0
- data/lib/ea/qea/validation/package_validator.rb +111 -0
- data/lib/ea/qea/validation/validation_engine.rb +387 -0
- data/lib/ea/qea/validation/validation_message.rb +144 -0
- data/lib/ea/qea/validation/validation_result.rb +210 -0
- data/lib/ea/qea/validation/validator_registry.rb +134 -0
- data/lib/ea/qea/validation.rb +28 -0
- data/lib/ea/qea/verification/comparison_result.rb +264 -0
- data/lib/ea/qea/verification/document_normalizer.rb +169 -0
- data/lib/ea/qea/verification/document_verifier.rb +322 -0
- data/lib/ea/qea/verification/element_comparator.rb +277 -0
- data/lib/ea/qea/verification/structure_matcher.rb +287 -0
- data/lib/ea/qea/verification.rb +14 -0
- data/lib/ea/qea.rb +185 -0
- data/lib/ea/transformations/configuration.rb +333 -0
- data/lib/ea/transformations/format_registry.rb +366 -0
- data/lib/ea/transformations/parsers/base_parser.rb +482 -0
- data/lib/ea/transformations/parsers/qea_parser.rb +401 -0
- data/lib/ea/transformations/parsers/xmi_parser.rb +243 -0
- data/lib/ea/transformations/transformation_engine.rb +390 -0
- data/lib/ea/transformations.rb +85 -0
- data/lib/ea/transformers/qea_to_xmi/association_end.rb +19 -0
- data/lib/ea/transformers/qea_to_xmi/cardinality.rb +96 -0
- data/lib/ea/transformers/qea_to_xmi/context.rb +106 -0
- data/lib/ea/transformers/qea_to_xmi/guid_format.rb +56 -0
- data/lib/ea/transformers/qea_to_xmi/id_allocator.rb +92 -0
- data/lib/ea/transformers/qea_to_xmi/run_state.rb +107 -0
- data/lib/ea/transformers/qea_to_xmi/transformer.rb +607 -0
- data/lib/ea/transformers/qea_to_xmi/visibility.rb +73 -0
- data/lib/ea/transformers/qea_to_xmi.rb +29 -0
- data/lib/ea/transformers/uml_to_xmi/id_generator.rb +54 -0
- data/lib/ea/transformers/uml_to_xmi/transformer.rb +152 -0
- data/lib/ea/transformers/uml_to_xmi/writer.rb +96 -0
- data/lib/ea/transformers/uml_to_xmi.rb +16 -0
- data/lib/ea/transformers.rb +34 -0
- data/lib/ea/version.rb +1 -1
- data/lib/ea/xmi/liquid_drops/association_drop.rb +56 -0
- data/lib/ea/xmi/liquid_drops/attribute_drop.rb +72 -0
- data/lib/ea/xmi/liquid_drops/cardinality_drop.rb +35 -0
- data/lib/ea/xmi/liquid_drops/connector_drop.rb +54 -0
- data/lib/ea/xmi/liquid_drops/constraint_drop.rb +29 -0
- data/lib/ea/xmi/liquid_drops/data_type_drop.rb +63 -0
- data/lib/ea/xmi/liquid_drops/dependency_drop.rb +36 -0
- data/lib/ea/xmi/liquid_drops/diagram_drop.rb +34 -0
- data/lib/ea/xmi/liquid_drops/enum_drop.rb +49 -0
- data/lib/ea/xmi/liquid_drops/enum_owned_literal_drop.rb +25 -0
- data/lib/ea/xmi/liquid_drops/generalization_attribute_drop.rb +87 -0
- data/lib/ea/xmi/liquid_drops/generalization_drop.rb +127 -0
- data/lib/ea/xmi/liquid_drops/klass_drop.rb +191 -0
- data/lib/ea/xmi/liquid_drops/operation_drop.rb +29 -0
- data/lib/ea/xmi/liquid_drops/package_drop.rb +108 -0
- data/lib/ea/xmi/liquid_drops/root_drop.rb +34 -0
- data/lib/ea/xmi/liquid_drops/source_target_drop.rb +43 -0
- data/lib/ea/xmi/lookup_service.rb +89 -0
- data/lib/ea/xmi/parser.rb +919 -0
- data/lib/ea/xmi.rb +35 -0
- data/lib/ea.rb +10 -1
- metadata +382 -9
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Transformers
|
|
5
|
+
# Full-fidelity transformer: Ea::Qea::Database → Sparx XMI.
|
|
6
|
+
#
|
|
7
|
+
# Walks the package tree starting at root packages, building
|
|
8
|
+
# Xmi::Sparx::Root / Xmi::Uml::UmlModel / Xmi::Uml::PackagedElement
|
|
9
|
+
# models from each QEA row, then asks the xmi gem to serialize them
|
|
10
|
+
# via `to_xml(use_prefix: true)` to produce Sparx XMI in the canonical
|
|
11
|
+
# mixed-prefix style.
|
|
12
|
+
#
|
|
13
|
+
# Use this for Sparx-to-Sparx round-trip — no intermediate
|
|
14
|
+
# Lutaml::Uml::Document, no loss of Sparx-specific concepts
|
|
15
|
+
# (multiplicities, tagged values, stereotypes, primitive types,
|
|
16
|
+
# instance specifications, association ends). For a tool-agnostic
|
|
17
|
+
# UML document → XMI path, use UmlToXmi.
|
|
18
|
+
module QeaToXmi
|
|
19
|
+
autoload :Transformer, "ea/transformers/qea_to_xmi/transformer"
|
|
20
|
+
autoload :Context, "ea/transformers/qea_to_xmi/context"
|
|
21
|
+
autoload :IdAllocator, "ea/transformers/qea_to_xmi/id_allocator"
|
|
22
|
+
autoload :GuidFormat, "ea/transformers/qea_to_xmi/guid_format"
|
|
23
|
+
autoload :Cardinality, "ea/transformers/qea_to_xmi/cardinality"
|
|
24
|
+
autoload :Visibility, "ea/transformers/qea_to_xmi/visibility"
|
|
25
|
+
autoload :RunState, "ea/transformers/qea_to_xmi/run_state"
|
|
26
|
+
autoload :AssociationEnd, "ea/transformers/qea_to_xmi/association_end"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Transformers
|
|
5
|
+
module UmlToXmi
|
|
6
|
+
# Allocates xmi:id values for UML elements during lossy serialization.
|
|
7
|
+
#
|
|
8
|
+
# Strategy:
|
|
9
|
+
# 1. If the element already has an `xmi_id` (set by the QEA factory
|
|
10
|
+
# during parsing), reuse it.
|
|
11
|
+
# 2. Otherwise synthesize a stable `EAID_…` from a counter.
|
|
12
|
+
#
|
|
13
|
+
# This class is used only by the lossy {UmlToXmi::Transformer} path.
|
|
14
|
+
# The full-fidelity {QeaToXmi} path uses {QeaToXmi::GuidFormat} to
|
|
15
|
+
# normalize raw EA GUIDs.
|
|
16
|
+
class IdGenerator
|
|
17
|
+
PREFIX = "EAID"
|
|
18
|
+
MODEL_ID = "#{PREFIX}_EA_MODEL"
|
|
19
|
+
|
|
20
|
+
def initialize
|
|
21
|
+
@assigned = {} # object_id → xmi:id
|
|
22
|
+
@counter = 0
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def eaid_for(element)
|
|
26
|
+
key = element.object_id
|
|
27
|
+
@assigned[key] ||= begin
|
|
28
|
+
@counter += 1
|
|
29
|
+
extract_id(element) || synthesize(@counter)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def model_id
|
|
34
|
+
MODEL_ID
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# `Lutaml::Uml::Value` exposes its XMI identifier via `id` (it does
|
|
40
|
+
# not inherit from TopElement, so it has no `xmi_id`). All other
|
|
41
|
+
# serializable UML types use `xmi_id`.
|
|
42
|
+
def extract_id(element)
|
|
43
|
+
return element.id if element.is_a?(Lutaml::Uml::Value)
|
|
44
|
+
|
|
45
|
+
element.xmi_id
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def synthesize(n)
|
|
49
|
+
format("%<prefix>s_%<hex>08X", prefix: PREFIX, hex: n)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Transformers
|
|
5
|
+
module UmlToXmi
|
|
6
|
+
# Orchestrates transformation of a {Lutaml::Uml::Document} to Sparx XMI.
|
|
7
|
+
#
|
|
8
|
+
# Walks the UML tree (packages → classifiers → features) and emits XML
|
|
9
|
+
# via {Writer}. Each UML element type has its own private emitter method.
|
|
10
|
+
#
|
|
11
|
+
# This is the LOSSY path — Sparx-specific concepts (stereotypes,
|
|
12
|
+
# multiplicities, tagged values, EA extension block) are not modeled in
|
|
13
|
+
# Lutaml::Uml and therefore not emitted. For Sparx-to-Sparx round-trip
|
|
14
|
+
# with full fidelity, use {Ea::Transformers::QeaToXmi}.
|
|
15
|
+
class Transformer
|
|
16
|
+
MODEL_NAME = "EA_Model"
|
|
17
|
+
|
|
18
|
+
def initialize(document)
|
|
19
|
+
@document = document
|
|
20
|
+
@id_gen = IdGenerator.new
|
|
21
|
+
@writer = Writer.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @return [String] XMI XML document
|
|
25
|
+
def serialize
|
|
26
|
+
assign_ids!
|
|
27
|
+
@writer.xmi_root do
|
|
28
|
+
@writer.documentation
|
|
29
|
+
@writer.uml_model(@id_gen.model_id, MODEL_NAME) do
|
|
30
|
+
emit_top_level
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
@writer.to_xml
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def assign_ids!
|
|
39
|
+
walk_all(@document.packages)
|
|
40
|
+
walk_all(@document.classes)
|
|
41
|
+
walk_all(@document.enums)
|
|
42
|
+
walk_all(@document.data_types)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def walk_all(elements)
|
|
46
|
+
elements.each { |e| register!(e) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def register!(element)
|
|
50
|
+
@id_gen.eaid_for(element)
|
|
51
|
+
case element
|
|
52
|
+
when Lutaml::Uml::Package then register_package!(element)
|
|
53
|
+
when Lutaml::Uml::UmlClass then register_class!(element)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def register_package!(pkg)
|
|
58
|
+
walk_all(pkg.packages)
|
|
59
|
+
walk_all(pkg.classes)
|
|
60
|
+
walk_all(pkg.enums)
|
|
61
|
+
walk_all(pkg.data_types)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def register_class!(klass)
|
|
65
|
+
klass.attributes.each { |a| @id_gen.eaid_for(a) }
|
|
66
|
+
klass.operations.each { |o| @id_gen.eaid_for(o) }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def emit_top_level
|
|
70
|
+
@document.packages.each { |p| emit_package(@writer, p) }
|
|
71
|
+
@document.classes.each { |c| emit_class(@writer, c) }
|
|
72
|
+
@document.enums.each { |e| emit_enum(@writer, e) }
|
|
73
|
+
@document.data_types.each { |d| emit_data_type(@writer, d) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def emit_package(w, pkg)
|
|
77
|
+
w.packaged_element("uml:Package", @id_gen.eaid_for(pkg), pkg.name) do
|
|
78
|
+
pkg.packages.each { |p| emit_package(w, p) }
|
|
79
|
+
pkg.classes.each { |c| emit_class(w, c) }
|
|
80
|
+
pkg.enums.each { |e| emit_enum(w, e) }
|
|
81
|
+
pkg.data_types.each { |d| emit_data_type(w, d) }
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def emit_class(w, klass)
|
|
86
|
+
w.packaged_element(
|
|
87
|
+
"uml:Class",
|
|
88
|
+
@id_gen.eaid_for(klass),
|
|
89
|
+
klass.name,
|
|
90
|
+
is_abstract: klass.is_abstract,
|
|
91
|
+
) do
|
|
92
|
+
klass.attributes.each { |a| emit_attribute(w, a) }
|
|
93
|
+
klass.operations.each { |o| emit_operation(w, o) }
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def emit_enum(w, enum)
|
|
98
|
+
w.packaged_element(
|
|
99
|
+
"uml:Enumeration",
|
|
100
|
+
@id_gen.eaid_for(enum),
|
|
101
|
+
enum.name,
|
|
102
|
+
) do
|
|
103
|
+
(enum.values || []).each { |v| emit_literal(w, v) }
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def emit_data_type(w, data_type)
|
|
108
|
+
w.packaged_element(
|
|
109
|
+
"uml:DataType",
|
|
110
|
+
@id_gen.eaid_for(data_type),
|
|
111
|
+
data_type.name,
|
|
112
|
+
) do
|
|
113
|
+
data_type.attributes.each { |a| emit_attribute(w, a) }
|
|
114
|
+
data_type.operations.each { |o| emit_operation(w, o) }
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def emit_attribute(w, attr)
|
|
119
|
+
type_ref = type_reference(attr.type)
|
|
120
|
+
w.owned_attribute(@id_gen.eaid_for(attr), attr.name, type: type_ref) do
|
|
121
|
+
# Multiplicity / defaults not modeled in Lutaml::Uml; deferred.
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def emit_operation(w, op)
|
|
126
|
+
w.owned_operation(@id_gen.eaid_for(op), op.name)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def emit_literal(w, literal)
|
|
130
|
+
case literal
|
|
131
|
+
when Lutaml::Uml::Value
|
|
132
|
+
w.owned_literal(@id_gen.eaid_for(literal), literal.name)
|
|
133
|
+
when String
|
|
134
|
+
w.owned_literal(synthesize_literal_id(literal), literal)
|
|
135
|
+
else
|
|
136
|
+
w.owned_literal(synthesize_literal_id(literal.to_s), literal.to_s)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def type_reference(type_name)
|
|
141
|
+
return nil if type_name.nil? || type_name.empty?
|
|
142
|
+
|
|
143
|
+
type_name
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def synthesize_literal_id(name)
|
|
147
|
+
format("EAID_LIT_%<hex>08X", hex: name.to_s.bytes.sum)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "nokogiri"
|
|
4
|
+
|
|
5
|
+
module Ea
|
|
6
|
+
module Transformers
|
|
7
|
+
module UmlToXmi
|
|
8
|
+
# Low-level XML structural primitives for emitting Sparx-flavored XMI.
|
|
9
|
+
#
|
|
10
|
+
# This class has no UML domain knowledge — it only knows the XML shapes
|
|
11
|
+
# (XMI root, uml:Model, packagedElement, ownedAttribute, …) required to
|
|
12
|
+
# produce a well-formed Sparx XMI document. {Transformer} drives it.
|
|
13
|
+
class Writer
|
|
14
|
+
XMI_NS = "http://www.omg.org/spec/XMI/20131001"
|
|
15
|
+
UML_NS = "http://www.omg.org/spec/UML/20161101"
|
|
16
|
+
|
|
17
|
+
attr_reader :builder
|
|
18
|
+
|
|
19
|
+
def initialize
|
|
20
|
+
@builder = Nokogiri::XML::Builder.new(encoding: "UTF-8")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def xmi_root
|
|
24
|
+
@builder["xmi"].XMI(root_attributes) { yield self }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def documentation(exporter: "ea-rb", exporter_version: Ea::VERSION)
|
|
28
|
+
@builder["xmi"].Documentation(
|
|
29
|
+
exporter: exporter,
|
|
30
|
+
exporterVersion: exporter_version,
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def uml_model(id, name)
|
|
35
|
+
@builder["uml"].Model(
|
|
36
|
+
"xmi:type": "uml:Model",
|
|
37
|
+
"xmi:id": id,
|
|
38
|
+
name: name,
|
|
39
|
+
) { yield self }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def packaged_element(uml_type, id, name, **attrs)
|
|
43
|
+
@builder.packagedElement(packaged_attrs(uml_type, id, name, attrs)) do
|
|
44
|
+
yield self if block_given?
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def owned_attribute(id, name, type: nil, visibility: "public")
|
|
49
|
+
attrs = { "xmi:type": "uml:Property", "xmi:id": id, name: name,
|
|
50
|
+
visibility: visibility }
|
|
51
|
+
attrs[:type] = type if type
|
|
52
|
+
@builder.ownedAttribute(attrs) { yield self if block_given? }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def owned_operation(id, name, visibility: "public")
|
|
56
|
+
@builder.ownedOperation(
|
|
57
|
+
"xmi:type": "uml:Operation", "xmi:id": id, name: name,
|
|
58
|
+
visibility: visibility,
|
|
59
|
+
) { yield self if block_given? }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def owned_literal(id, name)
|
|
63
|
+
@builder.ownedLiteral(
|
|
64
|
+
"xmi:type": "uml:EnumerationLiteral", "xmi:id": id, name: name,
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def generalization(id, general_id)
|
|
69
|
+
@builder.generalization(
|
|
70
|
+
"xmi:type": "uml:Generalization",
|
|
71
|
+
"xmi:id": id,
|
|
72
|
+
general: general_id,
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def to_xml
|
|
77
|
+
@builder.doc.to_xml
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def root_attributes
|
|
83
|
+
{ "xmlns:xmi": XMI_NS, "xmlns:uml": UML_NS }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def packaged_attrs(uml_type, id, name, attrs)
|
|
87
|
+
result = { "xmi:type": uml_type, "xmi:id": id }
|
|
88
|
+
result[:name] = name if name
|
|
89
|
+
result[:"isAbstract"] = "true" if attrs[:is_abstract]
|
|
90
|
+
result[:visibility] = attrs[:visibility] if attrs[:visibility]
|
|
91
|
+
result
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Transformers
|
|
5
|
+
# Lossy transformer: Lutaml::Uml::Document → Sparx XMI.
|
|
6
|
+
#
|
|
7
|
+
# Use this when the source is a tool-agnostic UML model (from LML,
|
|
8
|
+
# MagicDraw, Papyrus, etc.) and some information loss is acceptable.
|
|
9
|
+
# For Sparx QEA → Sparx XMI, use {Ea::Transformers::QeaToXmi} instead.
|
|
10
|
+
module UmlToXmi
|
|
11
|
+
autoload :Transformer, "ea/transformers/uml_to_xmi/transformer"
|
|
12
|
+
autoload :Writer, "ea/transformers/uml_to_xmi/writer"
|
|
13
|
+
autoload :IdGenerator, "ea/transformers/uml_to_xmi/id_generator"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
# Output transformers — converts domain models to interchange formats.
|
|
5
|
+
#
|
|
6
|
+
# Two entry points:
|
|
7
|
+
#
|
|
8
|
+
# {uml_to_xmi} — lossy, takes a Lutaml::Uml::Document (cross-tool use)
|
|
9
|
+
# {qea_to_xmi} — full fidelity, takes an Ea::Qea::Database
|
|
10
|
+
# (Sparx-to-Sparx round-trip)
|
|
11
|
+
module Transformers
|
|
12
|
+
autoload :UmlToXmi, "ea/transformers/uml_to_xmi"
|
|
13
|
+
autoload :QeaToXmi, "ea/transformers/qea_to_xmi"
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
# Lossy: any Lutaml::Uml::Document → Sparx XMI (cross-tool).
|
|
17
|
+
# @param document [Lutaml::Uml::Document]
|
|
18
|
+
# @return [String] XMI XML
|
|
19
|
+
def uml_to_xmi(document)
|
|
20
|
+
UmlToXmi::Transformer.new(document).serialize
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Full fidelity: Ea::Qea::Database → Sparx XMI.
|
|
24
|
+
# Walks the QEA tables directly — no intermediate UML model, no loss of
|
|
25
|
+
# Sparx-specific concepts (stereotypes, tagged values, multiplicities,
|
|
26
|
+
# diagrams, xrefs).
|
|
27
|
+
# @param database [Ea::Qea::Database]
|
|
28
|
+
# @return [String] XMI XML
|
|
29
|
+
def qea_to_xmi(database)
|
|
30
|
+
QeaToXmi::Transformer.new(database).serialize
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
data/lib/ea/version.rb
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Xmi
|
|
5
|
+
module LiquidDrops
|
|
6
|
+
class AssociationDrop < Liquid::Drop
|
|
7
|
+
def initialize(association, options = {}) # rubocop:disable Lint/MissingSuper
|
|
8
|
+
@model = association
|
|
9
|
+
@options = options
|
|
10
|
+
@lookup = options[:lookup]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def xmi_id
|
|
14
|
+
@model.xmi_id
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def member_end
|
|
18
|
+
@model.member_end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def member_end_type
|
|
22
|
+
@model.member_end_type
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def member_end_cardinality
|
|
26
|
+
::Ea::Xmi::LiquidDrops::CardinalityDrop.new(@model.member_end_cardinality)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def member_end_attribute_name
|
|
30
|
+
@model.member_end_attribute_name
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def member_end_xmi_id
|
|
34
|
+
@model.member_end_xmi_id
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def owner_end
|
|
38
|
+
@model.owner_end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def owner_end_xmi_id
|
|
42
|
+
@model.owner_end_xmi_id
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def definition
|
|
46
|
+
@model.definition
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def connector
|
|
50
|
+
connector = @lookup.fetch_connector(@model.xmi_id)
|
|
51
|
+
::Ea::Xmi::LiquidDrops::ConnectorDrop.new(connector, @options)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Xmi
|
|
5
|
+
module LiquidDrops
|
|
6
|
+
class AttributeDrop < Liquid::Drop
|
|
7
|
+
def initialize(model, options = {}) # rubocop:disable Lint/MissingSuper
|
|
8
|
+
@model = model
|
|
9
|
+
@options = options
|
|
10
|
+
@lookup = options[:lookup]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def id
|
|
14
|
+
@model.id
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def name
|
|
18
|
+
@model.name
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def type
|
|
22
|
+
@model.type
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def xmi_id
|
|
26
|
+
@model.xmi_id
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def is_derived # rubocop:disable Naming/PredicateName,Naming/PredicatePrefix
|
|
30
|
+
@model.is_derived
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def cardinality
|
|
34
|
+
::Ea::Xmi::LiquidDrops::CardinalityDrop.new(@model.cardinality)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def definition
|
|
38
|
+
if @options[:with_assoc] && @model.association
|
|
39
|
+
@lookup.loopup_assoc_def(@model.association)
|
|
40
|
+
else
|
|
41
|
+
@model.definition
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def association
|
|
46
|
+
if @options[:with_assoc] && @model.association
|
|
47
|
+
@model.association
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def association_connector
|
|
52
|
+
return unless @model.association
|
|
53
|
+
|
|
54
|
+
connector = @lookup.fetch_connector(@model.association)
|
|
55
|
+
if connector
|
|
56
|
+
::Ea::Xmi::LiquidDrops::ConnectorDrop.new(connector, @options)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def type_ns
|
|
61
|
+
if @options[:with_assoc] && @model.association
|
|
62
|
+
@model.type_ns
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def stereotype
|
|
67
|
+
@lookup.doc_node_attribute_value(@model.xmi_id, "stereotype")
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Xmi
|
|
5
|
+
module LiquidDrops
|
|
6
|
+
class CardinalityDrop < Liquid::Drop
|
|
7
|
+
def initialize(model) # rubocop:disable Lint/MissingSuper
|
|
8
|
+
@model = model
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def min
|
|
12
|
+
return nil unless @model
|
|
13
|
+
|
|
14
|
+
case @model
|
|
15
|
+
when ::Lutaml::Uml::Cardinality
|
|
16
|
+
@model.min
|
|
17
|
+
else
|
|
18
|
+
@model.lower_value&.value
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def max
|
|
23
|
+
return nil unless @model
|
|
24
|
+
|
|
25
|
+
case @model
|
|
26
|
+
when ::Lutaml::Uml::Cardinality
|
|
27
|
+
@model.max
|
|
28
|
+
else
|
|
29
|
+
@model.upper_value&.value
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Xmi
|
|
5
|
+
module LiquidDrops
|
|
6
|
+
class ConnectorDrop < Liquid::Drop
|
|
7
|
+
def initialize(model, options = {}) # rubocop:disable Lint/MissingSuper
|
|
8
|
+
@model = model
|
|
9
|
+
@options = options
|
|
10
|
+
@id_name_mapping = options[:id_name_mapping]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def idref
|
|
14
|
+
@model.idref
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def name
|
|
18
|
+
@model.name
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def type
|
|
22
|
+
@model&.properties&.ea_type
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def documentation
|
|
26
|
+
@model&.documentation&.value
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def ea_type
|
|
30
|
+
@model&.properties&.ea_type
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def direction
|
|
34
|
+
@model&.properties&.direction
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def source
|
|
38
|
+
::Ea::Xmi::LiquidDrops::SourceTargetDrop.new(@model.source,
|
|
39
|
+
@options)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def target
|
|
43
|
+
::Ea::Xmi::LiquidDrops::SourceTargetDrop.new(@model.target,
|
|
44
|
+
@options)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def recognized?
|
|
48
|
+
!!@id_name_mapping[@model.source.idref] &&
|
|
49
|
+
!!@id_name_mapping[@model.target.idref]
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Xmi
|
|
5
|
+
module LiquidDrops
|
|
6
|
+
class ConstraintDrop < Liquid::Drop
|
|
7
|
+
def initialize(model) # rubocop:disable Lint/MissingSuper
|
|
8
|
+
@model = model
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def name
|
|
12
|
+
@model.name
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def type
|
|
16
|
+
@model.type
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def weight
|
|
20
|
+
@model.weight
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def status
|
|
24
|
+
@model.status
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|