ea 0.4.0 → 0.5.1

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 (548) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +128 -1
  3. data/Rakefile +3 -0
  4. data/TODO.complete/01-t-xref-parsing.md +91 -0
  5. data/TODO.complete/02-per-label-geometry.md +50 -0
  6. data/TODO.complete/03-object-properties-constraints.md +57 -0
  7. data/TODO.complete/04-operation-params.md +37 -0
  8. data/TODO.complete/05-pdata-flag-matrix.md +52 -0
  9. data/TODO.complete/06-emf-image-rendering.md +34 -0
  10. data/TODO.complete/07-auxiliary-tables.md +47 -0
  11. data/TODO.complete/10-mdg-stereotype-registry.md +40 -0
  12. data/TODO.complete/11-gml-xsd-generation.md +42 -0
  13. data/TODO.complete/13-shapescript-interpreter.md +28 -0
  14. data/TODO.complete/20-cli-diff.md +46 -0
  15. data/TODO.complete/21-cli-render.md +26 -0
  16. data/TODO.complete/22-cli-export.md +29 -0
  17. data/TODO.complete/23-cli-mdg.md +23 -0
  18. data/TODO.complete/30-headerlines-ocp-refactor.md +72 -0
  19. data/TODO.complete/31-public-send-cleanup.md +61 -0
  20. data/TODO.complete/32-spec-coverage.md +33 -0
  21. data/TODO.complete/33-blocked-parity-gaps.md +41 -0
  22. data/TODO.complete/36-stereotype-icon-wiring.md +32 -0
  23. data/TODO.complete/37-cli-lint.md +32 -0
  24. data/TODO.complete/38-cli-query.md +26 -0
  25. data/TODO.complete/39-cli-info.md +23 -0
  26. data/TODO.complete/40-ocl-evaluator.md +31 -0
  27. data/TODO.complete/41-round-trip-spec.md +18 -0
  28. data/TODO.complete/42-html-diff-report.md +18 -0
  29. data/TODO.complete/43-json-schema-export.md +20 -0
  30. data/TODO.complete/44-remaining-qea-tables.md +40 -0
  31. data/TODO.complete/45-shapescript-extensions.md +25 -0
  32. data/TODO.complete/46-xsd-consolidation.md +20 -0
  33. data/TODO.complete/47-headerlines-legacy-removal.md +20 -0
  34. data/TODO.complete/51-remove-respond-to.md +25 -0
  35. data/TODO.complete/52-xsd-decouple-fixtures.md +23 -0
  36. data/TODO.complete/53-xmi-public-send-cleanup.md +26 -0
  37. data/TODO.complete/54-wire-stereotype-icon-renderer.md +20 -0
  38. data/TODO.complete/55-wire-shapescript.md +20 -0
  39. data/TODO.complete/56-wire-emf-renderer.md +27 -0
  40. data/TODO.complete/57-wire-ocl-evaluator.md +23 -0
  41. data/TODO.complete/58-diff-modifications.md +21 -0
  42. data/TODO.complete/59-delete-toy-xmi-export.md +30 -0
  43. data/TODO.complete/60-curate-json-export.md +20 -0
  44. data/TODO.complete/61-plantuml-relationships.md +22 -0
  45. data/TODO.complete/62-changelog.md +17 -0
  46. data/TODO.complete/63-dependabot-fix.md +18 -0
  47. data/TODO.complete/STATUS.md +85 -0
  48. data/TODO.diagrams/01-svg-measurement-harness.md +31 -0
  49. data/TODO.diagrams/02-polygon-over-rendering.md +29 -0
  50. data/TODO.diagrams/03-text-over-rendering.md +30 -0
  51. data/TODO.diagrams/04-rect-under-rendering.md +33 -0
  52. data/TODO.diagrams/05-canvas-dimension-parity.md +51 -0
  53. data/TODO.diagrams/06-group-layering-parity.md +39 -0
  54. data/TODO.diagrams/07-text-content-fidelity.md +65 -0
  55. data/TODO.diagrams/08-qea-direct-pipeline.md +46 -0
  56. data/TODO.diagrams/12-connector-labels.md +40 -0
  57. data/TODO.diagrams/12-per-entity-svg-layer-ordering.md +24 -0
  58. data/TODO.diagrams/13-marker-polygon-vs-path-dispatch.md +20 -0
  59. data/TODO.diagrams/13-per-text-type-font-sizes.md +33 -0
  60. data/TODO.diagrams/14-center-header-text.md +33 -0
  61. data/TODO.diagrams/14-filter-diagram-frame-elements.md +18 -0
  62. data/TODO.diagrams/15-deep-dead-code-audit.md +47 -0
  63. data/TODO.diagrams/15-per-element-font-from-style.md +24 -0
  64. data/TODO.diagrams/16-canvas-marker-extent.md +30 -0
  65. data/TODO.diagrams/16-document-remaining-parity-gaps.md +91 -0
  66. data/TODO.diagrams/17-italic-abstract-classes.md +28 -0
  67. data/TODO.diagrams/17-remaining-parity-gaps.md +90 -0
  68. data/TODO.diagrams/18-canvas-from-image-bounds.md +20 -0
  69. data/TODO.diagrams/18-property-lookup-index.md +42 -0
  70. data/TODO.diagrams/19-connector-sx-sy-routing.md +27 -0
  71. data/TODO.diagrams/19-replace-respond-to-with-is-a.md +31 -0
  72. data/TODO.diagrams/20-split-labels-emitter.md +56 -0
  73. data/TODO.diagrams/20-visibility-glyph-font-encoding.md +27 -0
  74. data/TODO.diagrams/21-aggregation-navigability-arrow.md +21 -0
  75. data/TODO.diagrams/21-auto-generated-legend-block.md +65 -0
  76. data/TODO.diagrams/22-ghost-element-discriminator.md +57 -0
  77. data/TODO.diagrams/22-style-based-path-grouping.md +28 -0
  78. data/TODO.diagrams/23-marker-polygon-deduplication.md +47 -0
  79. data/TODO.diagrams/23-multiplicity-label-positioning.md +29 -0
  80. data/TODO.diagrams/24-marker-anchor-structural.md +35 -0
  81. data/TODO.diagrams/24-track-marker-anchors-structurally.md +66 -0
  82. data/TODO.diagrams/24-truncate-properties-to-fit-box.md +24 -0
  83. data/TODO.diagrams/26-phantom-general-parent-rendering.md +59 -0
  84. data/TODO.diagrams/27-path-grouping-mode-detection.md +43 -0
  85. data/TODO.diagrams/28-architecture-cleanup-batch.md +47 -0
  86. data/TODO.diagrams/30-decompose-elements-groups-for.md +52 -0
  87. data/TODO.diagrams/32-examples-coverage-batch.md +78 -0
  88. data/TODO.diagrams/36-instance-specification-model.md +42 -0
  89. data/TODO.diagrams/37-element-icon-decorations.md +41 -0
  90. data/TODO.diagrams/38-html-note-and-receptions.md +32 -0
  91. data/TODO.diagrams/39-property-stereotype-discriminator.md +40 -0
  92. data/TODO.diagrams/40-standalone-1-discriminator.md +38 -0
  93. data/TODO.diagrams/41-xmi-to-svg-pipeline.md +113 -0
  94. data/TODO.diagrams/42-close-all-parity-deltas.md +69 -0
  95. data/TODO.diagrams/43-marker-deduplication-strategy.md +54 -0
  96. data/TODO.diagrams/44-element-icon-decorations.md +34 -0
  97. data/TODO.diagrams/45-phantom-parent-class-boxes.md +40 -0
  98. data/TODO.diagrams/46-user-drawn-legend-swatches.md +44 -0
  99. data/TODO.diagrams/47-attribute-line-fidelity.md +72 -0
  100. data/TODO.diagrams/48-spec-coverage-critical-emitter-components.md +45 -0
  101. data/TODO.diagrams/49-spec-coverage-orchestrators.md +25 -0
  102. data/TODO.diagrams/50-examples-parity.md +85 -0
  103. data/TODO.diagrams/51-property-oy-gap-and-remaining-gaps.md +48 -0
  104. data/TODO.diagrams/52-marker-registration-architecture.md +89 -0
  105. data/TODO.diagrams/53-remaining-example-gaps.md +86 -0
  106. data/TODO.diagrams/54-display-name-nested-classes.md +40 -0
  107. data/TODO.diagrams/55-visibility-icon-discriminator.md +58 -0
  108. data/TODO.diagrams/56-package-contents-rendering.md +148 -0
  109. data/TODO.diagrams/57-instance-specification-model.md +100 -0
  110. data/TODO.diagrams/58-domain-model-note-text.md +67 -0
  111. data/TODO.diagrams/59-classes-arrow-discriminator.md +57 -0
  112. data/TODO.diagrams/60-testschema-association-marker.md +40 -0
  113. data/TODO.diagrams/61-legend-block-rendering.md +55 -0
  114. data/TODO.diagrams/61-plateau-parity-gaps.md +59 -0
  115. data/TODO.diagrams/62-ghost-element-rendering.md +56 -0
  116. data/TODO.diagrams/63-qualified-name-wrap.md +36 -0
  117. data/TODO.diagrams/64-property-stereotype-labels.md +22 -0
  118. data/TODO.diagrams/65-path-coordinate-frame.md +62 -0
  119. data/TODO.diagrams/67-path-coordinate-frame.md +62 -0
  120. data/TODO.diagrams/68-inherited-attributes.md +65 -0
  121. data/TODO.diagrams/69-package-anchor-conditional.md +29 -0
  122. data/TODO.diagrams/70-mdg-technology-support.md +68 -0
  123. data/TODO.diagrams/71-off-canvas-parent-ghost.md +45 -0
  124. data/TODO.diagrams/72-basic-attr-divider-paths.md +39 -0
  125. data/TODO.diagrams/73-plateau-polygon-gap.md +69 -0
  126. data/TODO.diagrams/74-plateau-rect-gap.md +22 -0
  127. data/TODO.diagrams/75-plateau-text-gap.md +39 -0
  128. data/TODO.diagrams/76-basic-poly-path-gaps.md +41 -0
  129. data/TODO.diagrams/77-basic-text-overrender.md +40 -0
  130. data/TODO.diagrams/78-headerlines-ocp-refactor.md +65 -0
  131. data/TODO.diagrams/79-architecture-backlog.md +65 -0
  132. data/TODO.diagrams/80-basic-triangle-polygons.md +51 -0
  133. data/TODO.diagrams/81-plateau-path-overrender.md +53 -0
  134. data/TODO.diagrams/82-appearance-canvas-offset.md +54 -0
  135. data/TODO.diagrams/83-3d-diagram-subtitle.md +58 -0
  136. data/TODO.diagrams/84-canvas-offset-mismatches.md +61 -0
  137. data/TODO.diagrams/85-phantom-property-overrender.md +95 -0
  138. data/TODO.diagrams/86-foreign-package-rule.md +62 -0
  139. data/TODO.diagrams/87-internal-tech-encryption.md +80 -0
  140. data/TODO.diagrams/EXAMPLES_STATUS.md +88 -0
  141. data/TODO.diagrams/README.md +69 -0
  142. data/TODO.diagrams/STATUS.md +73 -0
  143. data/TODO.fixup/01-default-grouped-paths.md +34 -0
  144. data/TODO.fixup/02-marker-consolidation-per-connector.md +20 -0
  145. data/TODO.fixup/03-diagram-font-inference.md +28 -0
  146. data/TODO.fixup/04-extract-bounds-calculator.md +25 -0
  147. data/TODO.fixup/05-marker-registry-ocp.md +32 -0
  148. data/TODO.fixup/06-connector-path-bend-routing.md +28 -0
  149. data/TODO.fixup/07-edge-attachment-formula.md +22 -0
  150. data/TODO.fixup/08-visual-diff-harness.md +28 -0
  151. data/TODO.fixup/09-extract-element-compartments.md +34 -0
  152. data/TODO.fixup/10-canvas-width-overshoot-fix.md +20 -0
  153. data/TODO.fixup/11-layer-sequencer-extraction.md +18 -0
  154. data/TODO.fixup/12-path-style-harmony.md +23 -0
  155. data/TODO.fixup/13-datatype-stereotype-fallback.md +28 -0
  156. data/TODO.fixup/14-ea-exe-reverse-engineering-notes.md +56 -0
  157. data/TODO.fixup/15-enumeration-literal-rendering.md +30 -0
  158. data/TODO.fixup/16-operation-compartment-rendering.md +42 -0
  159. data/TODO.fixup/17-path-count-calibration.md +27 -0
  160. data/TODO.fixup/18-polygon-count-calibration.md +32 -0
  161. data/TODO.fixup/19-rect-count-undershoot-investigation.md +40 -0
  162. data/TODO.fixup/20-stereotype-color-from-classifier.md +18 -0
  163. data/TODO.fixup/21-visibility-filter-for-properties.md +24 -0
  164. data/TODO.fixup/22-multiplicity-display-rule-calibration.md +25 -0
  165. data/TODO.fixup/23-diagram-frame-title-element.md +31 -0
  166. data/TODO.fixup/24-note-element-rendering.md +23 -0
  167. data/TODO.fixup/25-ea-exe-disassembly-findings.md +177 -0
  168. data/TODO.fixup/26-svg-renderer-architecture.md +78 -0
  169. data/TODO.fixup/27-svg-element-emitters-disassembly.md +135 -0
  170. data/TODO.fixup/28-geometry-parser-identification.md +57 -0
  171. data/TODO.fixup/29-qea-svg-xmi-triple-comparison.md +185 -0
  172. data/TODO.fixup/30-theme-system-stub.md +46 -0
  173. data/TODO.fixup/31-theme-value-object-registry.md +17 -0
  174. data/TODO.fixup/32-styleex-on-diagram-model.md +22 -0
  175. data/TODO.fixup/33-text-encoding-decimals-rotation.md +29 -0
  176. data/TODO.fixup/34-per-type-fill-via-theme.md +18 -0
  177. data/TODO.fixup/35-stroke-width-per-theme.md +7 -0
  178. data/TODO.fixup/36-qea-rect-coord-normalization.md +24 -0
  179. data/TODO.fixup/37-package-shape-rendering.md +22 -0
  180. data/TODO.fixup/38-note-element-rendering.md +15 -0
  181. data/TODO.fixup/39-styleex-flag-honoring.md +25 -0
  182. data/TODO.fixup/40-connector-dashed-lines.md +15 -0
  183. data/TODO.fixup/41-diagram-frame-showborder-detection.md +23 -0
  184. data/TODO.fixup/42-textrenderer-centralization.md +10 -0
  185. data/TODO.fixup/43-colorresolver-unification.md +24 -0
  186. data/TODO.fixup/44-theme-aware-textrenderer.md +17 -0
  187. data/TODO.fixup/45-theme-aware-fontresolver.md +22 -0
  188. data/TODO.fixup/46-style-parameterized-by-theme.md +21 -0
  189. data/TODO.fixup/47-canvas-frame-inset.md +19 -0
  190. data/TODO.fixup/48-background-rect-format.md +15 -0
  191. data/TODO.fixup/49-all-samples-integration-test.md +17 -0
  192. data/TODO.fixup/50-svg-emitter-architecture-docs.md +33 -0
  193. data/TODO.fixup/51-promote-theme-to-ea-namespace.md +28 -0
  194. data/TODO.fixup/52-theme-definition-integrated-fills.md +26 -0
  195. data/TODO.fixup/53-theme-registry-yaml-loading.md +50 -0
  196. data/TODO.fixup/54-diagram-theme-api.md +50 -0
  197. data/TODO.fixup/55-update-eaemitter-to-ea-theme.md +23 -0
  198. data/TODO.fixup/56-theme-yaml-files.md +65 -0
  199. data/TODO.fixup/57-theme-specs-and-integration.md +27 -0
  200. data/TODO.fixup/58-ea-entry-point-theme-autoload.md +20 -0
  201. data/TODO.fixup/59-thread-theme-through-all-renderers.md +29 -0
  202. data/TODO.fixup/60-cli-theme-option.md +22 -0
  203. data/TODO.fixup/61-theme-validation.md +28 -0
  204. data/TODO.fixup/62-theme-builder-dsl.md +31 -0
  205. data/TODO.fixup/63-theme-integration-rendering-spec.md +24 -0
  206. data/TODO.fixup/64-theme-writeback-styleex.md +21 -0
  207. data/TODO.fixup/65-remove-dead-eaemitter-theme-files.md +30 -0
  208. data/TODO.fixup/66-styleex-writeback.md +22 -0
  209. data/TODO.fixup/67-delete-dead-code.md +28 -0
  210. data/TODO.fixup/68-figure-parity-status.md +82 -0
  211. data/TODO.fixup/69-package-polygon-rendering.md +54 -0
  212. data/TODO.fixup/70-tagged-values-compartment.md +39 -0
  213. data/TODO.fixup/71-auto-detect-theme-from-xmi.md +37 -0
  214. data/TODO.fixup/72-sample-exports-as-regression-specs.md +37 -0
  215. data/TODO.fixup/73-delete-dead-transformations.md +14 -0
  216. data/TODO.fixup/74-delete-dead-diagram-renderers.md +18 -0
  217. data/TODO.fixup/75-visibility-legend-icons.md +23 -0
  218. data/TODO.fixup/76-plateau-text-undershoot.md +27 -0
  219. data/TODO.fixup/77-canvas-frame-margins.md +28 -0
  220. data/TODO.fixup/78-note-element-rendering.md +30 -0
  221. data/TODO.fixup/79-operation-compartment-rendering.md +27 -0
  222. data/TODO.fixup/80-tagged-value-stereotype-visibility.md +37 -0
  223. data/TODO.fixup/81-figure-parity-current-state.md +63 -0
  224. data/config/qea_schema.yml +356 -0
  225. data/config/themes/119.yml +98 -0
  226. data/config/themes/default.yml +14 -0
  227. data/examples/exports/20251010_current_plateau_v5/UML_EA.DTD +6856 -0
  228. data/examples/exports/20251010_current_plateau_v5/model.xml +96709 -0
  229. data/examples/exports/ArcGISWorkspace_template/UML_EA.DTD +6856 -0
  230. data/examples/exports/ArcGISWorkspace_template/model.xml +1427 -0
  231. data/examples/exports/UmlModel_template/UML_EA.DTD +6856 -0
  232. data/examples/exports/UmlModel_template/model.xml +23 -0
  233. data/examples/exports/basic/UML_EA.DTD +6856 -0
  234. data/examples/exports/basic/model.xml +8039 -0
  235. data/examples/exports/simple/UML_EA.DTD +6856 -0
  236. data/examples/exports/simple/model.xml +585 -0
  237. data/examples/exports/simple_example/UML_EA.DTD +6856 -0
  238. data/examples/exports/simple_example/model.xml +23 -0
  239. data/examples/exports/test/UML_EA.DTD +6856 -0
  240. data/examples/exports/test/model.xml +1638 -0
  241. data/examples/qea/basic.qea +0 -0
  242. data/frontend/README.md +51 -0
  243. data/frontend/dist/app.iife.js +24 -0
  244. data/frontend/dist/style.css +1 -0
  245. data/frontend/index.html +26 -0
  246. data/frontend/package-lock.json +2359 -0
  247. data/frontend/package.json +26 -0
  248. data/frontend/src/App.vue +59 -0
  249. data/frontend/src/app.ts +8 -0
  250. data/frontend/src/components/AppHeader.vue +127 -0
  251. data/frontend/src/components/AppSidebar.vue +119 -0
  252. data/frontend/src/components/ClassDetails.vue +247 -0
  253. data/frontend/src/components/DiagramView.vue +79 -0
  254. data/frontend/src/components/PackageDetails.vue +92 -0
  255. data/frontend/src/components/PackageTreeNode.vue +64 -0
  256. data/frontend/src/components/SearchResults.vue +48 -0
  257. data/frontend/src/components/WelcomeView.vue +87 -0
  258. data/frontend/src/shims-vue.d.ts +5 -0
  259. data/frontend/src/stores/dataStore.ts +79 -0
  260. data/frontend/src/stores/uiStore.ts +167 -0
  261. data/frontend/src/styles/main.css +1566 -0
  262. data/frontend/src/types.ts +221 -0
  263. data/frontend/tests/e2e/__screenshots__/new-dark-mode.png +0 -0
  264. data/frontend/tests/e2e/__screenshots__/new-package-details.png +0 -0
  265. data/frontend/tests/e2e/__screenshots__/new-search-modal.png +0 -0
  266. data/frontend/tests/e2e/__screenshots__/new-sidebar-collapsed.png +0 -0
  267. data/frontend/tests/e2e/__screenshots__/new-welcome.png +0 -0
  268. data/frontend/tests/e2e/render.spec.ts +188 -0
  269. data/frontend/tsconfig.json +20 -0
  270. data/frontend/vite.config.ts +26 -0
  271. data/frontend/vitest.config.ts +8 -0
  272. data/lib/ea/cli/app.rb +57 -2
  273. data/lib/ea/cli/command/diagrams.rb +37 -28
  274. data/lib/ea/cli/command/diff.rb +46 -0
  275. data/lib/ea/cli/command/export.rb +101 -0
  276. data/lib/ea/cli/command/info.rb +75 -0
  277. data/lib/ea/cli/command/lint.rb +48 -0
  278. data/lib/ea/cli/command/mdg.rb +98 -0
  279. data/lib/ea/cli/command/query.rb +34 -0
  280. data/lib/ea/cli/command/render.rb +135 -0
  281. data/lib/ea/cli/command/svg.rb +8 -15
  282. data/lib/ea/cli/command.rb +7 -0
  283. data/lib/ea/diagram/display_config.rb +184 -0
  284. data/lib/ea/diagram.rb +4 -41
  285. data/lib/ea/diff/change.rb +30 -0
  286. data/lib/ea/diff/comparator.rb +109 -0
  287. data/lib/ea/diff/html_reporter.rb +91 -0
  288. data/lib/ea/diff.rb +15 -0
  289. data/lib/ea/export/json/generator.rb +118 -0
  290. data/lib/ea/export/json.rb +12 -0
  291. data/lib/ea/export/json_schema/generator.rb +77 -0
  292. data/lib/ea/export/json_schema.rb +12 -0
  293. data/lib/ea/export/plantuml/generator.rb +140 -0
  294. data/lib/ea/export/plantuml.rb +12 -0
  295. data/lib/ea/export/xsd/class_mapping.rb +65 -0
  296. data/lib/ea/export/xsd/generator.rb +137 -0
  297. data/lib/ea/export/xsd/namespace_registry.rb +82 -0
  298. data/lib/ea/export/xsd.rb +19 -0
  299. data/lib/ea/export.rb +13 -0
  300. data/lib/ea/image/emf_renderer.rb +42 -0
  301. data/lib/ea/image.rb +12 -0
  302. data/lib/ea/lint/engine.rb +30 -0
  303. data/lib/ea/lint/lint_rule.rb +38 -0
  304. data/lib/ea/lint/offense.rb +18 -0
  305. data/lib/ea/lint/rules/cyclic_generalization.rb +62 -0
  306. data/lib/ea/lint/rules/duplicate_name.rb +28 -0
  307. data/lib/ea/lint/rules/missing_stereotype.rb +40 -0
  308. data/lib/ea/lint/rules/naming_convention.rb +44 -0
  309. data/lib/ea/lint/rules/orphan_element.rb +28 -0
  310. data/lib/ea/lint/rules.rb +14 -0
  311. data/lib/ea/lint.rb +13 -0
  312. data/lib/ea/mdg/document.rb +120 -0
  313. data/lib/ea/mdg/loader.rb +118 -0
  314. data/lib/ea/mdg/registry.rb +98 -0
  315. data/lib/ea/mdg/stereotype_alias_registry.rb +77 -0
  316. data/lib/ea/mdg/xml/applies_to.rb +18 -0
  317. data/lib/ea/mdg/xml/apply.rb +18 -0
  318. data/lib/ea/mdg/xml/content.rb +18 -0
  319. data/lib/ea/mdg/xml/documentation.rb +24 -0
  320. data/lib/ea/mdg/xml/stereotype.rb +28 -0
  321. data/lib/ea/mdg/xml/stereotypes.rb +18 -0
  322. data/lib/ea/mdg/xml/tag.rb +28 -0
  323. data/lib/ea/mdg/xml/tagged_values.rb +18 -0
  324. data/lib/ea/mdg/xml/technology.rb +28 -0
  325. data/lib/ea/mdg/xml/uml_profile.rb +22 -0
  326. data/lib/ea/mdg/xml/uml_profiles.rb +18 -0
  327. data/lib/ea/mdg/xml.rb +24 -0
  328. data/lib/ea/mdg.rb +25 -0
  329. data/lib/ea/model/classifier.rb +4 -0
  330. data/lib/ea/model/constraint.rb +24 -0
  331. data/lib/ea/model/diagram.rb +85 -0
  332. data/lib/ea/model/diagram_connector.rb +34 -0
  333. data/lib/ea/model/diagram_element.rb +16 -0
  334. data/lib/ea/model/document.rb +47 -2
  335. data/lib/ea/model/ghost_label.rb +32 -0
  336. data/lib/ea/model/instance_specification.rb +34 -0
  337. data/lib/ea/model/legend.rb +43 -0
  338. data/lib/ea/model/legend_item.rb +26 -0
  339. data/lib/ea/model/note.rb +28 -0
  340. data/lib/ea/model/operation.rb +2 -0
  341. data/lib/ea/model/property.rb +2 -0
  342. data/lib/ea/model/relationship.rb +2 -0
  343. data/lib/ea/model/signal.rb +14 -0
  344. data/lib/ea/model/slot.rb +19 -0
  345. data/lib/ea/model.rb +8 -0
  346. data/lib/ea/ocl/evaluator.rb +94 -0
  347. data/lib/ea/ocl/nodes.rb +30 -0
  348. data/lib/ea/ocl/parser.rb +133 -0
  349. data/lib/ea/ocl/unsupported_error.rb +7 -0
  350. data/lib/ea/ocl.rb +13 -0
  351. data/lib/ea/qea/factory/document_builder.rb +24 -10
  352. data/lib/ea/qea/factory/generalization_builder.rb +9 -11
  353. data/lib/ea/qea/infrastructure/table_reader.rb +9 -0
  354. data/lib/ea/qea/models/ea_author.rb +35 -0
  355. data/lib/ea/qea/models/ea_glossary.rb +34 -0
  356. data/lib/ea/qea/models/ea_image.rb +53 -0
  357. data/lib/ea/qea/models/ea_implement.rb +32 -0
  358. data/lib/ea/qea/models/ea_list.rb +42 -0
  359. data/lib/ea/qea/models/ea_object_effort.rb +34 -0
  360. data/lib/ea/qea/models/ea_object_problem.rb +37 -0
  361. data/lib/ea/qea/models/ea_object_require.rb +34 -0
  362. data/lib/ea/qea/models/ea_object_resource.rb +34 -0
  363. data/lib/ea/qea/models/ea_object_risk.rb +34 -0
  364. data/lib/ea/qea/models/ea_object_scenario.rb +34 -0
  365. data/lib/ea/qea/models/ea_object_test.rb +35 -0
  366. data/lib/ea/qea/models/ea_object_trx.rb +34 -0
  367. data/lib/ea/qea/models/ea_operation.rb +7 -0
  368. data/lib/ea/qea/models/ea_palette.rb +30 -0
  369. data/lib/ea/qea/models/ea_palette_item.rb +36 -0
  370. data/lib/ea/qea/models/ea_phase.rb +40 -0
  371. data/lib/ea/qea/models/ea_role_constraint.rb +32 -0
  372. data/lib/ea/qea/models/ea_secrypt.rb +34 -0
  373. data/lib/ea/qea/models/ea_version.rb +39 -0
  374. data/lib/ea/qea/models/ea_xref.rb +11 -1
  375. data/lib/ea/qea/models.rb +19 -0
  376. data/lib/ea/qea/services/database_loader.rb +22 -1
  377. data/lib/ea/qea/validation/database/ocl_constraint_validator.rb +65 -0
  378. data/lib/ea/qea/validation/database.rb +2 -0
  379. data/lib/ea/qea/validation/validation_engine.rb +2 -0
  380. data/lib/ea/qea/validation.rb +2 -0
  381. data/lib/ea/query/builder.rb +127 -0
  382. data/lib/ea/query.rb +9 -0
  383. data/lib/ea/render/image_converter.rb +101 -0
  384. data/lib/ea/render.rb +10 -0
  385. data/lib/ea/shapescript/parser.rb +208 -0
  386. data/lib/ea/shapescript/renderer.rb +74 -0
  387. data/lib/ea/shapescript/shape.rb +34 -0
  388. data/lib/ea/shapescript.rb +24 -0
  389. data/lib/ea/sources/qea/adapter.rb +22 -5
  390. data/lib/ea/sources/qea/classifier_builder.rb +184 -7
  391. data/lib/ea/sources/qea/diagram_builder.rb +352 -24
  392. data/lib/ea/sources/qea/id_normalizer.rb +16 -1
  393. data/lib/ea/sources/qea/instance_builder.rb +113 -0
  394. data/lib/ea/sources/qea/legend_builder.rb +137 -0
  395. data/lib/ea/sources/qea/note_builder.rb +58 -0
  396. data/lib/ea/sources/qea/object_classifier_map.rb +6 -1
  397. data/lib/ea/sources/qea/operation_builder.rb +1 -0
  398. data/lib/ea/sources/qea/package_builder.rb +16 -0
  399. data/lib/ea/sources/qea/relationship_builder.rb +21 -5
  400. data/lib/ea/sources/qea/tagged_value_builder.rb +28 -9
  401. data/lib/ea/sources/qea/xref/legend_definition.rb +35 -0
  402. data/lib/ea/sources/qea/xref/parser.rb +150 -0
  403. data/lib/ea/sources/qea/xref/property_definition.rb +29 -0
  404. data/lib/ea/sources/qea/xref/record.rb +41 -0
  405. data/lib/ea/sources/qea/xref/stereotype_application.rb +37 -0
  406. data/lib/ea/sources/qea/xref.rb +27 -0
  407. data/lib/ea/sources/qea.rb +4 -0
  408. data/lib/ea/sources/xmi/adapter.rb +80 -2
  409. data/lib/ea/sources/xmi/classifier_builder.rb +10 -1
  410. data/lib/ea/sources/xmi/diagram_builder.rb +95 -18
  411. data/lib/ea/sources/xmi/extension_geometry_parser.rb +19 -2
  412. data/lib/ea/sources/xmi/extension_style_parser.rb +15 -2
  413. data/lib/ea/sources/xmi/note_builder.rb +46 -0
  414. data/lib/ea/sources/xmi/property_builder.rb +9 -0
  415. data/lib/ea/sources/xmi/stereotype_builder.rb +54 -0
  416. data/lib/ea/sources/xmi/tag_builder.rb +45 -0
  417. data/lib/ea/sources/xmi/umldi_keyword_extractor.rb +87 -0
  418. data/lib/ea/sources/xmi.rb +4 -0
  419. data/lib/ea/spa/output/sharded_multi_file_strategy.rb +18 -3
  420. data/lib/ea/spa/output/single_file_strategy.rb +8 -2
  421. data/lib/ea/spa/output/strategy.rb +34 -0
  422. data/lib/ea/svg/connector_router.rb +134 -0
  423. data/lib/ea/svg/ea_emitter/background.rb +2 -3
  424. data/lib/ea/svg/ea_emitter/bounds_calculator.rb +142 -0
  425. data/lib/ea/svg/ea_emitter/canvas.rb +56 -33
  426. data/lib/ea/svg/ea_emitter/color_resolver.rb +56 -0
  427. data/lib/ea/svg/ea_emitter/compartment/attributes.rb +28 -0
  428. data/lib/ea/svg/ea_emitter/compartment/constraints.rb +30 -0
  429. data/lib/ea/svg/ea_emitter/compartment/enum_literals.rb +86 -0
  430. data/lib/ea/svg/ea_emitter/compartment/header.rb +32 -0
  431. data/lib/ea/svg/ea_emitter/compartment/header_divider.rb +57 -0
  432. data/lib/ea/svg/ea_emitter/compartment/instance_slots.rb +96 -0
  433. data/lib/ea/svg/ea_emitter/compartment/instance_underline.rb +67 -0
  434. data/lib/ea/svg/ea_emitter/compartment/note_body.rb +52 -0
  435. data/lib/ea/svg/ea_emitter/compartment/note_text.rb +102 -0
  436. data/lib/ea/svg/ea_emitter/compartment/operations.rb +38 -0
  437. data/lib/ea/svg/ea_emitter/compartment/package_contents.rb +72 -0
  438. data/lib/ea/svg/ea_emitter/compartment/package_from_parent.rb +136 -0
  439. data/lib/ea/svg/ea_emitter/compartment/shape.rb +82 -0
  440. data/lib/ea/svg/ea_emitter/compartment/stereotype_icon.rb +32 -0
  441. data/lib/ea/svg/ea_emitter/compartment/tagged_values.rb +30 -0
  442. data/lib/ea/svg/ea_emitter/compartment.rb +60 -0
  443. data/lib/ea/svg/ea_emitter/connectors.rb +46 -12
  444. data/lib/ea/svg/ea_emitter/diagram_frame.rb +143 -0
  445. data/lib/ea/svg/ea_emitter/document.rb +49 -25
  446. data/lib/ea/svg/ea_emitter/element/attribute_line_builder.rb +117 -0
  447. data/lib/ea/svg/ea_emitter/element/attribute_renderer.rb +119 -0
  448. data/lib/ea/svg/ea_emitter/element/bcol_decoder.rb +29 -0
  449. data/lib/ea/svg/ea_emitter/element/compartment_geometry.rb +113 -0
  450. data/lib/ea/svg/ea_emitter/element/constraint_renderer.rb +53 -0
  451. data/lib/ea/svg/ea_emitter/element/divider_renderer.rb +17 -0
  452. data/lib/ea/svg/ea_emitter/element/enumeration_literal_renderer.rb +34 -0
  453. data/lib/ea/svg/ea_emitter/element/filter.rb +44 -0
  454. data/lib/ea/svg/ea_emitter/element/hand_draw_shape_renderer.rb +77 -0
  455. data/lib/ea/svg/ea_emitter/element/header_line_pipeline.rb +43 -0
  456. data/lib/ea/svg/ea_emitter/element/header_line_provider/instance_spec.rb +27 -0
  457. data/lib/ea/svg/ea_emitter/element/header_line_provider/name.rb +76 -0
  458. data/lib/ea/svg/ea_emitter/element/header_line_provider/parent_ghost.rb +25 -0
  459. data/lib/ea/svg/ea_emitter/element/header_line_provider/stereotype_label.rb +68 -0
  460. data/lib/ea/svg/ea_emitter/element/header_line_provider.rb +19 -0
  461. data/lib/ea/svg/ea_emitter/element/header_lines.rb +27 -0
  462. data/lib/ea/svg/ea_emitter/element/header_renderer.rb +48 -0
  463. data/lib/ea/svg/ea_emitter/element/icon_renderer.rb +109 -0
  464. data/lib/ea/svg/ea_emitter/element/legend_renderer.rb +177 -0
  465. data/lib/ea/svg/ea_emitter/element/note_shape_renderer.rb +112 -0
  466. data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +100 -0
  467. data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +181 -0
  468. data/lib/ea/svg/ea_emitter/element/row_icon_renderer.rb +156 -0
  469. data/lib/ea/svg/ea_emitter/element/shape_renderer.rb +17 -0
  470. data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +119 -0
  471. data/lib/ea/svg/ea_emitter/element/tagged_value_renderer.rb +50 -0
  472. data/lib/ea/svg/ea_emitter/element/text_escape.rb +20 -0
  473. data/lib/ea/svg/ea_emitter/element.rb +37 -0
  474. data/lib/ea/svg/ea_emitter/elements.rb +283 -95
  475. data/lib/ea/svg/ea_emitter/font_resolver.rb +111 -0
  476. data/lib/ea/svg/ea_emitter/ghost_labels.rb +66 -0
  477. data/lib/ea/svg/ea_emitter/label/end_label.rb +266 -0
  478. data/lib/ea/svg/ea_emitter/label/midpoint_label.rb +101 -0
  479. data/lib/ea/svg/ea_emitter/label/registry.rb +50 -0
  480. data/lib/ea/svg/ea_emitter/label.rb +23 -0
  481. data/lib/ea/svg/ea_emitter/labels.rb +93 -27
  482. data/lib/ea/svg/ea_emitter/layer.rb +20 -0
  483. data/lib/ea/svg/ea_emitter/layer_sequencer.rb +93 -0
  484. data/lib/ea/svg/ea_emitter/marker/arrow_path.rb +52 -0
  485. data/lib/ea/svg/ea_emitter/marker/diamond.rb +57 -0
  486. data/lib/ea/svg/ea_emitter/marker/kind.rb +35 -0
  487. data/lib/ea/svg/ea_emitter/marker/open_triangle.rb +27 -0
  488. data/lib/ea/svg/ea_emitter/marker/package_import.rb +39 -0
  489. data/lib/ea/svg/ea_emitter/marker/plus.rb +40 -0
  490. data/lib/ea/svg/ea_emitter/marker/registry.rb +42 -0
  491. data/lib/ea/svg/ea_emitter/marker.rb +37 -0
  492. data/lib/ea/svg/ea_emitter/markers.rb +351 -64
  493. data/lib/ea/svg/ea_emitter/render_context.rb +52 -0
  494. data/lib/ea/svg/ea_emitter/style.rb +15 -0
  495. data/lib/ea/svg/ea_emitter/text_renderer.rb +92 -0
  496. data/lib/ea/svg/ea_emitter.rb +33 -11
  497. data/lib/ea/svg/parity/checker.rb +105 -0
  498. data/lib/ea/svg/parity/source.rb +59 -0
  499. data/lib/ea/svg/parity/suite.rb +113 -0
  500. data/lib/ea/svg/parity.rb +14 -0
  501. data/lib/ea/svg.rb +2 -15
  502. data/lib/ea/theme/definition.rb +176 -0
  503. data/lib/ea/theme/loader.rb +125 -0
  504. data/lib/ea/theme/registry.rb +96 -0
  505. data/lib/ea/theme.rb +37 -0
  506. data/lib/ea/transformations.rb +5 -88
  507. data/lib/ea/validation/xmi_parity.rb +80 -0
  508. data/lib/ea/validation.rb +12 -0
  509. data/lib/ea/version.rb +4 -1
  510. data/lib/ea.rb +15 -4
  511. data/lib/tasks/svg_bench.rake +87 -0
  512. data/lutaml-uml-0.5.1.gem +0 -0
  513. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes-list.png +0 -0
  514. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes.png +0 -0
  515. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-2-gradients.png +0 -0
  516. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-3-colors.png +0 -0
  517. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance-fonts.png +0 -0
  518. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance.png +0 -0
  519. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-5-behavior.png +0 -0
  520. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-6-sequence.png +0 -0
  521. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram.png +0 -0
  522. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-2-objects.png +0 -0
  523. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-3-links.png +0 -0
  524. metadata +464 -26
  525. data/lib/ea/diagram/configuration.rb +0 -379
  526. data/lib/ea/diagram/element_renderers/base_renderer.rb +0 -77
  527. data/lib/ea/diagram/element_renderers/class_renderer.rb +0 -323
  528. data/lib/ea/diagram/element_renderers/connector_renderer.rb +0 -41
  529. data/lib/ea/diagram/element_renderers/package_renderer.rb +0 -61
  530. data/lib/ea/diagram/element_renderers.rb +0 -43
  531. data/lib/ea/diagram/extractor.rb +0 -566
  532. data/lib/ea/diagram/layout_engine.rb +0 -170
  533. data/lib/ea/diagram/path_builder.rb +0 -202
  534. data/lib/ea/diagram/style_parser.rb +0 -42
  535. data/lib/ea/diagram/style_resolver.rb +0 -291
  536. data/lib/ea/diagram/svg_renderer.rb +0 -274
  537. data/lib/ea/diagram/util.rb +0 -73
  538. data/lib/ea/svg/bounds_calculator.rb +0 -74
  539. data/lib/ea/svg/connector_path.rb +0 -138
  540. data/lib/ea/svg/element_box.rb +0 -256
  541. data/lib/ea/svg/renderer.rb +0 -77
  542. data/lib/ea/svg/style_resolver.rb +0 -72
  543. data/lib/ea/transformations/configuration.rb +0 -333
  544. data/lib/ea/transformations/format_registry.rb +0 -366
  545. data/lib/ea/transformations/parsers/base_parser.rb +0 -482
  546. data/lib/ea/transformations/parsers/qea_parser.rb +0 -401
  547. data/lib/ea/transformations/parsers/xmi_parser.rb +0 -243
  548. data/lib/ea/transformations/transformation_engine.rb +0 -390
