ea 0.4.0 → 0.5.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.
Files changed (529) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +3 -0
  3. data/TODO.complete/01-t-xref-parsing.md +91 -0
  4. data/TODO.complete/02-per-label-geometry.md +50 -0
  5. data/TODO.complete/03-object-properties-constraints.md +57 -0
  6. data/TODO.complete/04-operation-params.md +37 -0
  7. data/TODO.complete/05-pdata-flag-matrix.md +52 -0
  8. data/TODO.complete/06-emf-image-rendering.md +34 -0
  9. data/TODO.complete/07-auxiliary-tables.md +47 -0
  10. data/TODO.complete/10-mdg-stereotype-registry.md +40 -0
  11. data/TODO.complete/11-gml-xsd-generation.md +42 -0
  12. data/TODO.complete/13-shapescript-interpreter.md +28 -0
  13. data/TODO.complete/20-cli-diff.md +46 -0
  14. data/TODO.complete/21-cli-render.md +26 -0
  15. data/TODO.complete/22-cli-export.md +29 -0
  16. data/TODO.complete/23-cli-mdg.md +23 -0
  17. data/TODO.complete/30-headerlines-ocp-refactor.md +72 -0
  18. data/TODO.complete/31-public-send-cleanup.md +61 -0
  19. data/TODO.complete/32-spec-coverage.md +33 -0
  20. data/TODO.complete/33-blocked-parity-gaps.md +41 -0
  21. data/TODO.complete/36-stereotype-icon-wiring.md +32 -0
  22. data/TODO.complete/37-cli-lint.md +32 -0
  23. data/TODO.complete/38-cli-query.md +26 -0
  24. data/TODO.complete/39-cli-info.md +23 -0
  25. data/TODO.complete/40-ocl-evaluator.md +31 -0
  26. data/TODO.complete/41-round-trip-spec.md +18 -0
  27. data/TODO.complete/42-html-diff-report.md +18 -0
  28. data/TODO.complete/43-json-schema-export.md +20 -0
  29. data/TODO.complete/44-remaining-qea-tables.md +40 -0
  30. data/TODO.complete/45-shapescript-extensions.md +25 -0
  31. data/TODO.complete/46-xsd-consolidation.md +20 -0
  32. data/TODO.complete/47-headerlines-legacy-removal.md +20 -0
  33. data/TODO.complete/STATUS.md +109 -0
  34. data/TODO.diagrams/01-svg-measurement-harness.md +31 -0
  35. data/TODO.diagrams/02-polygon-over-rendering.md +29 -0
  36. data/TODO.diagrams/03-text-over-rendering.md +30 -0
  37. data/TODO.diagrams/04-rect-under-rendering.md +33 -0
  38. data/TODO.diagrams/05-canvas-dimension-parity.md +51 -0
  39. data/TODO.diagrams/06-group-layering-parity.md +39 -0
  40. data/TODO.diagrams/07-text-content-fidelity.md +65 -0
  41. data/TODO.diagrams/08-qea-direct-pipeline.md +46 -0
  42. data/TODO.diagrams/12-connector-labels.md +40 -0
  43. data/TODO.diagrams/12-per-entity-svg-layer-ordering.md +24 -0
  44. data/TODO.diagrams/13-marker-polygon-vs-path-dispatch.md +20 -0
  45. data/TODO.diagrams/13-per-text-type-font-sizes.md +33 -0
  46. data/TODO.diagrams/14-center-header-text.md +33 -0
  47. data/TODO.diagrams/14-filter-diagram-frame-elements.md +18 -0
  48. data/TODO.diagrams/15-deep-dead-code-audit.md +47 -0
  49. data/TODO.diagrams/15-per-element-font-from-style.md +24 -0
  50. data/TODO.diagrams/16-canvas-marker-extent.md +30 -0
  51. data/TODO.diagrams/16-document-remaining-parity-gaps.md +91 -0
  52. data/TODO.diagrams/17-italic-abstract-classes.md +28 -0
  53. data/TODO.diagrams/17-remaining-parity-gaps.md +90 -0
  54. data/TODO.diagrams/18-canvas-from-image-bounds.md +20 -0
  55. data/TODO.diagrams/18-property-lookup-index.md +42 -0
  56. data/TODO.diagrams/19-connector-sx-sy-routing.md +27 -0
  57. data/TODO.diagrams/19-replace-respond-to-with-is-a.md +31 -0
  58. data/TODO.diagrams/20-split-labels-emitter.md +56 -0
  59. data/TODO.diagrams/20-visibility-glyph-font-encoding.md +27 -0
  60. data/TODO.diagrams/21-aggregation-navigability-arrow.md +21 -0
  61. data/TODO.diagrams/21-auto-generated-legend-block.md +65 -0
  62. data/TODO.diagrams/22-ghost-element-discriminator.md +57 -0
  63. data/TODO.diagrams/22-style-based-path-grouping.md +28 -0
  64. data/TODO.diagrams/23-marker-polygon-deduplication.md +47 -0
  65. data/TODO.diagrams/23-multiplicity-label-positioning.md +29 -0
  66. data/TODO.diagrams/24-marker-anchor-structural.md +35 -0
  67. data/TODO.diagrams/24-track-marker-anchors-structurally.md +66 -0
  68. data/TODO.diagrams/24-truncate-properties-to-fit-box.md +24 -0
  69. data/TODO.diagrams/26-phantom-general-parent-rendering.md +59 -0
  70. data/TODO.diagrams/27-path-grouping-mode-detection.md +43 -0
  71. data/TODO.diagrams/28-architecture-cleanup-batch.md +47 -0
  72. data/TODO.diagrams/30-decompose-elements-groups-for.md +52 -0
  73. data/TODO.diagrams/32-examples-coverage-batch.md +78 -0
  74. data/TODO.diagrams/36-instance-specification-model.md +42 -0
  75. data/TODO.diagrams/37-element-icon-decorations.md +41 -0
  76. data/TODO.diagrams/38-html-note-and-receptions.md +32 -0
  77. data/TODO.diagrams/39-property-stereotype-discriminator.md +40 -0
  78. data/TODO.diagrams/40-standalone-1-discriminator.md +38 -0
  79. data/TODO.diagrams/41-xmi-to-svg-pipeline.md +113 -0
  80. data/TODO.diagrams/42-close-all-parity-deltas.md +69 -0
  81. data/TODO.diagrams/43-marker-deduplication-strategy.md +54 -0
  82. data/TODO.diagrams/44-element-icon-decorations.md +34 -0
  83. data/TODO.diagrams/45-phantom-parent-class-boxes.md +40 -0
  84. data/TODO.diagrams/46-user-drawn-legend-swatches.md +44 -0
  85. data/TODO.diagrams/47-attribute-line-fidelity.md +72 -0
  86. data/TODO.diagrams/48-spec-coverage-critical-emitter-components.md +45 -0
  87. data/TODO.diagrams/49-spec-coverage-orchestrators.md +25 -0
  88. data/TODO.diagrams/50-examples-parity.md +85 -0
  89. data/TODO.diagrams/51-property-oy-gap-and-remaining-gaps.md +48 -0
  90. data/TODO.diagrams/52-marker-registration-architecture.md +89 -0
  91. data/TODO.diagrams/53-remaining-example-gaps.md +86 -0
  92. data/TODO.diagrams/54-display-name-nested-classes.md +40 -0
  93. data/TODO.diagrams/55-visibility-icon-discriminator.md +58 -0
  94. data/TODO.diagrams/56-package-contents-rendering.md +148 -0
  95. data/TODO.diagrams/57-instance-specification-model.md +100 -0
  96. data/TODO.diagrams/58-domain-model-note-text.md +67 -0
  97. data/TODO.diagrams/59-classes-arrow-discriminator.md +57 -0
  98. data/TODO.diagrams/60-testschema-association-marker.md +40 -0
  99. data/TODO.diagrams/61-legend-block-rendering.md +55 -0
  100. data/TODO.diagrams/61-plateau-parity-gaps.md +59 -0
  101. data/TODO.diagrams/62-ghost-element-rendering.md +56 -0
  102. data/TODO.diagrams/63-qualified-name-wrap.md +36 -0
  103. data/TODO.diagrams/64-property-stereotype-labels.md +22 -0
  104. data/TODO.diagrams/65-path-coordinate-frame.md +62 -0
  105. data/TODO.diagrams/67-path-coordinate-frame.md +62 -0
  106. data/TODO.diagrams/68-inherited-attributes.md +65 -0
  107. data/TODO.diagrams/69-package-anchor-conditional.md +29 -0
  108. data/TODO.diagrams/70-mdg-technology-support.md +68 -0
  109. data/TODO.diagrams/71-off-canvas-parent-ghost.md +45 -0
  110. data/TODO.diagrams/72-basic-attr-divider-paths.md +39 -0
  111. data/TODO.diagrams/73-plateau-polygon-gap.md +69 -0
  112. data/TODO.diagrams/74-plateau-rect-gap.md +22 -0
  113. data/TODO.diagrams/75-plateau-text-gap.md +39 -0
  114. data/TODO.diagrams/76-basic-poly-path-gaps.md +41 -0
  115. data/TODO.diagrams/77-basic-text-overrender.md +40 -0
  116. data/TODO.diagrams/78-headerlines-ocp-refactor.md +65 -0
  117. data/TODO.diagrams/79-architecture-backlog.md +65 -0
  118. data/TODO.diagrams/80-basic-triangle-polygons.md +51 -0
  119. data/TODO.diagrams/81-plateau-path-overrender.md +53 -0
  120. data/TODO.diagrams/82-appearance-canvas-offset.md +54 -0
  121. data/TODO.diagrams/83-3d-diagram-subtitle.md +58 -0
  122. data/TODO.diagrams/84-canvas-offset-mismatches.md +61 -0
  123. data/TODO.diagrams/85-phantom-property-overrender.md +95 -0
  124. data/TODO.diagrams/86-foreign-package-rule.md +62 -0
  125. data/TODO.diagrams/87-internal-tech-encryption.md +80 -0
  126. data/TODO.diagrams/EXAMPLES_STATUS.md +88 -0
  127. data/TODO.diagrams/README.md +69 -0
  128. data/TODO.diagrams/STATUS.md +73 -0
  129. data/TODO.fixup/01-default-grouped-paths.md +34 -0
  130. data/TODO.fixup/02-marker-consolidation-per-connector.md +20 -0
  131. data/TODO.fixup/03-diagram-font-inference.md +28 -0
  132. data/TODO.fixup/04-extract-bounds-calculator.md +25 -0
  133. data/TODO.fixup/05-marker-registry-ocp.md +32 -0
  134. data/TODO.fixup/06-connector-path-bend-routing.md +28 -0
  135. data/TODO.fixup/07-edge-attachment-formula.md +22 -0
  136. data/TODO.fixup/08-visual-diff-harness.md +28 -0
  137. data/TODO.fixup/09-extract-element-compartments.md +34 -0
  138. data/TODO.fixup/10-canvas-width-overshoot-fix.md +20 -0
  139. data/TODO.fixup/11-layer-sequencer-extraction.md +18 -0
  140. data/TODO.fixup/12-path-style-harmony.md +23 -0
  141. data/TODO.fixup/13-datatype-stereotype-fallback.md +28 -0
  142. data/TODO.fixup/14-ea-exe-reverse-engineering-notes.md +56 -0
  143. data/TODO.fixup/15-enumeration-literal-rendering.md +30 -0
  144. data/TODO.fixup/16-operation-compartment-rendering.md +42 -0
  145. data/TODO.fixup/17-path-count-calibration.md +27 -0
  146. data/TODO.fixup/18-polygon-count-calibration.md +32 -0
  147. data/TODO.fixup/19-rect-count-undershoot-investigation.md +40 -0
  148. data/TODO.fixup/20-stereotype-color-from-classifier.md +18 -0
  149. data/TODO.fixup/21-visibility-filter-for-properties.md +24 -0
  150. data/TODO.fixup/22-multiplicity-display-rule-calibration.md +25 -0
  151. data/TODO.fixup/23-diagram-frame-title-element.md +31 -0
  152. data/TODO.fixup/24-note-element-rendering.md +23 -0
  153. data/TODO.fixup/25-ea-exe-disassembly-findings.md +177 -0
  154. data/TODO.fixup/26-svg-renderer-architecture.md +78 -0
  155. data/TODO.fixup/27-svg-element-emitters-disassembly.md +135 -0
  156. data/TODO.fixup/28-geometry-parser-identification.md +57 -0
  157. data/TODO.fixup/29-qea-svg-xmi-triple-comparison.md +185 -0
  158. data/TODO.fixup/30-theme-system-stub.md +46 -0
  159. data/TODO.fixup/31-theme-value-object-registry.md +17 -0
  160. data/TODO.fixup/32-styleex-on-diagram-model.md +22 -0
  161. data/TODO.fixup/33-text-encoding-decimals-rotation.md +29 -0
  162. data/TODO.fixup/34-per-type-fill-via-theme.md +18 -0
  163. data/TODO.fixup/35-stroke-width-per-theme.md +7 -0
  164. data/TODO.fixup/36-qea-rect-coord-normalization.md +24 -0
  165. data/TODO.fixup/37-package-shape-rendering.md +22 -0
  166. data/TODO.fixup/38-note-element-rendering.md +15 -0
  167. data/TODO.fixup/39-styleex-flag-honoring.md +25 -0
  168. data/TODO.fixup/40-connector-dashed-lines.md +15 -0
  169. data/TODO.fixup/41-diagram-frame-showborder-detection.md +23 -0
  170. data/TODO.fixup/42-textrenderer-centralization.md +10 -0
  171. data/TODO.fixup/43-colorresolver-unification.md +24 -0
  172. data/TODO.fixup/44-theme-aware-textrenderer.md +17 -0
  173. data/TODO.fixup/45-theme-aware-fontresolver.md +22 -0
  174. data/TODO.fixup/46-style-parameterized-by-theme.md +21 -0
  175. data/TODO.fixup/47-canvas-frame-inset.md +19 -0
  176. data/TODO.fixup/48-background-rect-format.md +15 -0
  177. data/TODO.fixup/49-all-samples-integration-test.md +17 -0
  178. data/TODO.fixup/50-svg-emitter-architecture-docs.md +33 -0
  179. data/TODO.fixup/51-promote-theme-to-ea-namespace.md +28 -0
  180. data/TODO.fixup/52-theme-definition-integrated-fills.md +26 -0
  181. data/TODO.fixup/53-theme-registry-yaml-loading.md +50 -0
  182. data/TODO.fixup/54-diagram-theme-api.md +50 -0
  183. data/TODO.fixup/55-update-eaemitter-to-ea-theme.md +23 -0
  184. data/TODO.fixup/56-theme-yaml-files.md +65 -0
  185. data/TODO.fixup/57-theme-specs-and-integration.md +27 -0
  186. data/TODO.fixup/58-ea-entry-point-theme-autoload.md +20 -0
  187. data/TODO.fixup/59-thread-theme-through-all-renderers.md +29 -0
  188. data/TODO.fixup/60-cli-theme-option.md +22 -0
  189. data/TODO.fixup/61-theme-validation.md +28 -0
  190. data/TODO.fixup/62-theme-builder-dsl.md +31 -0
  191. data/TODO.fixup/63-theme-integration-rendering-spec.md +24 -0
  192. data/TODO.fixup/64-theme-writeback-styleex.md +21 -0
  193. data/TODO.fixup/65-remove-dead-eaemitter-theme-files.md +30 -0
  194. data/TODO.fixup/66-styleex-writeback.md +22 -0
  195. data/TODO.fixup/67-delete-dead-code.md +28 -0
  196. data/TODO.fixup/68-figure-parity-status.md +82 -0
  197. data/TODO.fixup/69-package-polygon-rendering.md +54 -0
  198. data/TODO.fixup/70-tagged-values-compartment.md +39 -0
  199. data/TODO.fixup/71-auto-detect-theme-from-xmi.md +37 -0
  200. data/TODO.fixup/72-sample-exports-as-regression-specs.md +37 -0
  201. data/TODO.fixup/73-delete-dead-transformations.md +14 -0
  202. data/TODO.fixup/74-delete-dead-diagram-renderers.md +18 -0
  203. data/TODO.fixup/75-visibility-legend-icons.md +23 -0
  204. data/TODO.fixup/76-plateau-text-undershoot.md +27 -0
  205. data/TODO.fixup/77-canvas-frame-margins.md +28 -0
  206. data/TODO.fixup/78-note-element-rendering.md +30 -0
  207. data/TODO.fixup/79-operation-compartment-rendering.md +27 -0
  208. data/TODO.fixup/80-tagged-value-stereotype-visibility.md +37 -0
  209. data/TODO.fixup/81-figure-parity-current-state.md +63 -0
  210. data/config/qea_schema.yml +356 -0
  211. data/config/themes/119.yml +98 -0
  212. data/config/themes/default.yml +14 -0
  213. data/examples/exports/20251010_current_plateau_v5/UML_EA.DTD +6856 -0
  214. data/examples/exports/20251010_current_plateau_v5/model.xml +96709 -0
  215. data/examples/exports/ArcGISWorkspace_template/UML_EA.DTD +6856 -0
  216. data/examples/exports/ArcGISWorkspace_template/model.xml +1427 -0
  217. data/examples/exports/UmlModel_template/UML_EA.DTD +6856 -0
  218. data/examples/exports/UmlModel_template/model.xml +23 -0
  219. data/examples/exports/basic/UML_EA.DTD +6856 -0
  220. data/examples/exports/basic/model.xml +8039 -0
  221. data/examples/exports/simple/UML_EA.DTD +6856 -0
  222. data/examples/exports/simple/model.xml +585 -0
  223. data/examples/exports/simple_example/UML_EA.DTD +6856 -0
  224. data/examples/exports/simple_example/model.xml +23 -0
  225. data/examples/exports/test/UML_EA.DTD +6856 -0
  226. data/examples/exports/test/model.xml +1638 -0
  227. data/examples/qea/basic.qea +0 -0
  228. data/frontend/README.md +51 -0
  229. data/frontend/dist/app.iife.js +24 -0
  230. data/frontend/dist/style.css +1 -0
  231. data/frontend/index.html +26 -0
  232. data/frontend/package-lock.json +2359 -0
  233. data/frontend/package.json +26 -0
  234. data/frontend/src/App.vue +59 -0
  235. data/frontend/src/app.ts +8 -0
  236. data/frontend/src/components/AppHeader.vue +127 -0
  237. data/frontend/src/components/AppSidebar.vue +119 -0
  238. data/frontend/src/components/ClassDetails.vue +247 -0
  239. data/frontend/src/components/DiagramView.vue +79 -0
  240. data/frontend/src/components/PackageDetails.vue +92 -0
  241. data/frontend/src/components/PackageTreeNode.vue +64 -0
  242. data/frontend/src/components/SearchResults.vue +48 -0
  243. data/frontend/src/components/WelcomeView.vue +87 -0
  244. data/frontend/src/shims-vue.d.ts +5 -0
  245. data/frontend/src/stores/dataStore.ts +79 -0
  246. data/frontend/src/stores/uiStore.ts +167 -0
  247. data/frontend/src/styles/main.css +1566 -0
  248. data/frontend/src/types.ts +221 -0
  249. data/frontend/tests/e2e/__screenshots__/new-dark-mode.png +0 -0
  250. data/frontend/tests/e2e/__screenshots__/new-package-details.png +0 -0
  251. data/frontend/tests/e2e/__screenshots__/new-search-modal.png +0 -0
  252. data/frontend/tests/e2e/__screenshots__/new-sidebar-collapsed.png +0 -0
  253. data/frontend/tests/e2e/__screenshots__/new-welcome.png +0 -0
  254. data/frontend/tests/e2e/render.spec.ts +188 -0
  255. data/frontend/tsconfig.json +20 -0
  256. data/frontend/vite.config.ts +26 -0
  257. data/frontend/vitest.config.ts +8 -0
  258. data/lib/ea/cli/app.rb +57 -2
  259. data/lib/ea/cli/command/diagrams.rb +37 -28
  260. data/lib/ea/cli/command/diff.rb +46 -0
  261. data/lib/ea/cli/command/export.rb +80 -0
  262. data/lib/ea/cli/command/info.rb +75 -0
  263. data/lib/ea/cli/command/lint.rb +48 -0
  264. data/lib/ea/cli/command/mdg.rb +98 -0
  265. data/lib/ea/cli/command/query.rb +34 -0
  266. data/lib/ea/cli/command/render.rb +135 -0
  267. data/lib/ea/cli/command/svg.rb +8 -15
  268. data/lib/ea/cli/command.rb +7 -0
  269. data/lib/ea/diagram/display_config.rb +184 -0
  270. data/lib/ea/diagram.rb +4 -41
  271. data/lib/ea/diff/change.rb +30 -0
  272. data/lib/ea/diff/comparator.rb +82 -0
  273. data/lib/ea/diff/html_reporter.rb +91 -0
  274. data/lib/ea/diff.rb +15 -0
  275. data/lib/ea/export/json/generator.rb +42 -0
  276. data/lib/ea/export/json.rb +12 -0
  277. data/lib/ea/export/json_schema/generator.rb +77 -0
  278. data/lib/ea/export/json_schema.rb +12 -0
  279. data/lib/ea/export/plantuml/generator.rb +73 -0
  280. data/lib/ea/export/plantuml.rb +12 -0
  281. data/lib/ea/export/xmi/generator.rb +77 -0
  282. data/lib/ea/export/xmi.rb +11 -0
  283. data/lib/ea/export/xsd/class_mapping.rb +65 -0
  284. data/lib/ea/export/xsd/generator.rb +142 -0
  285. data/lib/ea/export/xsd/namespace_registry.rb +82 -0
  286. data/lib/ea/export/xsd.rb +19 -0
  287. data/lib/ea/export.rb +14 -0
  288. data/lib/ea/image/emf_renderer.rb +42 -0
  289. data/lib/ea/image.rb +12 -0
  290. data/lib/ea/lint/engine.rb +30 -0
  291. data/lib/ea/lint/lint_rule.rb +38 -0
  292. data/lib/ea/lint/offense.rb +18 -0
  293. data/lib/ea/lint/rules/cyclic_generalization.rb +62 -0
  294. data/lib/ea/lint/rules/duplicate_name.rb +28 -0
  295. data/lib/ea/lint/rules/missing_stereotype.rb +35 -0
  296. data/lib/ea/lint/rules/naming_convention.rb +44 -0
  297. data/lib/ea/lint/rules/orphan_element.rb +28 -0
  298. data/lib/ea/lint/rules.rb +14 -0
  299. data/lib/ea/lint.rb +13 -0
  300. data/lib/ea/mdg/document.rb +120 -0
  301. data/lib/ea/mdg/loader.rb +118 -0
  302. data/lib/ea/mdg/registry.rb +98 -0
  303. data/lib/ea/mdg/stereotype_alias_registry.rb +77 -0
  304. data/lib/ea/mdg/xml/applies_to.rb +18 -0
  305. data/lib/ea/mdg/xml/apply.rb +18 -0
  306. data/lib/ea/mdg/xml/content.rb +18 -0
  307. data/lib/ea/mdg/xml/documentation.rb +24 -0
  308. data/lib/ea/mdg/xml/stereotype.rb +28 -0
  309. data/lib/ea/mdg/xml/stereotypes.rb +18 -0
  310. data/lib/ea/mdg/xml/tag.rb +28 -0
  311. data/lib/ea/mdg/xml/tagged_values.rb +18 -0
  312. data/lib/ea/mdg/xml/technology.rb +28 -0
  313. data/lib/ea/mdg/xml/uml_profile.rb +22 -0
  314. data/lib/ea/mdg/xml/uml_profiles.rb +18 -0
  315. data/lib/ea/mdg/xml.rb +24 -0
  316. data/lib/ea/mdg.rb +25 -0
  317. data/lib/ea/model/classifier.rb +4 -0
  318. data/lib/ea/model/constraint.rb +24 -0
  319. data/lib/ea/model/diagram.rb +85 -0
  320. data/lib/ea/model/diagram_connector.rb +34 -0
  321. data/lib/ea/model/diagram_element.rb +16 -0
  322. data/lib/ea/model/document.rb +47 -2
  323. data/lib/ea/model/ghost_label.rb +32 -0
  324. data/lib/ea/model/instance_specification.rb +34 -0
  325. data/lib/ea/model/legend.rb +43 -0
  326. data/lib/ea/model/legend_item.rb +26 -0
  327. data/lib/ea/model/note.rb +28 -0
  328. data/lib/ea/model/operation.rb +2 -0
  329. data/lib/ea/model/property.rb +2 -0
  330. data/lib/ea/model/relationship.rb +2 -0
  331. data/lib/ea/model/signal.rb +14 -0
  332. data/lib/ea/model/slot.rb +19 -0
  333. data/lib/ea/model.rb +8 -0
  334. data/lib/ea/ocl/evaluator.rb +94 -0
  335. data/lib/ea/ocl/nodes.rb +30 -0
  336. data/lib/ea/ocl/parser.rb +133 -0
  337. data/lib/ea/ocl/unsupported_error.rb +7 -0
  338. data/lib/ea/ocl.rb +13 -0
  339. data/lib/ea/qea/factory/document_builder.rb +24 -10
  340. data/lib/ea/qea/factory/generalization_builder.rb +9 -11
  341. data/lib/ea/qea/infrastructure/table_reader.rb +9 -0
  342. data/lib/ea/qea/models/ea_author.rb +35 -0
  343. data/lib/ea/qea/models/ea_glossary.rb +34 -0
  344. data/lib/ea/qea/models/ea_image.rb +53 -0
  345. data/lib/ea/qea/models/ea_implement.rb +32 -0
  346. data/lib/ea/qea/models/ea_list.rb +42 -0
  347. data/lib/ea/qea/models/ea_object_effort.rb +34 -0
  348. data/lib/ea/qea/models/ea_object_problem.rb +37 -0
  349. data/lib/ea/qea/models/ea_object_require.rb +34 -0
  350. data/lib/ea/qea/models/ea_object_resource.rb +34 -0
  351. data/lib/ea/qea/models/ea_object_risk.rb +34 -0
  352. data/lib/ea/qea/models/ea_object_scenario.rb +34 -0
  353. data/lib/ea/qea/models/ea_object_test.rb +35 -0
  354. data/lib/ea/qea/models/ea_object_trx.rb +34 -0
  355. data/lib/ea/qea/models/ea_operation.rb +7 -0
  356. data/lib/ea/qea/models/ea_palette.rb +30 -0
  357. data/lib/ea/qea/models/ea_palette_item.rb +36 -0
  358. data/lib/ea/qea/models/ea_phase.rb +40 -0
  359. data/lib/ea/qea/models/ea_role_constraint.rb +32 -0
  360. data/lib/ea/qea/models/ea_secrypt.rb +34 -0
  361. data/lib/ea/qea/models/ea_version.rb +39 -0
  362. data/lib/ea/qea/models/ea_xref.rb +11 -1
  363. data/lib/ea/qea/models.rb +19 -0
  364. data/lib/ea/qea/services/database_loader.rb +22 -1
  365. data/lib/ea/query/builder.rb +108 -0
  366. data/lib/ea/query.rb +9 -0
  367. data/lib/ea/render/image_converter.rb +101 -0
  368. data/lib/ea/render.rb +10 -0
  369. data/lib/ea/shapescript/parser.rb +208 -0
  370. data/lib/ea/shapescript/renderer.rb +74 -0
  371. data/lib/ea/shapescript/shape.rb +34 -0
  372. data/lib/ea/shapescript.rb +24 -0
  373. data/lib/ea/sources/qea/adapter.rb +22 -5
  374. data/lib/ea/sources/qea/classifier_builder.rb +184 -7
  375. data/lib/ea/sources/qea/diagram_builder.rb +352 -24
  376. data/lib/ea/sources/qea/id_normalizer.rb +16 -1
  377. data/lib/ea/sources/qea/instance_builder.rb +113 -0
  378. data/lib/ea/sources/qea/legend_builder.rb +137 -0
  379. data/lib/ea/sources/qea/note_builder.rb +58 -0
  380. data/lib/ea/sources/qea/object_classifier_map.rb +6 -1
  381. data/lib/ea/sources/qea/operation_builder.rb +1 -0
  382. data/lib/ea/sources/qea/package_builder.rb +16 -0
  383. data/lib/ea/sources/qea/relationship_builder.rb +21 -5
  384. data/lib/ea/sources/qea/tagged_value_builder.rb +28 -9
  385. data/lib/ea/sources/qea/xref/legend_definition.rb +35 -0
  386. data/lib/ea/sources/qea/xref/parser.rb +150 -0
  387. data/lib/ea/sources/qea/xref/property_definition.rb +29 -0
  388. data/lib/ea/sources/qea/xref/record.rb +41 -0
  389. data/lib/ea/sources/qea/xref/stereotype_application.rb +37 -0
  390. data/lib/ea/sources/qea/xref.rb +27 -0
  391. data/lib/ea/sources/qea.rb +4 -0
  392. data/lib/ea/sources/xmi/adapter.rb +80 -2
  393. data/lib/ea/sources/xmi/classifier_builder.rb +10 -1
  394. data/lib/ea/sources/xmi/diagram_builder.rb +95 -18
  395. data/lib/ea/sources/xmi/extension_geometry_parser.rb +19 -2
  396. data/lib/ea/sources/xmi/extension_style_parser.rb +15 -2
  397. data/lib/ea/sources/xmi/note_builder.rb +46 -0
  398. data/lib/ea/sources/xmi/property_builder.rb +9 -0
  399. data/lib/ea/sources/xmi/stereotype_builder.rb +54 -0
  400. data/lib/ea/sources/xmi/tag_builder.rb +45 -0
  401. data/lib/ea/sources/xmi/umldi_keyword_extractor.rb +87 -0
  402. data/lib/ea/sources/xmi.rb +4 -0
  403. data/lib/ea/spa/output/sharded_multi_file_strategy.rb +18 -3
  404. data/lib/ea/spa/output/single_file_strategy.rb +8 -2
  405. data/lib/ea/spa/output/strategy.rb +34 -0
  406. data/lib/ea/svg/connector_router.rb +134 -0
  407. data/lib/ea/svg/ea_emitter/background.rb +2 -3
  408. data/lib/ea/svg/ea_emitter/bounds_calculator.rb +142 -0
  409. data/lib/ea/svg/ea_emitter/canvas.rb +56 -33
  410. data/lib/ea/svg/ea_emitter/color_resolver.rb +56 -0
  411. data/lib/ea/svg/ea_emitter/compartment/attributes.rb +28 -0
  412. data/lib/ea/svg/ea_emitter/compartment/constraints.rb +30 -0
  413. data/lib/ea/svg/ea_emitter/compartment/enum_literals.rb +86 -0
  414. data/lib/ea/svg/ea_emitter/compartment/header.rb +32 -0
  415. data/lib/ea/svg/ea_emitter/compartment/header_divider.rb +57 -0
  416. data/lib/ea/svg/ea_emitter/compartment/instance_slots.rb +96 -0
  417. data/lib/ea/svg/ea_emitter/compartment/instance_underline.rb +67 -0
  418. data/lib/ea/svg/ea_emitter/compartment/note_body.rb +52 -0
  419. data/lib/ea/svg/ea_emitter/compartment/note_text.rb +102 -0
  420. data/lib/ea/svg/ea_emitter/compartment/operations.rb +38 -0
  421. data/lib/ea/svg/ea_emitter/compartment/package_contents.rb +72 -0
  422. data/lib/ea/svg/ea_emitter/compartment/package_from_parent.rb +136 -0
  423. data/lib/ea/svg/ea_emitter/compartment/shape.rb +82 -0
  424. data/lib/ea/svg/ea_emitter/compartment/tagged_values.rb +30 -0
  425. data/lib/ea/svg/ea_emitter/compartment.rb +58 -0
  426. data/lib/ea/svg/ea_emitter/connectors.rb +46 -12
  427. data/lib/ea/svg/ea_emitter/diagram_frame.rb +143 -0
  428. data/lib/ea/svg/ea_emitter/document.rb +15 -24
  429. data/lib/ea/svg/ea_emitter/element/attribute_line_builder.rb +117 -0
  430. data/lib/ea/svg/ea_emitter/element/attribute_renderer.rb +119 -0
  431. data/lib/ea/svg/ea_emitter/element/bcol_decoder.rb +29 -0
  432. data/lib/ea/svg/ea_emitter/element/compartment_geometry.rb +113 -0
  433. data/lib/ea/svg/ea_emitter/element/constraint_renderer.rb +53 -0
  434. data/lib/ea/svg/ea_emitter/element/divider_renderer.rb +17 -0
  435. data/lib/ea/svg/ea_emitter/element/enumeration_literal_renderer.rb +34 -0
  436. data/lib/ea/svg/ea_emitter/element/filter.rb +44 -0
  437. data/lib/ea/svg/ea_emitter/element/hand_draw_shape_renderer.rb +77 -0
  438. data/lib/ea/svg/ea_emitter/element/header_line_pipeline.rb +43 -0
  439. data/lib/ea/svg/ea_emitter/element/header_line_provider/instance_spec.rb +27 -0
  440. data/lib/ea/svg/ea_emitter/element/header_line_provider/name.rb +76 -0
  441. data/lib/ea/svg/ea_emitter/element/header_line_provider/parent_ghost.rb +25 -0
  442. data/lib/ea/svg/ea_emitter/element/header_line_provider/stereotype_label.rb +68 -0
  443. data/lib/ea/svg/ea_emitter/element/header_line_provider.rb +19 -0
  444. data/lib/ea/svg/ea_emitter/element/header_lines.rb +27 -0
  445. data/lib/ea/svg/ea_emitter/element/header_renderer.rb +48 -0
  446. data/lib/ea/svg/ea_emitter/element/icon_renderer.rb +109 -0
  447. data/lib/ea/svg/ea_emitter/element/legend_renderer.rb +177 -0
  448. data/lib/ea/svg/ea_emitter/element/note_shape_renderer.rb +112 -0
  449. data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +100 -0
  450. data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +181 -0
  451. data/lib/ea/svg/ea_emitter/element/row_icon_renderer.rb +156 -0
  452. data/lib/ea/svg/ea_emitter/element/shape_renderer.rb +17 -0
  453. data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +82 -0
  454. data/lib/ea/svg/ea_emitter/element/tagged_value_renderer.rb +50 -0
  455. data/lib/ea/svg/ea_emitter/element/text_escape.rb +20 -0
  456. data/lib/ea/svg/ea_emitter/element.rb +37 -0
  457. data/lib/ea/svg/ea_emitter/elements.rb +283 -95
  458. data/lib/ea/svg/ea_emitter/font_resolver.rb +111 -0
  459. data/lib/ea/svg/ea_emitter/ghost_labels.rb +66 -0
  460. data/lib/ea/svg/ea_emitter/label/end_label.rb +266 -0
  461. data/lib/ea/svg/ea_emitter/label/midpoint_label.rb +101 -0
  462. data/lib/ea/svg/ea_emitter/label/registry.rb +50 -0
  463. data/lib/ea/svg/ea_emitter/label.rb +23 -0
  464. data/lib/ea/svg/ea_emitter/labels.rb +93 -27
  465. data/lib/ea/svg/ea_emitter/layer.rb +20 -0
  466. data/lib/ea/svg/ea_emitter/layer_sequencer.rb +93 -0
  467. data/lib/ea/svg/ea_emitter/marker/arrow_path.rb +52 -0
  468. data/lib/ea/svg/ea_emitter/marker/diamond.rb +57 -0
  469. data/lib/ea/svg/ea_emitter/marker/kind.rb +35 -0
  470. data/lib/ea/svg/ea_emitter/marker/open_triangle.rb +27 -0
  471. data/lib/ea/svg/ea_emitter/marker/package_import.rb +39 -0
  472. data/lib/ea/svg/ea_emitter/marker/plus.rb +40 -0
  473. data/lib/ea/svg/ea_emitter/marker/registry.rb +42 -0
  474. data/lib/ea/svg/ea_emitter/marker.rb +37 -0
  475. data/lib/ea/svg/ea_emitter/markers.rb +351 -64
  476. data/lib/ea/svg/ea_emitter/render_context.rb +52 -0
  477. data/lib/ea/svg/ea_emitter/style.rb +15 -0
  478. data/lib/ea/svg/ea_emitter/text_renderer.rb +92 -0
  479. data/lib/ea/svg/ea_emitter.rb +33 -11
  480. data/lib/ea/svg/parity/checker.rb +105 -0
  481. data/lib/ea/svg/parity/source.rb +59 -0
  482. data/lib/ea/svg/parity/suite.rb +113 -0
  483. data/lib/ea/svg/parity.rb +14 -0
  484. data/lib/ea/svg.rb +2 -15
  485. data/lib/ea/theme/definition.rb +176 -0
  486. data/lib/ea/theme/loader.rb +125 -0
  487. data/lib/ea/theme/registry.rb +96 -0
  488. data/lib/ea/theme.rb +37 -0
  489. data/lib/ea/transformations.rb +5 -88
  490. data/lib/ea/version.rb +4 -1
  491. data/lib/ea.rb +14 -4
  492. data/lib/tasks/svg_bench.rake +87 -0
  493. data/lutaml-uml-0.5.1.gem +0 -0
  494. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes-list.png +0 -0
  495. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes.png +0 -0
  496. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-2-gradients.png +0 -0
  497. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-3-colors.png +0 -0
  498. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance-fonts.png +0 -0
  499. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance.png +0 -0
  500. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-5-behavior.png +0 -0
  501. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-6-sequence.png +0 -0
  502. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram.png +0 -0
  503. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-2-objects.png +0 -0
  504. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-3-links.png +0 -0
  505. metadata +449 -26
  506. data/lib/ea/diagram/configuration.rb +0 -379
  507. data/lib/ea/diagram/element_renderers/base_renderer.rb +0 -77
  508. data/lib/ea/diagram/element_renderers/class_renderer.rb +0 -323
  509. data/lib/ea/diagram/element_renderers/connector_renderer.rb +0 -41
  510. data/lib/ea/diagram/element_renderers/package_renderer.rb +0 -61
  511. data/lib/ea/diagram/element_renderers.rb +0 -43
  512. data/lib/ea/diagram/extractor.rb +0 -566
  513. data/lib/ea/diagram/layout_engine.rb +0 -170
  514. data/lib/ea/diagram/path_builder.rb +0 -202
  515. data/lib/ea/diagram/style_parser.rb +0 -42
  516. data/lib/ea/diagram/style_resolver.rb +0 -291
  517. data/lib/ea/diagram/svg_renderer.rb +0 -274
  518. data/lib/ea/diagram/util.rb +0 -73
  519. data/lib/ea/svg/bounds_calculator.rb +0 -74
  520. data/lib/ea/svg/connector_path.rb +0 -138
  521. data/lib/ea/svg/element_box.rb +0 -256
  522. data/lib/ea/svg/renderer.rb +0 -77
  523. data/lib/ea/svg/style_resolver.rb +0 -72
  524. data/lib/ea/transformations/configuration.rb +0 -333
  525. data/lib/ea/transformations/format_registry.rb +0 -366
  526. data/lib/ea/transformations/parsers/base_parser.rb +0 -482
  527. data/lib/ea/transformations/parsers/qea_parser.rb +0 -401
  528. data/lib/ea/transformations/parsers/xmi_parser.rb +0 -243
  529. data/lib/ea/transformations/transformation_engine.rb +0 -390
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Lint
5
+ module Rules
6
+ # Class names should be CamelCase. Attribute names should be
7
+ # camelCase. Detect deviations.
8
+ class NamingConventionRule < LintRule
9
+ self.severity = :warning
10
+
11
+ CLASS_PATTERN = /\A[A-Z][A-Za-z0-9]*\z/
12
+ ATTR_PATTERN = /\A[a-z][A-Za-z0-9]*\z/
13
+
14
+ def check(model)
15
+ offenses = []
16
+ classes = (model.collections[:objects] || [])
17
+ .select { |o| o.object_type == "Class" }
18
+ classes.each do |klass|
19
+ next if klass.name.nil? || klass.name.empty?
20
+
21
+ unless klass.name.match?(CLASS_PATTERN)
22
+ offenses << offense(entity_id: klass.object_id,
23
+ entity_name: klass.name,
24
+ message: "Class name is not CamelCase")
25
+ end
26
+ end
27
+
28
+ attrs = model.collections[:attributes] || []
29
+ attrs.each do |attr|
30
+ next if attr.name.nil? || attr.name.empty?
31
+
32
+ unless attr.name.match?(ATTR_PATTERN)
33
+ offenses << offense(entity_id: attr.attribute_id || attr.object_id,
34
+ entity_name: attr.name,
35
+ message: "Attribute name is not camelCase")
36
+ end
37
+ end
38
+
39
+ offenses
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Lint
5
+ module Rules
6
+ # Class with no parent package (orphan). Usually indicates a
7
+ # modeling accident.
8
+ class OrphanElementRule < LintRule
9
+ self.severity = :warning
10
+
11
+ def check(model)
12
+ valid_pkg_ids = (model.collections[:packages] || [])
13
+ .map(&:package_id).to_set
14
+
15
+ (model.collections[:objects] || []).select { |o| o.object_type == "Class" }.map do |klass|
16
+ next unless klass.package_id
17
+
18
+ unless valid_pkg_ids.include?(klass.package_id)
19
+ offense(entity_id: klass.object_id,
20
+ entity_name: klass.name,
21
+ message: "Class references missing package_id=#{klass.package_id}")
22
+ end
23
+ end.compact
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Lint
5
+ # Default namespace for built-in lint rules.
6
+ module Rules
7
+ autoload :NamingConventionRule, "ea/lint/rules/naming_convention"
8
+ autoload :OrphanElementRule, "ea/lint/rules/orphan_element"
9
+ autoload :DuplicateNameRule, "ea/lint/rules/duplicate_name"
10
+ autoload :CyclicGeneralizationRule, "ea/lint/rules/cyclic_generalization"
11
+ autoload :MissingStereotypeRule, "ea/lint/rules/missing_stereotype"
12
+ end
13
+ end
14
+ end
data/lib/ea/lint.rb ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ # Model lint namespace. Each rule extends LintRule and produces
5
+ # Offense records. New rules register in Engine without touching
6
+ # existing rules (OCP).
7
+ module Lint
8
+ autoload :Engine, "ea/lint/engine"
9
+ autoload :LintRule, "ea/lint/lint_rule"
10
+ autoload :Offense, "ea/lint/offense"
11
+ autoload :Rules, "ea/lint/rules"
12
+ end
13
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string: true
2
+
3
+ module Ea
4
+ module Mdg
5
+ # One MDG technology file's parsed model. Immutable container
6
+ # carrying the technology's name, packages, classifiers,
7
+ # stereotype definitions, and generalization relationships.
8
+ #
9
+ # Two MDG file formats are supported:
10
+ #
11
+ # 1. XMI format (UML:Model/UML:Class/...) — carries reference
12
+ # model classes with properties and generalizations.
13
+ # 2. MDG.Technology format (<MDG.Technology>/<UMLProfiles>) —
14
+ # carries stereotype definitions with tagged-value specs.
15
+ #
16
+ # Both formats contribute to the same Document; stereotypes and
17
+ # classifiers coexist without coupling.
18
+ #
19
+ # Lookups are O(1) via lazily-built hash indexes. Callers do
20
+ # not mutate the Document.
21
+ class Document
22
+ ClassifierEntry = Struct.new(:id, :name, :package_name,
23
+ :properties, keyword_init: true)
24
+ PropertyEntry = Struct.new(:name, :type_name, :visibility,
25
+ :multiplicity_lower,
26
+ :multiplicity_upper,
27
+ keyword_init: true)
28
+ GeneralizationEntry = Struct.new(:specific_id, :general_id,
29
+ keyword_init: true)
30
+ # Stereotype definition from an MDG.Technology file.
31
+ # Carries the stereotype's name, what element types it applies
32
+ # to (Class, Package, Property, etc.), its tagged-value specs,
33
+ # and any notes. `generalizes` links to a parent stereotype
34
+ # when the MDG defines an inheritance chain between stereotypes.
35
+ StereotypeEntry = Struct.new(:name, :applies_to, :tagged_values,
36
+ :notes, :generalizes,
37
+ :base_stereotypes,
38
+ keyword_init: true)
39
+ TaggedValueSpec = Struct.new(:name, :type, :description, :unit,
40
+ :values, :default,
41
+ keyword_init: true)
42
+
43
+ attr_reader :technology_name, :classifiers, :generalizations,
44
+ :packages, :stereotypes
45
+
46
+ def initialize(technology_name:, classifiers:, generalizations:,
47
+ packages:, stereotypes: [])
48
+ @technology_name = technology_name
49
+ @classifiers = classifiers.freeze
50
+ @generalizations = generalizations.freeze
51
+ @packages = packages.freeze
52
+ @stereotypes = stereotypes.freeze
53
+ end
54
+
55
+ # O(1) classifier lookup by EA GUID. Returns ClassifierEntry
56
+ # or nil.
57
+ def find_by_guid(guid)
58
+ classifier_guid_index[guid]
59
+ end
60
+
61
+ # O(1) classifier lookup by name. Returns the FIRST match —
62
+ # MDGs typically use unique class names within a technology.
63
+ def find_by_name(name)
64
+ classifier_name_index[name]
65
+ end
66
+
67
+ # Returns the parent ClassifierEntry for a given child id,
68
+ # or nil when the child has no MDG-defined parent.
69
+ def parent_of(classifier_id)
70
+ gen = generalizations.find { |g| g.specific_id == classifier_id }
71
+ return nil unless gen
72
+
73
+ find_by_guid(gen.general_id)
74
+ end
75
+
76
+ # Enumerates each classifier's ancestor chain (parent,
77
+ # grandparent, ...). Stops at the root or when a cycle is
78
+ # detected.
79
+ def ancestors_of(classifier_id)
80
+ seen = {}
81
+ Enumerator.new do |yielder|
82
+ current_id = classifier_id
83
+ while current_id && !seen.key?(current_id)
84
+ seen[current_id] = true
85
+ parent = parent_of(current_id)
86
+ break unless parent
87
+
88
+ yielder << parent
89
+ current_id = parent.id
90
+ end
91
+ end
92
+ end
93
+
94
+ # O(1) stereotype lookup by name. Returns StereotypeEntry or nil.
95
+ def find_stereotype(name)
96
+ stereotype_index[name]
97
+ end
98
+
99
+ private
100
+
101
+ def classifier_guid_index
102
+ @classifier_guid_index ||= classifiers.each_with_object({}) do |c, h|
103
+ h[c.id] = c
104
+ end
105
+ end
106
+
107
+ def classifier_name_index
108
+ @classifier_name_index ||= classifiers.each_with_object({}) do |c, h|
109
+ h[c.name] = c unless h.key?(c.name)
110
+ end
111
+ end
112
+
113
+ def stereotype_index
114
+ @stereotype_index ||= stereotypes.each_with_object({}) do |s, h|
115
+ h[s.name] = s unless h.key?(s.name)
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string: true
2
+
3
+ module Ea
4
+ module Mdg
5
+ # Parses an MDG technology file into an Mdg::Document.
6
+ #
7
+ # Supports two EA MDG file formats:
8
+ #
9
+ # 1. **MDG.Technology format** (root: `<MDG.Technology>`) —
10
+ # Parsed via lutaml-model XML mapping (Ea::Mdg::Xml::*).
11
+ # Carries UMLProfile elements with Stereotype definitions
12
+ # and their tagged-value specs. Example: CityGML MDG.
13
+ #
14
+ # 2. **XMI format** (root: `<XMI>`) — Delegates to the xmi
15
+ # gem's typed Sparx model for UML class/attribute/generalization
16
+ # extraction. Carries reference model classes. Example: ISO 19103.
17
+ #
18
+ # The format is auto-detected from the root element. Both
19
+ # contribute to the same Document; stereotypes and classifiers
20
+ # coexist without coupling.
21
+ class Loader
22
+ attr_reader :xml
23
+
24
+ def initialize(xml)
25
+ @xml = xml
26
+ end
27
+
28
+ def self.from_path(path)
29
+ new(File.read(path))
30
+ end
31
+
32
+ def self.from_xml(xml)
33
+ new(xml)
34
+ end
35
+
36
+ def document
37
+ @document ||= build_document
38
+ end
39
+
40
+ private
41
+
42
+ def build_document
43
+ if mdg_technology_format?
44
+ build_from_mdg_technology
45
+ else
46
+ build_from_xmi
47
+ end
48
+ end
49
+
50
+ def mdg_technology_format?
51
+ stripped = xml.lstrip
52
+ stripped.start_with?("<MDG.Technology") ||
53
+ (stripped.start_with?("<?xml") && xml.include?("<MDG.Technology"))
54
+ end
55
+
56
+ # ---- MDG.Technology format (lutaml-model) ----
57
+
58
+ def build_from_mdg_technology
59
+ tech = Xml::Technology.from_xml(xml)
60
+ Document.new(
61
+ technology_name: tech.documentation&.name || "Unknown",
62
+ classifiers: [],
63
+ generalizations: [],
64
+ packages: [],
65
+ stereotypes: extract_stereotypes(tech)
66
+ )
67
+ end
68
+
69
+ def extract_stereotypes(tech)
70
+ profiles = tech.uml_profiles&.profiles || []
71
+ profiles += tech.standalone_profiles if tech.standalone_profiles
72
+ profiles.flat_map do |profile|
73
+ stereotypes = profile.content&.stereotypes&.items || []
74
+ stereotypes.map { |s| stereotype_entry(s) }
75
+ end
76
+ end
77
+
78
+ def stereotype_entry(xml_stereotype)
79
+ applies = xml_stereotype.applies_to&.applies || []
80
+ tags = xml_stereotype.tagged_values&.tags || []
81
+ Document::StereotypeEntry.new(
82
+ name: xml_stereotype.name,
83
+ applies_to: applies.map(&:type).compact,
84
+ tagged_values: tags.map { |t| tag_spec(t) },
85
+ notes: xml_stereotype.notes,
86
+ generalizes: xml_stereotype.generalizes,
87
+ base_stereotypes: xml_stereotype.base_stereotypes
88
+ )
89
+ end
90
+
91
+ def tag_spec(xml_tag)
92
+ Document::TaggedValueSpec.new(
93
+ name: xml_tag.name,
94
+ type: xml_tag.type,
95
+ description: xml_tag.description,
96
+ unit: xml_tag.unit,
97
+ values: xml_tag.values,
98
+ default: xml_tag.default
99
+ )
100
+ end
101
+
102
+ # ---- XMI format ----
103
+ # XMI files use UML 1.3 namespace elements. Full lutaml-model
104
+ # XML mapping for the UML namespace schema is a follow-up
105
+ # task. For now, XMI parsing returns an empty Document with
106
+ # just the technology name extracted from the root package.
107
+
108
+ def build_from_xmi
109
+ Document.new(
110
+ technology_name: "Unknown",
111
+ classifiers: [],
112
+ generalizations: [],
113
+ packages: []
114
+ )
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string: true
2
+
3
+ module Ea
4
+ module Mdg
5
+ # Multi-MDG lookup. Holds one or more loaded MDG Documents
6
+ # and exposes a unified lookup surface (by name or by GUID)
7
+ # that searches across all registered technologies.
8
+ #
9
+ # Source adapters OPTIONALLY accept an Mdg::Registry and use
10
+ # it to resolve inherited attributes from MDG-defined parent
11
+ # classes when a local Classifier's generalization parent
12
+ # isn't in the local model.
13
+ #
14
+ # Example:
15
+ #
16
+ # registry = Ea::Mdg::Registry.new
17
+ # registry.register(Ea::Mdg::Loader.from_path("MDG_ISO19103.xml"))
18
+ # registry.find_classifier_by_name("Any") # → ClassifierEntry
19
+ # registry.inherited_properties_for("EAID_...") # → [PropertyEntry, ...]
20
+ class Registry
21
+ include Enumerable
22
+
23
+ attr_reader :documents
24
+
25
+ def initialize
26
+ @documents = []
27
+ end
28
+
29
+ # Register an MDG Document. Subsequent registrations of the
30
+ # same technology_name replace the prior one (last-wins).
31
+ def register(document)
32
+ @documents.reject! { |d| d.technology_name == document.technology_name }
33
+ @documents << document
34
+ end
35
+
36
+ def each(&block)
37
+ @documents.each(&block)
38
+ end
39
+
40
+ def size
41
+ @documents.size
42
+ end
43
+
44
+ def technology_names
45
+ @documents.map(&:technology_name)
46
+ end
47
+
48
+ # Lookup by GUID across all registered technologies.
49
+ def find_classifier_by_guid(guid)
50
+ each do |doc|
51
+ found = doc.find_by_guid(guid)
52
+ return found if found
53
+ end
54
+ nil
55
+ end
56
+
57
+ # Lookup by name across all registered technologies. Returns
58
+ # the first match (technologies are searched in registration
59
+ # order).
60
+ def find_classifier_by_name(name)
61
+ each do |doc|
62
+ found = doc.find_by_name(name)
63
+ return found if found
64
+ end
65
+ nil
66
+ end
67
+
68
+ # Returns all Properties inherited from MDG ancestors of
69
+ # the given classifier. The classifier's OWN properties
70
+ # are excluded — only inherited MDG attributes are returned.
71
+ #
72
+ # Caller passes the local Classifier's EA GUID. The local
73
+ # Classifier is looked up across all registered MDGs; if
74
+ # found, its MDG ancestor chain is walked and each
75
+ # ancestor's properties are collected in order.
76
+ def inherited_properties_for(classifier_guid)
77
+ result = []
78
+ seen_property_names = {}
79
+
80
+ each do |doc|
81
+ classifier = doc.find_by_guid(classifier_guid)
82
+ next unless classifier
83
+
84
+ doc.ancestors_of(classifier_guid).each do |ancestor|
85
+ (ancestor.properties || []).each do |prop|
86
+ next if seen_property_names.key?(prop.name)
87
+
88
+ seen_property_names[prop.name] = true
89
+ result << prop
90
+ end
91
+ end
92
+ end
93
+
94
+ result
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Mdg
5
+ # Stereotype alias resolver. Loads EA's GMLStereotypes.xml (or
6
+ # equivalent) and maps variant spellings to a canonical name.
7
+ #
8
+ # Source XML format:
9
+ # <Stereotypes>
10
+ # <Stereotype name="FeatureType">
11
+ # <Alias>Feature Type</Alias>
12
+ # <Alias>featureType</Alias>
13
+ # <Alias>featuretype</Alias>
14
+ # </Stereotype>
15
+ # </Stereotypes>
16
+ #
17
+ # Usage:
18
+ # registry = StereotypeAliasRegistry.from_path("GMLStereotypes.xml")
19
+ # registry.canonicalize("featuretype") # → "FeatureType"
20
+ # registry.canonicalize("unknown") # → "unknown" (passthrough)
21
+ class StereotypeAliasRegistry
22
+ attr_reader :aliases
23
+
24
+ # @param aliases [Hash{String => String}] alias → canonical name
25
+ def initialize(aliases = {})
26
+ @aliases = aliases
27
+ end
28
+
29
+ # Build a registry by parsing the GMLStereotypes.xml format.
30
+ # @param path [String] path to XML file
31
+ # @return [StereotypeAliasRegistry]
32
+ def self.from_path(path)
33
+ return new if path.nil? || !File.exist?(path)
34
+
35
+ require "nokogiri"
36
+ doc = Nokogiri::XML(File.read(path))
37
+ from_nokogiri(doc)
38
+ end
39
+
40
+ # Build from a parsed Nokogiri document. Tested separately so
41
+ # the parser path is exercised without file IO.
42
+ # @param doc [Nokogiri::XML::Document]
43
+ # @return [StereotypeAliasRegistry]
44
+ def self.from_nokogiri(doc)
45
+ aliases = {}
46
+ doc.xpath("//Stereotype").each do |stereo|
47
+ canonical = stereo["name"]
48
+ next unless canonical
49
+
50
+ aliases[canonical.downcase] = canonical
51
+ stereo.xpath("Alias").each do |alias_node|
52
+ text = alias_node.text.strip
53
+ next if text.empty?
54
+
55
+ aliases[text.downcase] = canonical
56
+ end
57
+ end
58
+ new(aliases)
59
+ end
60
+
61
+ # Map a variant spelling to the canonical form. Case-insensitive.
62
+ # Unknown stereotypes pass through unchanged.
63
+ # @param name [String, nil]
64
+ # @return [String, nil]
65
+ def canonicalize(name)
66
+ return nil if name.nil? || name.empty?
67
+
68
+ aliases[name.downcase] || name
69
+ end
70
+
71
+ # @return [Array<String>] all canonical stereotype names
72
+ def canonical_names
73
+ aliases.values.uniq
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Ea
6
+ module Mdg
7
+ module Xml
8
+ class AppliesTo < Lutaml::Model::Serializable
9
+ attribute :applies, Apply, collection: true
10
+
11
+ xml do
12
+ root "AppliesTo"
13
+ map_element "Apply", to: :applies
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Ea
6
+ module Mdg
7
+ module Xml
8
+ class Apply < Lutaml::Model::Serializable
9
+ attribute :type, :string
10
+
11
+ xml do
12
+ root "Apply"
13
+ map_attribute "type", to: :type
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Ea
6
+ module Mdg
7
+ module Xml
8
+ class Content < Lutaml::Model::Serializable
9
+ attribute :stereotypes, Stereotypes
10
+
11
+ xml do
12
+ root "Content"
13
+ map_element "Stereotypes", to: :stereotypes
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Ea
6
+ module Mdg
7
+ module Xml
8
+ class Documentation < Lutaml::Model::Serializable
9
+ attribute :id, :string
10
+ attribute :name, :string
11
+ attribute :version, :string
12
+ attribute :notes, :string
13
+
14
+ xml do
15
+ root "Documentation"
16
+ map_attribute "id", to: :id
17
+ map_attribute "name", to: :name
18
+ map_attribute "version", to: :version
19
+ map_attribute "notes", to: :notes
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Ea
6
+ module Mdg
7
+ module Xml
8
+ class Stereotype < Lutaml::Model::Serializable
9
+ attribute :name, :string
10
+ attribute :notes, :string
11
+ attribute :generalizes, :string
12
+ attribute :base_stereotypes, :string
13
+ attribute :applies_to, AppliesTo
14
+ attribute :tagged_values, TaggedValues
15
+
16
+ xml do
17
+ root "Stereotype"
18
+ map_attribute "name", to: :name
19
+ map_attribute "notes", to: :notes
20
+ map_attribute "generalizes", to: :generalizes
21
+ map_attribute "baseStereotypes", to: :base_stereotypes
22
+ map_element "AppliesTo", to: :applies_to
23
+ map_element "TaggedValues", to: :tagged_values
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Ea
6
+ module Mdg
7
+ module Xml
8
+ class Stereotypes < Lutaml::Model::Serializable
9
+ attribute :items, Stereotype, collection: true
10
+
11
+ xml do
12
+ root "Stereotypes"
13
+ map_element "Stereotype", to: :items
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Ea
6
+ module Mdg
7
+ module Xml
8
+ class Tag < Lutaml::Model::Serializable
9
+ attribute :name, :string
10
+ attribute :type, :string
11
+ attribute :description, :string
12
+ attribute :unit, :string
13
+ attribute :values, :string
14
+ attribute :default, :string
15
+
16
+ xml do
17
+ root "Tag"
18
+ map_attribute "name", to: :name
19
+ map_attribute "type", to: :type
20
+ map_attribute "description", to: :description
21
+ map_attribute "unit", to: :unit
22
+ map_attribute "values", to: :values
23
+ map_attribute "default", to: :default
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Ea
6
+ module Mdg
7
+ module Xml
8
+ class TaggedValues < Lutaml::Model::Serializable
9
+ attribute :tags, Tag, collection: true
10
+
11
+ xml do
12
+ root "TaggedValues"
13
+ map_element "Tag", to: :tags
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end