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,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ module Xref
7
+ # Pure parser for EA's `t_xref.Description` mini-language.
8
+ # Stateless — every call returns a fresh `Xref::Record`.
9
+ #
10
+ # Supported formats:
11
+ # @STEREO;Name=X;FQName=Y;@ENDSTEREO;
12
+ # @PROP=@NAME=..@ENDNAME;@TYPE=..@ENDTYPE;@VALU=..@ENDVALU;@PRMT=..@ENDPRMT;@ENDPROP;
13
+ # <key>=<value>;<key>=<value>;
14
+ #
15
+ # Unknown formats fall through to key=value parsing.
16
+ module Parser
17
+ module_function
18
+
19
+ # Parse one Description string into a typed Record.
20
+ # @param description [String, nil]
21
+ # @return [Ea::Sources::Qea::Xref::Record]
22
+ def parse(description)
23
+ return Record.new if description.nil? || description.empty?
24
+
25
+ Record.new(
26
+ stereotype: parse_stereotype(description),
27
+ properties: parse_properties(description),
28
+ legends: parse_legends(description),
29
+ key_values: parse_key_values(description),
30
+ raw: description
31
+ )
32
+ end
33
+
34
+ # -- per-format extractors ----------------------------------
35
+
36
+ # Matches both legacy `@STEREO;Name=X;GUID={..};` and the
37
+ # plateau form `@STEREO;Name=X;FQName=Y;@ENDSTEREO;`.
38
+ STEREO_REGEX = /
39
+ @STEREO;
40
+ (.*?)
41
+ (?:@ENDSTEREO;|\z)
42
+ /mx.freeze
43
+
44
+ def parse_stereotype(description)
45
+ match = description.match(STEREO_REGEX)
46
+ return nil unless match
47
+
48
+ fields = parse_semicolon_fields(match[1])
49
+ StereotypeApplication.new(
50
+ name: fields["Name"],
51
+ fqname: fields["FQName"]
52
+ )
53
+ end
54
+
55
+ # One Description can carry multiple @PROP=...@ENDPROP; blocks.
56
+ # Split, then classify each as PropertyDefinition or LegendDefinition.
57
+ PROP_REGEX = /
58
+ @PROP=
59
+ (.*?)
60
+ @ENDPROP;
61
+ /mx.freeze
62
+
63
+ def parse_properties(description)
64
+ blocks = description.scan(PROP_REGEX).flatten
65
+ blocks.each_with_object([]) do |block, acc|
66
+ record = build_prop_record(block)
67
+ acc << record if record
68
+ end
69
+ end
70
+
71
+ def parse_legends(description)
72
+ parse_properties(description).select do |prop|
73
+ prop.is_a?(LegendDefinition)
74
+ end
75
+ end
76
+
77
+ def build_prop_record(block)
78
+ name = extract_tagged_value(block, "NAME")
79
+ type = extract_tagged_value(block, "TYPE")
80
+ value = extract_tagged_value(block, "VALU")
81
+ parameter = extract_tagged_value(block, "PRMT")
82
+
83
+ if type&.start_with?("LEGEND_")
84
+ LegendDefinition.new(
85
+ name: name,
86
+ legend_type: type,
87
+ colors: parse_color_value(value),
88
+ parameter: parameter
89
+ )
90
+ else
91
+ PropertyDefinition.new(
92
+ name: name,
93
+ type: type,
94
+ value: value,
95
+ parameter: parameter
96
+ )
97
+ end
98
+ end
99
+
100
+ # Extracts the content between @TAG=...@ENDTAG; markers.
101
+ # Returns nil when the tag is absent.
102
+ def extract_tagged_value(block, tag)
103
+ pattern = /@#{tag}=(.*?)@END#{tag};/m.freeze
104
+ match = block.match(pattern)
105
+ match ? match[1] : nil
106
+ end
107
+
108
+ # EA encodes legend VALU as `#key#=value;` pairs.
109
+ # Returns a hash keyed by underscored symbol.
110
+ def parse_color_value(value)
111
+ return {} if value.nil? || value.empty?
112
+
113
+ value.scan(/#([^#=]+)#=([^;]*);/).each_with_object({}) do |(k, v), acc|
114
+ acc[k.downcase.to_sym] = v
115
+ end
116
+ end
117
+
118
+ # Legacy `Key=Value;Key=Value;` parsing. Skips content that
119
+ # looks like @STEREO or @PROP blocks to avoid noise.
120
+ def parse_key_values(description)
121
+ stripped = description
122
+ .gsub(/@STEREO;.*?(?:@ENDSTEREO;|\z)/m, "")
123
+ .gsub(/@PROP=.*?@ENDPROP;/m, "")
124
+ pairs = stripped.split(";").map { |s| s.split("=", 2) }
125
+ pairs.each_with_object({}) do |(k, v), acc|
126
+ next if k.nil? || k.empty? || v.nil?
127
+
128
+ acc[k.downcase.to_sym] = v
129
+ end
130
+ end
131
+
132
+ # Parse `Key=Value;Key=value;` into a string-keyed hash.
133
+ # Used internally for the @STEREO body fields.
134
+ def parse_semicolon_fields(body)
135
+ body.split(";").each_with_object({}) do |part, acc|
136
+ key, value = part.split("=", 2)
137
+ next if key.nil? || key.empty? || value.nil?
138
+
139
+ # Strip @END* terminators that may be attached when the
140
+ # body wasn't cleanly split (defensive — usually already
141
+ # consumed by STEREO_REGEX).
142
+ cleaned = value.sub(/@END\w+\z/, "")
143
+ acc[key] = cleaned
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ module Xref
7
+ # Tagged-value / property definition record. Produced when the
8
+ # Description contains one or more `@PROP=@NAME=..@ENDNAME;`
9
+ # blocks for non-legend properties.
10
+ #
11
+ # Example source:
12
+ # @PROP=@NAME=isActive@ENDNAME;@TYPE=Boolean@ENDTYPE;
13
+ # @VALU=@ENDVALU;@PRMT=@ENDPRMT;@ENDPROP;
14
+ PropertyDefinition = Struct.new(:name, :type, :value, :parameter,
15
+ keyword_init: true) do
16
+ # @return [Boolean] true when the value field is non-empty
17
+ def default_value?
18
+ !value.nil? && !value.empty?
19
+ end
20
+
21
+ # @return [Boolean] true when the property is declared as Boolean
22
+ def boolean?
23
+ type == "Boolean"
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ module Xref
7
+ # Parsed t_xref.Description. Holds typed records for each block
8
+ # found in the source string. One Description can carry multiple
9
+ # records (e.g., several `@PROP=` blocks defining a stereotype's
10
+ # tagged-value set).
11
+ #
12
+ # Consumers branch on record type without re-parsing strings.
13
+ Record = Struct.new(:stereotype, :properties, :legends,
14
+ :key_values, :raw, keyword_init: true) do
15
+ def initialize(*)
16
+ super
17
+ self.properties ||= []
18
+ self.legends ||= []
19
+ self.key_values ||= {}
20
+ end
21
+
22
+ # @return [Boolean] true when a stereotype application is present
23
+ def stereotype?
24
+ !stereotype.nil?
25
+ end
26
+
27
+ # @return [Boolean] true when at least one legend is present
28
+ def legend?
29
+ !legends.empty?
30
+ end
31
+
32
+ # Convenience: first legend's name (the legend's title).
33
+ # @return [String, nil]
34
+ def legend_title
35
+ legends.first&.name
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ module Xref
7
+ # Stereotype application record. Produced when the Description
8
+ # starts with `@STEREO;`. Captures the short name and the
9
+ # fully-qualified `tech::Stereo` form when present.
10
+ #
11
+ # Example source:
12
+ # @STEREO;Name=FeatureType;FQName=GML::FeatureType;@ENDSTEREO;
13
+ StereotypeApplication = Struct.new(:name, :fqname, keyword_init: true) do
14
+ # Returns the technology prefix from FQName, or nil.
15
+ # @return [String, nil]
16
+ def technology
17
+ return nil unless fqname
18
+
19
+ idx = fqname.index("::")
20
+ idx ? fqname[0, idx] : nil
21
+ end
22
+
23
+ # Returns the stereotype name without technology prefix.
24
+ # Falls back to +name+ when FQName is absent.
25
+ # @return [String, nil]
26
+ def unqualified_name
27
+ if fqname && fqname.include?("::")
28
+ fqname.split("::", 2).last
29
+ else
30
+ name
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ # Namespace for parsing EA's `t_xref.Description` mini-language.
7
+ #
8
+ # EA encodes semi-structured metadata in a custom format combining:
9
+ #
10
+ # - Stereotype application: `@STEREO;Name=X;FQName=Y;@ENDSTEREO;`
11
+ # - Property definitions: `@PROP=@NAME=..@ENDNAME;@TYPE=..@ENDTYPE;...`
12
+ # - Legend definitions: `@PROP=@NAME=..;@TYPE=LEGEND_OBJECTSTYLE;...`
13
+ # - Legacy key=value: `aggregation=composite;direction=source;`
14
+ #
15
+ # The parser returns a typed `Xref::Record` value object so consumers
16
+ # can branch on record type without re-parsing strings (OCP/MECE).
17
+ module Xref
18
+ autoload :Parser, "ea/sources/qea/xref/parser"
19
+ autoload :Record, "ea/sources/qea/xref/record"
20
+ autoload :StereotypeApplication,
21
+ "ea/sources/qea/xref/stereotype_application"
22
+ autoload :PropertyDefinition, "ea/sources/qea/xref/property_definition"
23
+ autoload :LegendDefinition, "ea/sources/qea/xref/legend_definition"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -22,9 +22,13 @@ module Ea
22
22
  autoload :RelationshipBuilder, "ea/sources/qea/relationship_builder"
23
23
  autoload :StereotypeBuilder, "ea/sources/qea/stereotype_builder"
24
24
  autoload :TaggedValueBuilder, "ea/sources/qea/tagged_value_builder"
25
+ autoload :NoteBuilder, "ea/sources/qea/note_builder"
25
26
  autoload :AnnotationBuilder, "ea/sources/qea/annotation_builder"
27
+ autoload :InstanceBuilder, "ea/sources/qea/instance_builder"
28
+ autoload :LegendBuilder, "ea/sources/qea/legend_builder"
26
29
  autoload :DiagramBuilder, "ea/sources/qea/diagram_builder"
27
30
  autoload :DiagramStyleParser, "ea/sources/qea/diagram_style_parser"
31
+ autoload :Xref, "ea/sources/qea/xref"
28
32
  autoload :Adapter, "ea/sources/qea/adapter"
29
33
  end
30
34
  end
@@ -25,18 +25,92 @@ module Ea
25
25
  end
26
26
 
27
27
  def to_document
28
- Ea::Model::Document.new(
28
+ doc = Ea::Model::Document.new(
29
29
  metadata: metadata,
30
30
  packages: packages,
31
31
  classifiers: classifiers,
32
32
  relationships: relationships,
33
33
  stereotypes: [],
34
+ notes: notes,
34
35
  diagrams: diagrams
35
36
  )
37
+ apply_tagged_values(doc)
38
+ apply_stereotypes(doc)
39
+ resolve_type_names(doc)
40
+ doc
36
41
  end
37
42
 
38
43
  private
39
44
 
45
+ # Attach stereotypes parsed from <xmi:Extension>/<elements>
46
+ # to their owning classifiers and packages.
47
+ def apply_stereotypes(doc)
48
+ grouped = StereotypeBuilder.new(root).grouped_by_element
49
+ return if grouped.empty?
50
+
51
+ doc.classifiers.each do |c|
52
+ next unless c.id
53
+
54
+ refs = grouped[c.id]
55
+ next unless refs
56
+
57
+ c.stereotype_refs = refs
58
+ end
59
+ doc.packages.each do |p|
60
+ next unless p.id
61
+
62
+ refs = grouped[p.id] || grouped["EAID_#{p.id[5..]}"]
63
+ next unless refs
64
+
65
+ p.stereotype_refs = refs
66
+ end
67
+ end
68
+
69
+ # Attach tagged values parsed from the <xmi:Extension><tags>
70
+ # block to their owning classifiers and packages.
71
+ def apply_tagged_values(doc)
72
+ grouped = TagBuilder.new(xmi_path).grouped_by_model_element
73
+ return if grouped.empty?
74
+
75
+ doc.classifiers.each do |c|
76
+ next unless c.id
77
+
78
+ tags = grouped[c.id]
79
+ next unless tags
80
+
81
+ c.tagged_values = tags
82
+ end
83
+ doc.packages.each do |p|
84
+ next unless p.id
85
+
86
+ tags = grouped[p.id] || grouped["EAID_#{p.id[5..]}"]
87
+ next unless tags
88
+
89
+ p.tagged_values = tags
90
+ end
91
+ end
92
+
93
+ def resolve_type_names(doc)
94
+ # In the XMI, the <type xmi:idref=...> is the type
95
+ # classifier's GUID. The property's type_name was
96
+ # pre-populated by PropertyBuilder with the same idref;
97
+ # we replace it with the in-model qualified name when
98
+ # the referenced classifier is loaded.
99
+ qualified_by_id = doc.classifiers.each_with_object({}) do |c, acc|
100
+ acc[c.id] = c.qualified_name if c.id && c.qualified_name
101
+ end
102
+
103
+ doc.classifiers.each do |classifier|
104
+ (classifier.properties || []).each do |prop|
105
+ ref = prop.type_ref
106
+ next if ref.nil? || ref.empty?
107
+
108
+ resolved = qualified_by_id[ref]
109
+ prop.type_name = resolved if resolved
110
+ end
111
+ end
112
+ end
113
+
40
114
  def metadata
41
115
  @metadata ||= MetadataBuilder.new(root, xmi_path).build
42
116
  end
@@ -54,7 +128,11 @@ module Ea
54
128
  end
55
129
 
56
130
  def diagrams
57
- @diagrams ||= DiagramBuilder.new(root).build_all
131
+ @diagrams ||= DiagramBuilder.new(root, xmi_path).build_all
132
+ end
133
+
134
+ def notes
135
+ @notes ||= NoteBuilder.new(root).build_all
58
136
  end
59
137
  end
60
138
  end
@@ -73,8 +73,17 @@ module Ea
73
73
  end
74
74
 
75
75
  def qualified_name(element, containing_package)
76
+ # The XMI's `name` attribute may already be in
77
+ # "ns:Class" form (e.g. "xs:string" lives in the `xs`
78
+ # package). Forcing the package name in front would
79
+ # produce "xs::xs:string". Strip the existing namespace
80
+ # prefix from the simple name before composing the
81
+ # qualified name.
82
+ raw = element.name.to_s
83
+ simple = raw.split(":").last
84
+ simple = raw if simple.nil? || simple.empty?
76
85
  pkg_name = containing_package&.name
77
- pkg_name ? "#{pkg_name}::#{element.name}" : element.name
86
+ pkg_name ? "#{pkg_name}::#{simple}" : simple
78
87
  end
79
88
 
80
89
  def boolean(value)
@@ -9,10 +9,11 @@ module Ea
9
9
  # SOID/EOID, EDGE, label boxes) when present. Falls back to
10
10
  # the UML-DI owned_element bounds otherwise.
11
11
  class DiagramBuilder
12
- attr_reader :root
12
+ attr_reader :root, :xmi_path
13
13
 
14
- def initialize(root)
14
+ def initialize(root, xmi_path = nil)
15
15
  @root = root
16
+ @xmi_path = xmi_path
16
17
  end
17
18
 
18
19
  def build_all
@@ -42,6 +43,7 @@ module Ea
42
43
  placed_elements = ext_elements.placed_elements
43
44
  placed_connectors = ext_elements.placed_connectors
44
45
  props = ext_diagram.properties
46
+ umldi_keywords = keywords_for(id)
45
47
 
46
48
  Ea::Model::Diagram.new(
47
49
  id: id,
@@ -49,13 +51,39 @@ module Ea
49
51
  package_id: ext_diagram.model&.package,
50
52
  diagram_type: props&.type&.split(":")&.last&.downcase,
51
53
  bounds: canvas_bounds(placed_elements),
52
- elements: build_elements(placed_elements, id),
54
+ style: style_for(ext_diagram),
55
+ style_ex: style_ex_for(ext_diagram),
56
+ elements: build_elements(placed_elements, id, umldi_keywords),
53
57
  connectors: build_connectors(placed_connectors,
54
58
  placed_elements, id),
55
59
  annotations: []
56
60
  )
57
61
  end
58
62
 
63
+ # EA's XMI stores Style (a.k.a. Style1) in `style1.value` —
64
+ # the per-diagram feature visibility flags (HideAtts, HideOps,
65
+ # HideStereo, etc.). Parsed by DisplayConfig alongside
66
+ # StyleEx.
67
+ def style_for(ext_diagram)
68
+ style1 = ext_diagram.style1
69
+ return nil unless style1
70
+
71
+ style1.value
72
+ end
73
+
74
+ # EA's XMI stores StyleEx in the diagram's `style2.value`
75
+ # attribute (alongside Style1 in `style1.value`). The value
76
+ # is a semicolon-separated key=value string carrying the
77
+ # Theme=:NNN identifier and behavioral flags (SuppressFOC,
78
+ # AttPkg, ShowNotes, etc.) — the same shape our Diagram
79
+ # model expects in `style_ex`.
80
+ def style_ex_for(ext_diagram)
81
+ style2 = ext_diagram.style2
82
+ return nil unless style2
83
+
84
+ style2.value
85
+ end
86
+
59
87
  private
60
88
 
61
89
  def canvas_bounds(placed_elements)
@@ -72,11 +100,11 @@ module Ea
72
100
  height: bottom - top)
73
101
  end
74
102
 
75
- def build_elements(placed, owner_id)
76
- placed.map { |p| build_element(p, owner_id) }
103
+ def build_elements(placed, owner_id, umldi_keywords = {})
104
+ placed.map { |p| build_element(p, owner_id, umldi_keywords) }
77
105
  end
78
106
 
79
- def build_element(placed, owner_id)
107
+ def build_element(placed, owner_id, umldi_keywords = {})
80
108
  geom = placed.geometry
81
109
  style = placed.style
82
110
  Ea::Model::DiagramElement.new(
@@ -93,8 +121,11 @@ module Ea
93
121
  font_bold: style.bold,
94
122
  font_italic: style.italic,
95
123
  font_underline: style.underline,
124
+ show_tagged_values: style.show_tagged_values,
125
+ icon_visibility: style.hide_icon&.to_s,
96
126
  z_order: placed.seqno,
97
127
  duid: style.duid,
128
+ umldi_keyword: umldi_keywords[placed.subject],
98
129
  style: {}
99
130
  )
100
131
  end
@@ -122,6 +153,7 @@ module Ea
122
153
  source = by_duid[style.soid]
123
154
  target = by_duid[style.eoid]
124
155
  waypoints = compute_waypoints(geom, source, target)
156
+ ext_meta = connector_meta(placed.subject)
125
157
  Ea::Model::DiagramConnector.new(
126
158
  id: IdNormalizer.synthetic_id(owner_id, "conn", placed.subject),
127
159
  diagram_id: owner_id,
@@ -132,6 +164,8 @@ module Ea
132
164
  target_element_ref: target && synthetic_element_id(owner_id, target),
133
165
  source_edge: geom.edge,
134
166
  target_edge: geom.edge,
167
+ connector_type: ext_meta[:ea_type],
168
+ direction: ext_meta[:direction],
135
169
  waypoints: waypoints,
136
170
  style: {},
137
171
  line_color: style.color,
@@ -141,28 +175,71 @@ module Ea
141
175
  )
142
176
  end
143
177
 
178
+ def connector_meta(subject_id)
179
+ connector = connector_index[subject_id]
180
+ return { ea_type: nil, direction: nil } unless connector
181
+
182
+ props = connector.properties
183
+ {
184
+ ea_type: props&.ea_type,
185
+ direction: props&.direction
186
+ }
187
+ end
188
+
189
+ def connector_index
190
+ @connector_index ||= begin
191
+ conns = root.extension&.connectors&.connector || []
192
+ conns.each_with_object({}) do |c, acc|
193
+ acc[c.idref] = c if c.idref
194
+ end
195
+ end
196
+ end
197
+
198
+ def umldi_extractor
199
+ return nil unless xmi_path
200
+
201
+ @umldi_extractor ||= UmldiKeywordExtractor.new(xmi_path)
202
+ end
203
+
204
+ def keywords_for(diagram_id)
205
+ return {} unless umldi_extractor
206
+
207
+ umldi_extractor.keywords_for_diagram(diagram_id)
208
+ end
209
+
144
210
  def synthetic_element_id(owner_id, placed)
145
211
  IdNormalizer.synthetic_id(owner_id, "elem", placed.subject)
146
212
  end
147
213
 
148
214
  def compute_waypoints(geom, source, target)
149
- src_pt = edge_point(source, geom.edge, :source)
150
- tgt_pt = edge_point(target, geom.edge, :target)
151
- return [] unless src_pt && tgt_pt
215
+ src_bounds = bounds_from_placed(source)
216
+ tgt_bounds = bounds_from_placed(target)
217
+ return [] unless src_bounds && tgt_bounds
152
218
 
153
- pts = [src_pt]
154
- if geom.sx && geom.sy && (geom.sx.nonzero? || geom.sy.nonzero?)
155
- pts << [src_pt[0] + geom.sx, src_pt[1] + geom.sy]
156
- end
157
- if geom.ex && geom.ey && (geom.ex.nonzero? || geom.ey.nonzero?)
158
- pts << [tgt_pt[0] - geom.ex, tgt_pt[1] - geom.ey]
159
- end
160
- pts << tgt_pt
161
- pts.map do |x, y|
219
+ router = Ea::Svg::ConnectorRouter.new(
220
+ source_bounds: src_bounds,
221
+ target_bounds: tgt_bounds,
222
+ edge_code: geom.edge,
223
+ source_delta: [geom.sx, geom.sy],
224
+ target_delta: [geom.ex, geom.ey],
225
+ bend_path: geom.bend_points || []
226
+ )
227
+ router.waypoints.map do |x, y|
162
228
  Ea::Model::Waypoint.new(position: Ea::Model::Point.new(x: x, y: y))
163
229
  end
164
230
  end
165
231
 
232
+ def bounds_from_placed(placed)
233
+ return nil unless placed
234
+
235
+ g = placed.geometry
236
+ return nil unless g.left && g.top && g.right && g.bottom
237
+
238
+ Ea::Model::Bounds.new(x: g.left, y: g.top,
239
+ width: g.right - g.left,
240
+ height: g.bottom - g.top)
241
+ end
242
+
166
243
  def edge_point(placed, edge_code, _end_kind)
167
244
  return nil unless placed
168
245
 
@@ -32,10 +32,27 @@ module Ea
32
32
  ey: int(kv["EY"]),
33
33
  edge: int(kv["EDGE"]),
34
34
  label_boxes: extract_label_boxes(geometry_string),
35
- path: kv["Path"]
35
+ path: kv["Path"],
36
+ bend_points: parse_bend_path(kv["Path"])
36
37
  )
37
38
  end
38
39
 
40
+ # EA's Path= field uses the format `x1:y1$x2:y2$...` where
41
+ # each pair is a bend point. Coordinates are absolute pixel
42
+ # positions on the diagram canvas.
43
+ def parse_bend_path(raw)
44
+ return [] if raw.nil? || raw.empty?
45
+
46
+ raw.split("$").filter_map do |pair|
47
+ next nil unless pair.include?(":")
48
+
49
+ x_str, y_str = pair.split(":", 2)
50
+ x = int(x_str)
51
+ y = int(y_str)
52
+ [x, y] if x && y
53
+ end
54
+ end
55
+
39
56
  def split_pairs(str)
40
57
  str.split(";").filter_map do |pair|
41
58
  next nil unless pair.include?("=")
@@ -91,7 +108,7 @@ module Ea
91
108
  :left, :top, :right, :bottom,
92
109
  :img_left, :img_top, :img_right, :img_bottom,
93
110
  :sx, :sy, :ex, :ey, :edge,
94
- :label_boxes, :path,
111
+ :label_boxes, :path, :bend_points,
95
112
  keyword_init: true
96
113
  )
97
114