@@ -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
 
@@ -23,7 +23,8 @@ module Ea
23
23
  italic: truthy?(kv["italic"]),
24
24
  underline: truthy?(kv["ul"]),
25
25
  black: truthy?(kv["black"]),
26
- hide_icon: truthy?(kv["HideIcon"]),
26
+ hide_icon: tri_state_icon(kv["HideIcon"]),
27
+ show_tagged_values: truthy?(kv["Tag"]),
27
28
  duid: kv["DUID"],
28
29
  soid: kv["SOID"],
29
30
  eoid: kv["EOID"],
@@ -34,6 +35,17 @@ module Ea
34
35
  )
35
36
  end
36
37
 
38
+ # HideIcon tri-state: "1" → :hidden, "0" → :shown,
39
+ # missing → nil (use the diagram default, which EA treats
40
+ # as hidden in practice).
41
+ def tri_state_icon(value)
42
+ return nil if value.nil? || value.empty?
43
+ return :hidden if value == "1" || value == "true" || value == "-1"
44
+ return :shown if value == "0"
45
+
46
+ nil
47
+ end
48
+
37
49
  def split_pairs(str)
38
50
  str.split(";").filter_map do |pair|
39
51
  next nil unless pair.include?("=")
@@ -59,7 +71,7 @@ module Ea
59
71
  return 13 if value.nil? || value.empty?
60
72
 
61
73
  pct = int(value)
62
- return 13 if pct.nil?
74
+ return 13 if pct.nil? || pct.zero?
63
75
 
64
76
  (pct * 13 / 100).round
65
77
  end
@@ -68,6 +80,7 @@ module Ea
68
80
  :background_color, :line_color, :line_width,
69
81
  :font_family, :font_size,
70
82
  :bold, :italic, :underline, :black, :hide_icon,
83
+ :show_tagged_values,
71
84
  :duid, :soid, :eoid, :mode, :color, :hidden,
72
85
  :raw,
73
86
  keyword_init: true
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+
5
+ module Ea
6
+ module Sources
7
+ module Xmi
8
+ # Extracts Note elements from EA's XMI extension block.
9
+ # Notes are documentation entries (`<element xmi:type="uml:Note">`)
10
+ # inside `<xmi:Extension>/<elements>`. Each carries a
11
+ # `<properties documentation="..."/>` child with the body text.
12
+ #
13
+ # The xmi gem parses Note elements as part of the extension
14
+ # block; we walk them and project to Ea::Model::Note.
15
+ class NoteBuilder
16
+ attr_reader :root
17
+
18
+ def initialize(root)
19
+ @root = root
20
+ end
21
+
22
+ def build_all
23
+ elements = root.extension&.elements
24
+ return [] unless elements
25
+
26
+ elements.element.to_a.filter_map { |e| build_one(e) }
27
+ end
28
+
29
+ private
30
+
31
+ def build_one(ext_element)
32
+ type = ext_element.type
33
+ return nil unless type == "uml:Note"
34
+
35
+ props = ext_element.properties
36
+ Ea::Model::Note.new(
37
+ id: ext_element.idref,
38
+ name: ext_element.name,
39
+ body: props&.documentation,
40
+ note_type: "Note"
41
+ )
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -19,6 +19,7 @@ module Ea
19
19
  id: id,
20
20
  name: attr.name,
21
21
  owner_id: owner.id,
22
+ type_ref: type_ref_for(attr),
22
23
  type_name: type_name_for(attr),
23
24
  qualified_name: "#{owner.name}::#{attr.name}",
24
25
  multiplicity_lower: parse_lower(attr),
@@ -29,6 +30,7 @@ module Ea
29
30
  is_ordered: boolean(attr.is_ordered),
30
31
  is_unique: boolean(attr.is_unique),
31
32
  aggregation: attr.aggregation || "none",
33
+ association_id: IdNormalizer.from_xmi_id(attr.association),
32
34
  visibility: attr.visibility,
33
35
  annotations: AnnotationBuilder.from_element(attr, id)
34
36
  )
@@ -44,6 +46,13 @@ module Ea
44
46
  attr.uml_type&.idref
45
47
  end
46
48
 
49
+ def type_ref_for(attr)
50
+ # XMI <type xmi:idref=...> is the type classifier's GUID.
51
+ # The adapter's resolve_type_names walks this against the
52
+ # model to attach the in-model qualified name.
53
+ attr.uml_type&.idref
54
+ end
55
+
47
56
  def parse_lower(attr)
48
57
  value = attr.lower_value
49
58
  return 1 unless value
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Xmi
6
+ # Extracts per-element stereotype applications from EA's
7
+ # `<xmi:Extension>/<elements>` block. EA stores stereotype
8
+ # info in `<element xmi:idref="EAID_...">/<properties
9
+ # stereotype="X"/>`. The uml:Model packaged elements carry
10
+ # type info but not stereotypes.
11
+ #
12
+ # Returns a Hash{element_id => Array<String>} of stereotype
13
+ # names applied to that element.
14
+ class StereotypeBuilder
15
+ attr_reader :root
16
+
17
+ def initialize(root)
18
+ @root = root
19
+ end
20
+
21
+ def grouped_by_element
22
+ @grouped ||= begin
23
+ elements = root.extension&.elements
24
+ return {} unless elements
25
+
26
+ elements.element.each_with_object({}) do |ext_element, acc|
27
+ id = ext_element.idref
28
+ next unless id
29
+
30
+ stereotypes = stereotypes_for(ext_element)
31
+ next if stereotypes.empty?
32
+
33
+ acc[id] = stereotypes
34
+ end
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ # The `<properties stereotype="X"/>` attribute holds the
41
+ # primary stereotype. Additional stereotypes from `<xrefs>`
42
+ # are not captured here — that would require parsing the
43
+ # packed XREFPROP string format.
44
+ def stereotypes_for(ext_element)
45
+ props = ext_element.properties
46
+ value = props&.stereotype
47
+ return [] if value.nil? || value.to_s.empty?
48
+
49
+ [value.to_s]
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+
5
+ module Ea
6
+ module Sources
7
+ module Xmi
8
+ # Extracts tagged values from the `<xmi:Extension><tags><tag/>`
9
+ # block. The xmi gem does not expose Sparx tags directly, so we
10
+ # parse the raw XML once and group tags by their
11
+ # `modelElement` attribute. Each tag's value is truncated at
12
+ # the first `#` (EA encodes notes after that marker).
13
+ class TagBuilder
14
+ attr_reader :xmi_path
15
+
16
+ def initialize(xmi_path)
17
+ @xmi_path = xmi_path
18
+ end
19
+
20
+ # Returns Hash{model_element_id => Array<Ea::Model::TaggedValue>}
21
+ def grouped_by_model_element
22
+ return {} if xmi_path.nil?
23
+
24
+ @grouped ||= begin
25
+ doc = Nokogiri::XML(File.read(xmi_path))
26
+ # <tags><tag/></tags> lives inside per-element <element>
27
+ # blocks within <xmi:Extension>. The element itself is in
28
+ # no XML namespace; match by local-name to be safe.
29
+ doc.xpath(%(//*[local-name()="tags"]/*[local-name()="tag"]))
30
+ .each_with_object({}) do |node, acc|
31
+ me = node["modelElement"]
32
+ next unless me
33
+
34
+ key = node["name"].to_s
35
+ value = node["value"].to_s.split("#", 2).first
36
+ next if key.empty?
37
+
38
+ (acc[me] ||= []) << Ea::Model::TaggedValue.new(key: key, value: value)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end