ea 0.3.1 → 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 (544) 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/01-xmi-extension-elements-parsing.md +67 -0
  36. data/TODO.diagrams/02-diagram-element-rich-model.md +61 -0
  37. data/TODO.diagrams/02-polygon-over-rendering.md +29 -0
  38. data/TODO.diagrams/03-ea-style-svg-emitter.md +70 -0
  39. data/TODO.diagrams/03-text-over-rendering.md +30 -0
  40. data/TODO.diagrams/04-connector-routing-from-soid-eoid.md +55 -0
  41. data/TODO.diagrams/04-rect-under-rendering.md +33 -0
  42. data/TODO.diagrams/05-canvas-dimension-parity.md +51 -0
  43. data/TODO.diagrams/05-marker-rendering-per-relationship-type.md +49 -0
  44. data/TODO.diagrams/06-connector-labels.md +49 -0
  45. data/TODO.diagrams/06-group-layering-parity.md +39 -0
  46. data/TODO.diagrams/07-canvas-size-and-viewbox.md +39 -0
  47. data/TODO.diagrams/07-text-content-fidelity.md +65 -0
  48. data/TODO.diagrams/08-qea-direct-pipeline.md +46 -0
  49. data/TODO.diagrams/08-visual-regression-harness.md +34 -0
  50. data/TODO.diagrams/09-cli-render-all-diagrams.md +25 -0
  51. data/TODO.diagrams/10-stereotype-color-config.md +29 -0
  52. data/TODO.diagrams/11-elkrb-auto-layout-integration.md +53 -0
  53. data/TODO.diagrams/12-connector-labels.md +40 -0
  54. data/TODO.diagrams/12-per-entity-svg-layer-ordering.md +24 -0
  55. data/TODO.diagrams/13-marker-polygon-vs-path-dispatch.md +20 -0
  56. data/TODO.diagrams/13-per-text-type-font-sizes.md +33 -0
  57. data/TODO.diagrams/14-center-header-text.md +33 -0
  58. data/TODO.diagrams/14-filter-diagram-frame-elements.md +18 -0
  59. data/TODO.diagrams/15-deep-dead-code-audit.md +47 -0
  60. data/TODO.diagrams/15-per-element-font-from-style.md +24 -0
  61. data/TODO.diagrams/16-canvas-marker-extent.md +30 -0
  62. data/TODO.diagrams/16-document-remaining-parity-gaps.md +91 -0
  63. data/TODO.diagrams/17-italic-abstract-classes.md +28 -0
  64. data/TODO.diagrams/17-remaining-parity-gaps.md +90 -0
  65. data/TODO.diagrams/18-canvas-from-image-bounds.md +20 -0
  66. data/TODO.diagrams/18-property-lookup-index.md +42 -0
  67. data/TODO.diagrams/19-connector-sx-sy-routing.md +27 -0
  68. data/TODO.diagrams/19-replace-respond-to-with-is-a.md +31 -0
  69. data/TODO.diagrams/20-split-labels-emitter.md +56 -0
  70. data/TODO.diagrams/20-visibility-glyph-font-encoding.md +27 -0
  71. data/TODO.diagrams/21-aggregation-navigability-arrow.md +21 -0
  72. data/TODO.diagrams/21-auto-generated-legend-block.md +65 -0
  73. data/TODO.diagrams/22-ghost-element-discriminator.md +57 -0
  74. data/TODO.diagrams/22-style-based-path-grouping.md +28 -0
  75. data/TODO.diagrams/23-marker-polygon-deduplication.md +47 -0
  76. data/TODO.diagrams/23-multiplicity-label-positioning.md +29 -0
  77. data/TODO.diagrams/24-marker-anchor-structural.md +35 -0
  78. data/TODO.diagrams/24-track-marker-anchors-structurally.md +66 -0
  79. data/TODO.diagrams/24-truncate-properties-to-fit-box.md +24 -0
  80. data/TODO.diagrams/26-phantom-general-parent-rendering.md +59 -0
  81. data/TODO.diagrams/27-path-grouping-mode-detection.md +43 -0
  82. data/TODO.diagrams/28-architecture-cleanup-batch.md +47 -0
  83. data/TODO.diagrams/30-decompose-elements-groups-for.md +52 -0
  84. data/TODO.diagrams/32-examples-coverage-batch.md +78 -0
  85. data/TODO.diagrams/36-instance-specification-model.md +42 -0
  86. data/TODO.diagrams/37-element-icon-decorations.md +41 -0
  87. data/TODO.diagrams/38-html-note-and-receptions.md +32 -0
  88. data/TODO.diagrams/39-property-stereotype-discriminator.md +40 -0
  89. data/TODO.diagrams/40-standalone-1-discriminator.md +38 -0
  90. data/TODO.diagrams/41-xmi-to-svg-pipeline.md +113 -0
  91. data/TODO.diagrams/42-close-all-parity-deltas.md +69 -0
  92. data/TODO.diagrams/43-marker-deduplication-strategy.md +54 -0
  93. data/TODO.diagrams/44-element-icon-decorations.md +34 -0
  94. data/TODO.diagrams/45-phantom-parent-class-boxes.md +40 -0
  95. data/TODO.diagrams/46-user-drawn-legend-swatches.md +44 -0
  96. data/TODO.diagrams/47-attribute-line-fidelity.md +72 -0
  97. data/TODO.diagrams/48-spec-coverage-critical-emitter-components.md +45 -0
  98. data/TODO.diagrams/49-spec-coverage-orchestrators.md +25 -0
  99. data/TODO.diagrams/50-examples-parity.md +85 -0
  100. data/TODO.diagrams/51-property-oy-gap-and-remaining-gaps.md +48 -0
  101. data/TODO.diagrams/52-marker-registration-architecture.md +89 -0
  102. data/TODO.diagrams/53-remaining-example-gaps.md +86 -0
  103. data/TODO.diagrams/54-display-name-nested-classes.md +40 -0
  104. data/TODO.diagrams/55-visibility-icon-discriminator.md +58 -0
  105. data/TODO.diagrams/56-package-contents-rendering.md +148 -0
  106. data/TODO.diagrams/57-instance-specification-model.md +100 -0
  107. data/TODO.diagrams/58-domain-model-note-text.md +67 -0
  108. data/TODO.diagrams/59-classes-arrow-discriminator.md +57 -0
  109. data/TODO.diagrams/60-testschema-association-marker.md +40 -0
  110. data/TODO.diagrams/61-legend-block-rendering.md +55 -0
  111. data/TODO.diagrams/61-plateau-parity-gaps.md +59 -0
  112. data/TODO.diagrams/62-ghost-element-rendering.md +56 -0
  113. data/TODO.diagrams/63-qualified-name-wrap.md +36 -0
  114. data/TODO.diagrams/64-property-stereotype-labels.md +22 -0
  115. data/TODO.diagrams/65-path-coordinate-frame.md +62 -0
  116. data/TODO.diagrams/67-path-coordinate-frame.md +62 -0
  117. data/TODO.diagrams/68-inherited-attributes.md +65 -0
  118. data/TODO.diagrams/69-package-anchor-conditional.md +29 -0
  119. data/TODO.diagrams/70-mdg-technology-support.md +68 -0
  120. data/TODO.diagrams/71-off-canvas-parent-ghost.md +45 -0
  121. data/TODO.diagrams/72-basic-attr-divider-paths.md +39 -0
  122. data/TODO.diagrams/73-plateau-polygon-gap.md +69 -0
  123. data/TODO.diagrams/74-plateau-rect-gap.md +22 -0
  124. data/TODO.diagrams/75-plateau-text-gap.md +39 -0
  125. data/TODO.diagrams/76-basic-poly-path-gaps.md +41 -0
  126. data/TODO.diagrams/77-basic-text-overrender.md +40 -0
  127. data/TODO.diagrams/78-headerlines-ocp-refactor.md +65 -0
  128. data/TODO.diagrams/79-architecture-backlog.md +65 -0
  129. data/TODO.diagrams/80-basic-triangle-polygons.md +51 -0
  130. data/TODO.diagrams/81-plateau-path-overrender.md +53 -0
  131. data/TODO.diagrams/82-appearance-canvas-offset.md +54 -0
  132. data/TODO.diagrams/83-3d-diagram-subtitle.md +58 -0
  133. data/TODO.diagrams/84-canvas-offset-mismatches.md +61 -0
  134. data/TODO.diagrams/85-phantom-property-overrender.md +95 -0
  135. data/TODO.diagrams/86-foreign-package-rule.md +62 -0
  136. data/TODO.diagrams/87-internal-tech-encryption.md +80 -0
  137. data/TODO.diagrams/EXAMPLES_STATUS.md +88 -0
  138. data/TODO.diagrams/README.md +69 -0
  139. data/TODO.diagrams/STATUS.md +73 -0
  140. data/TODO.fixup/01-default-grouped-paths.md +34 -0
  141. data/TODO.fixup/02-marker-consolidation-per-connector.md +20 -0
  142. data/TODO.fixup/03-diagram-font-inference.md +28 -0
  143. data/TODO.fixup/04-extract-bounds-calculator.md +25 -0
  144. data/TODO.fixup/05-marker-registry-ocp.md +32 -0
  145. data/TODO.fixup/06-connector-path-bend-routing.md +28 -0
  146. data/TODO.fixup/07-edge-attachment-formula.md +22 -0
  147. data/TODO.fixup/08-visual-diff-harness.md +28 -0
  148. data/TODO.fixup/09-extract-element-compartments.md +34 -0
  149. data/TODO.fixup/10-canvas-width-overshoot-fix.md +20 -0
  150. data/TODO.fixup/11-layer-sequencer-extraction.md +18 -0
  151. data/TODO.fixup/12-path-style-harmony.md +23 -0
  152. data/TODO.fixup/13-datatype-stereotype-fallback.md +28 -0
  153. data/TODO.fixup/14-ea-exe-reverse-engineering-notes.md +56 -0
  154. data/TODO.fixup/15-enumeration-literal-rendering.md +30 -0
  155. data/TODO.fixup/16-operation-compartment-rendering.md +42 -0
  156. data/TODO.fixup/17-path-count-calibration.md +27 -0
  157. data/TODO.fixup/18-polygon-count-calibration.md +32 -0
  158. data/TODO.fixup/19-rect-count-undershoot-investigation.md +40 -0
  159. data/TODO.fixup/20-stereotype-color-from-classifier.md +18 -0
  160. data/TODO.fixup/21-visibility-filter-for-properties.md +24 -0
  161. data/TODO.fixup/22-multiplicity-display-rule-calibration.md +25 -0
  162. data/TODO.fixup/23-diagram-frame-title-element.md +31 -0
  163. data/TODO.fixup/24-note-element-rendering.md +23 -0
  164. data/TODO.fixup/25-ea-exe-disassembly-findings.md +177 -0
  165. data/TODO.fixup/26-svg-renderer-architecture.md +78 -0
  166. data/TODO.fixup/27-svg-element-emitters-disassembly.md +135 -0
  167. data/TODO.fixup/28-geometry-parser-identification.md +57 -0
  168. data/TODO.fixup/29-qea-svg-xmi-triple-comparison.md +185 -0
  169. data/TODO.fixup/30-theme-system-stub.md +46 -0
  170. data/TODO.fixup/31-theme-value-object-registry.md +17 -0
  171. data/TODO.fixup/32-styleex-on-diagram-model.md +22 -0
  172. data/TODO.fixup/33-text-encoding-decimals-rotation.md +29 -0
  173. data/TODO.fixup/34-per-type-fill-via-theme.md +18 -0
  174. data/TODO.fixup/35-stroke-width-per-theme.md +7 -0
  175. data/TODO.fixup/36-qea-rect-coord-normalization.md +24 -0
  176. data/TODO.fixup/37-package-shape-rendering.md +22 -0
  177. data/TODO.fixup/38-note-element-rendering.md +15 -0
  178. data/TODO.fixup/39-styleex-flag-honoring.md +25 -0
  179. data/TODO.fixup/40-connector-dashed-lines.md +15 -0
  180. data/TODO.fixup/41-diagram-frame-showborder-detection.md +23 -0
  181. data/TODO.fixup/42-textrenderer-centralization.md +10 -0
  182. data/TODO.fixup/43-colorresolver-unification.md +24 -0
  183. data/TODO.fixup/44-theme-aware-textrenderer.md +17 -0
  184. data/TODO.fixup/45-theme-aware-fontresolver.md +22 -0
  185. data/TODO.fixup/46-style-parameterized-by-theme.md +21 -0
  186. data/TODO.fixup/47-canvas-frame-inset.md +19 -0
  187. data/TODO.fixup/48-background-rect-format.md +15 -0
  188. data/TODO.fixup/49-all-samples-integration-test.md +17 -0
  189. data/TODO.fixup/50-svg-emitter-architecture-docs.md +33 -0
  190. data/TODO.fixup/51-promote-theme-to-ea-namespace.md +28 -0
  191. data/TODO.fixup/52-theme-definition-integrated-fills.md +26 -0
  192. data/TODO.fixup/53-theme-registry-yaml-loading.md +50 -0
  193. data/TODO.fixup/54-diagram-theme-api.md +50 -0
  194. data/TODO.fixup/55-update-eaemitter-to-ea-theme.md +23 -0
  195. data/TODO.fixup/56-theme-yaml-files.md +65 -0
  196. data/TODO.fixup/57-theme-specs-and-integration.md +27 -0
  197. data/TODO.fixup/58-ea-entry-point-theme-autoload.md +20 -0
  198. data/TODO.fixup/59-thread-theme-through-all-renderers.md +29 -0
  199. data/TODO.fixup/60-cli-theme-option.md +22 -0
  200. data/TODO.fixup/61-theme-validation.md +28 -0
  201. data/TODO.fixup/62-theme-builder-dsl.md +31 -0
  202. data/TODO.fixup/63-theme-integration-rendering-spec.md +24 -0
  203. data/TODO.fixup/64-theme-writeback-styleex.md +21 -0
  204. data/TODO.fixup/65-remove-dead-eaemitter-theme-files.md +30 -0
  205. data/TODO.fixup/66-styleex-writeback.md +22 -0
  206. data/TODO.fixup/67-delete-dead-code.md +28 -0
  207. data/TODO.fixup/68-figure-parity-status.md +82 -0
  208. data/TODO.fixup/69-package-polygon-rendering.md +54 -0
  209. data/TODO.fixup/70-tagged-values-compartment.md +39 -0
  210. data/TODO.fixup/71-auto-detect-theme-from-xmi.md +37 -0
  211. data/TODO.fixup/72-sample-exports-as-regression-specs.md +37 -0
  212. data/TODO.fixup/73-delete-dead-transformations.md +14 -0
  213. data/TODO.fixup/74-delete-dead-diagram-renderers.md +18 -0
  214. data/TODO.fixup/75-visibility-legend-icons.md +23 -0
  215. data/TODO.fixup/76-plateau-text-undershoot.md +27 -0
  216. data/TODO.fixup/77-canvas-frame-margins.md +28 -0
  217. data/TODO.fixup/78-note-element-rendering.md +30 -0
  218. data/TODO.fixup/79-operation-compartment-rendering.md +27 -0
  219. data/TODO.fixup/80-tagged-value-stereotype-visibility.md +37 -0
  220. data/TODO.fixup/81-figure-parity-current-state.md +63 -0
  221. data/config/qea_schema.yml +356 -0
  222. data/config/stereotype_colors.yml +15 -0
  223. data/config/themes/119.yml +98 -0
  224. data/config/themes/default.yml +14 -0
  225. data/examples/exports/20251010_current_plateau_v5/UML_EA.DTD +6856 -0
  226. data/examples/exports/20251010_current_plateau_v5/model.xml +96709 -0
  227. data/examples/exports/ArcGISWorkspace_template/UML_EA.DTD +6856 -0
  228. data/examples/exports/ArcGISWorkspace_template/model.xml +1427 -0
  229. data/examples/exports/UmlModel_template/UML_EA.DTD +6856 -0
  230. data/examples/exports/UmlModel_template/model.xml +23 -0
  231. data/examples/exports/basic/UML_EA.DTD +6856 -0
  232. data/examples/exports/basic/model.xml +8039 -0
  233. data/examples/exports/simple/UML_EA.DTD +6856 -0
  234. data/examples/exports/simple/model.xml +585 -0
  235. data/examples/exports/simple_example/UML_EA.DTD +6856 -0
  236. data/examples/exports/simple_example/model.xml +23 -0
  237. data/examples/exports/test/UML_EA.DTD +6856 -0
  238. data/examples/exports/test/model.xml +1638 -0
  239. data/examples/qea/basic.qea +0 -0
  240. data/frontend/README.md +51 -0
  241. data/frontend/dist/app.iife.js +24 -0
  242. data/frontend/dist/style.css +1 -0
  243. data/frontend/index.html +26 -0
  244. data/frontend/package-lock.json +2359 -0
  245. data/frontend/package.json +26 -0
  246. data/frontend/src/App.vue +59 -0
  247. data/frontend/src/app.ts +8 -0
  248. data/frontend/src/components/AppHeader.vue +127 -0
  249. data/frontend/src/components/AppSidebar.vue +119 -0
  250. data/frontend/src/components/ClassDetails.vue +247 -0
  251. data/frontend/src/components/DiagramView.vue +79 -0
  252. data/frontend/src/components/PackageDetails.vue +92 -0
  253. data/frontend/src/components/PackageTreeNode.vue +64 -0
  254. data/frontend/src/components/SearchResults.vue +48 -0
  255. data/frontend/src/components/WelcomeView.vue +87 -0
  256. data/frontend/src/shims-vue.d.ts +5 -0
  257. data/frontend/src/stores/dataStore.ts +79 -0
  258. data/frontend/src/stores/uiStore.ts +167 -0
  259. data/frontend/src/styles/main.css +1566 -0
  260. data/frontend/src/types.ts +221 -0
  261. data/frontend/tests/e2e/__screenshots__/new-dark-mode.png +0 -0
  262. data/frontend/tests/e2e/__screenshots__/new-package-details.png +0 -0
  263. data/frontend/tests/e2e/__screenshots__/new-search-modal.png +0 -0
  264. data/frontend/tests/e2e/__screenshots__/new-sidebar-collapsed.png +0 -0
  265. data/frontend/tests/e2e/__screenshots__/new-welcome.png +0 -0
  266. data/frontend/tests/e2e/render.spec.ts +188 -0
  267. data/frontend/tsconfig.json +20 -0
  268. data/frontend/vite.config.ts +26 -0
  269. data/frontend/vitest.config.ts +8 -0
  270. data/lib/ea/cli/app.rb +62 -1
  271. data/lib/ea/cli/command/diagrams.rb +37 -28
  272. data/lib/ea/cli/command/diff.rb +46 -0
  273. data/lib/ea/cli/command/export.rb +80 -0
  274. data/lib/ea/cli/command/info.rb +75 -0
  275. data/lib/ea/cli/command/lint.rb +48 -0
  276. data/lib/ea/cli/command/mdg.rb +98 -0
  277. data/lib/ea/cli/command/query.rb +34 -0
  278. data/lib/ea/cli/command/render.rb +135 -0
  279. data/lib/ea/cli/command/svg.rb +43 -6
  280. data/lib/ea/cli/command.rb +7 -0
  281. data/lib/ea/diagram/display_config.rb +184 -0
  282. data/lib/ea/diagram.rb +4 -41
  283. data/lib/ea/diff/change.rb +30 -0
  284. data/lib/ea/diff/comparator.rb +82 -0
  285. data/lib/ea/diff/html_reporter.rb +91 -0
  286. data/lib/ea/diff.rb +15 -0
  287. data/lib/ea/export/json/generator.rb +42 -0
  288. data/lib/ea/export/json.rb +12 -0
  289. data/lib/ea/export/json_schema/generator.rb +77 -0
  290. data/lib/ea/export/json_schema.rb +12 -0
  291. data/lib/ea/export/plantuml/generator.rb +73 -0
  292. data/lib/ea/export/plantuml.rb +12 -0
  293. data/lib/ea/export/xmi/generator.rb +77 -0
  294. data/lib/ea/export/xmi.rb +11 -0
  295. data/lib/ea/export/xsd/class_mapping.rb +65 -0
  296. data/lib/ea/export/xsd/generator.rb +142 -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 +14 -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 +35 -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 +53 -1
  333. data/lib/ea/model/diagram_element.rb +44 -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/query/builder.rb +108 -0
  378. data/lib/ea/query.rb +9 -0
  379. data/lib/ea/render/image_converter.rb +101 -0
  380. data/lib/ea/render.rb +10 -0
  381. data/lib/ea/shapescript/parser.rb +208 -0
  382. data/lib/ea/shapescript/renderer.rb +74 -0
  383. data/lib/ea/shapescript/shape.rb +34 -0
  384. data/lib/ea/shapescript.rb +24 -0
  385. data/lib/ea/sources/qea/adapter.rb +22 -5
  386. data/lib/ea/sources/qea/classifier_builder.rb +184 -7
  387. data/lib/ea/sources/qea/diagram_builder.rb +352 -24
  388. data/lib/ea/sources/qea/id_normalizer.rb +16 -1
  389. data/lib/ea/sources/qea/instance_builder.rb +113 -0
  390. data/lib/ea/sources/qea/legend_builder.rb +137 -0
  391. data/lib/ea/sources/qea/note_builder.rb +58 -0
  392. data/lib/ea/sources/qea/object_classifier_map.rb +6 -1
  393. data/lib/ea/sources/qea/operation_builder.rb +1 -0
  394. data/lib/ea/sources/qea/package_builder.rb +16 -0
  395. data/lib/ea/sources/qea/relationship_builder.rb +21 -5
  396. data/lib/ea/sources/qea/tagged_value_builder.rb +28 -9
  397. data/lib/ea/sources/qea/xref/legend_definition.rb +35 -0
  398. data/lib/ea/sources/qea/xref/parser.rb +150 -0
  399. data/lib/ea/sources/qea/xref/property_definition.rb +29 -0
  400. data/lib/ea/sources/qea/xref/record.rb +41 -0
  401. data/lib/ea/sources/qea/xref/stereotype_application.rb +37 -0
  402. data/lib/ea/sources/qea/xref.rb +27 -0
  403. data/lib/ea/sources/qea.rb +4 -0
  404. data/lib/ea/sources/xmi/adapter.rb +80 -2
  405. data/lib/ea/sources/xmi/annotation_builder.rb +1 -1
  406. data/lib/ea/sources/xmi/classifier_builder.rb +10 -1
  407. data/lib/ea/sources/xmi/diagram_builder.rb +242 -45
  408. data/lib/ea/sources/xmi/extension_elements.rb +74 -0
  409. data/lib/ea/sources/xmi/extension_geometry_parser.rb +124 -0
  410. data/lib/ea/sources/xmi/extension_style_parser.rb +91 -0
  411. data/lib/ea/sources/xmi/note_builder.rb +46 -0
  412. data/lib/ea/sources/xmi/property_builder.rb +9 -0
  413. data/lib/ea/sources/xmi/stereotype_builder.rb +54 -0
  414. data/lib/ea/sources/xmi/tag_builder.rb +45 -0
  415. data/lib/ea/sources/xmi/umldi_keyword_extractor.rb +87 -0
  416. data/lib/ea/sources/xmi.rb +7 -0
  417. data/lib/ea/spa/output/sharded_multi_file_strategy.rb +18 -3
  418. data/lib/ea/spa/output/single_file_strategy.rb +8 -2
  419. data/lib/ea/spa/output/strategy.rb +34 -0
  420. data/lib/ea/svg/connector_router.rb +134 -0
  421. data/lib/ea/svg/ea_emitter/background.rb +15 -0
  422. data/lib/ea/svg/ea_emitter/bounds_calculator.rb +142 -0
  423. data/lib/ea/svg/ea_emitter/canvas.rb +88 -0
  424. data/lib/ea/svg/ea_emitter/color_resolver.rb +56 -0
  425. data/lib/ea/svg/ea_emitter/compartment/attributes.rb +28 -0
  426. data/lib/ea/svg/ea_emitter/compartment/constraints.rb +30 -0
  427. data/lib/ea/svg/ea_emitter/compartment/enum_literals.rb +86 -0
  428. data/lib/ea/svg/ea_emitter/compartment/header.rb +32 -0
  429. data/lib/ea/svg/ea_emitter/compartment/header_divider.rb +57 -0
  430. data/lib/ea/svg/ea_emitter/compartment/instance_slots.rb +96 -0
  431. data/lib/ea/svg/ea_emitter/compartment/instance_underline.rb +67 -0
  432. data/lib/ea/svg/ea_emitter/compartment/note_body.rb +52 -0
  433. data/lib/ea/svg/ea_emitter/compartment/note_text.rb +102 -0
  434. data/lib/ea/svg/ea_emitter/compartment/operations.rb +38 -0
  435. data/lib/ea/svg/ea_emitter/compartment/package_contents.rb +72 -0
  436. data/lib/ea/svg/ea_emitter/compartment/package_from_parent.rb +136 -0
  437. data/lib/ea/svg/ea_emitter/compartment/shape.rb +82 -0
  438. data/lib/ea/svg/ea_emitter/compartment/tagged_values.rb +30 -0
  439. data/lib/ea/svg/ea_emitter/compartment.rb +58 -0
  440. data/lib/ea/svg/ea_emitter/connectors.rb +84 -0
  441. data/lib/ea/svg/ea_emitter/diagram_frame.rb +143 -0
  442. data/lib/ea/svg/ea_emitter/document.rb +35 -0
  443. data/lib/ea/svg/ea_emitter/element/attribute_line_builder.rb +117 -0
  444. data/lib/ea/svg/ea_emitter/element/attribute_renderer.rb +119 -0
  445. data/lib/ea/svg/ea_emitter/element/bcol_decoder.rb +29 -0
  446. data/lib/ea/svg/ea_emitter/element/compartment_geometry.rb +113 -0
  447. data/lib/ea/svg/ea_emitter/element/constraint_renderer.rb +53 -0
  448. data/lib/ea/svg/ea_emitter/element/divider_renderer.rb +17 -0
  449. data/lib/ea/svg/ea_emitter/element/enumeration_literal_renderer.rb +34 -0
  450. data/lib/ea/svg/ea_emitter/element/filter.rb +44 -0
  451. data/lib/ea/svg/ea_emitter/element/hand_draw_shape_renderer.rb +77 -0
  452. data/lib/ea/svg/ea_emitter/element/header_line_pipeline.rb +43 -0
  453. data/lib/ea/svg/ea_emitter/element/header_line_provider/instance_spec.rb +27 -0
  454. data/lib/ea/svg/ea_emitter/element/header_line_provider/name.rb +76 -0
  455. data/lib/ea/svg/ea_emitter/element/header_line_provider/parent_ghost.rb +25 -0
  456. data/lib/ea/svg/ea_emitter/element/header_line_provider/stereotype_label.rb +68 -0
  457. data/lib/ea/svg/ea_emitter/element/header_line_provider.rb +19 -0
  458. data/lib/ea/svg/ea_emitter/element/header_lines.rb +27 -0
  459. data/lib/ea/svg/ea_emitter/element/header_renderer.rb +48 -0
  460. data/lib/ea/svg/ea_emitter/element/icon_renderer.rb +109 -0
  461. data/lib/ea/svg/ea_emitter/element/legend_renderer.rb +177 -0
  462. data/lib/ea/svg/ea_emitter/element/note_shape_renderer.rb +112 -0
  463. data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +100 -0
  464. data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +181 -0
  465. data/lib/ea/svg/ea_emitter/element/row_icon_renderer.rb +156 -0
  466. data/lib/ea/svg/ea_emitter/element/shape_renderer.rb +17 -0
  467. data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +82 -0
  468. data/lib/ea/svg/ea_emitter/element/tagged_value_renderer.rb +50 -0
  469. data/lib/ea/svg/ea_emitter/element/text_escape.rb +20 -0
  470. data/lib/ea/svg/ea_emitter/element.rb +37 -0
  471. data/lib/ea/svg/ea_emitter/elements.rb +357 -0
  472. data/lib/ea/svg/ea_emitter/font_resolver.rb +111 -0
  473. data/lib/ea/svg/ea_emitter/ghost_labels.rb +66 -0
  474. data/lib/ea/svg/ea_emitter/label/end_label.rb +266 -0
  475. data/lib/ea/svg/ea_emitter/label/midpoint_label.rb +101 -0
  476. data/lib/ea/svg/ea_emitter/label/registry.rb +50 -0
  477. data/lib/ea/svg/ea_emitter/label.rb +23 -0
  478. data/lib/ea/svg/ea_emitter/labels.rb +130 -0
  479. data/lib/ea/svg/ea_emitter/layer.rb +20 -0
  480. data/lib/ea/svg/ea_emitter/layer_sequencer.rb +93 -0
  481. data/lib/ea/svg/ea_emitter/marker/arrow_path.rb +52 -0
  482. data/lib/ea/svg/ea_emitter/marker/diamond.rb +57 -0
  483. data/lib/ea/svg/ea_emitter/marker/kind.rb +35 -0
  484. data/lib/ea/svg/ea_emitter/marker/open_triangle.rb +27 -0
  485. data/lib/ea/svg/ea_emitter/marker/package_import.rb +39 -0
  486. data/lib/ea/svg/ea_emitter/marker/plus.rb +40 -0
  487. data/lib/ea/svg/ea_emitter/marker/registry.rb +42 -0
  488. data/lib/ea/svg/ea_emitter/marker.rb +37 -0
  489. data/lib/ea/svg/ea_emitter/markers.rb +404 -0
  490. data/lib/ea/svg/ea_emitter/render_context.rb +52 -0
  491. data/lib/ea/svg/ea_emitter/style.rb +15 -0
  492. data/lib/ea/svg/ea_emitter/text_renderer.rb +92 -0
  493. data/lib/ea/svg/ea_emitter.rb +51 -0
  494. data/lib/ea/svg/parity/checker.rb +105 -0
  495. data/lib/ea/svg/parity/source.rb +59 -0
  496. data/lib/ea/svg/parity/suite.rb +113 -0
  497. data/lib/ea/svg/parity.rb +14 -0
  498. data/lib/ea/svg/stereotype_color_resolver.rb +50 -0
  499. data/lib/ea/svg.rb +4 -15
  500. data/lib/ea/theme/definition.rb +176 -0
  501. data/lib/ea/theme/loader.rb +125 -0
  502. data/lib/ea/theme/registry.rb +96 -0
  503. data/lib/ea/theme.rb +37 -0
  504. data/lib/ea/transformations.rb +5 -88
  505. data/lib/ea/version.rb +4 -1
  506. data/lib/ea.rb +14 -4
  507. data/lib/tasks/svg_bench.rake +87 -0
  508. data/lutaml-uml-0.5.1.gem +0 -0
  509. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes-list.png +0 -0
  510. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes.png +0 -0
  511. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-2-gradients.png +0 -0
  512. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-3-colors.png +0 -0
  513. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance-fonts.png +0 -0
  514. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance.png +0 -0
  515. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-5-behavior.png +0 -0
  516. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-6-sequence.png +0 -0
  517. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram.png +0 -0
  518. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-2-objects.png +0 -0
  519. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-3-links.png +0 -0
  520. metadata +473 -26
  521. data/lib/ea/diagram/configuration.rb +0 -379
  522. data/lib/ea/diagram/element_renderers/base_renderer.rb +0 -77
  523. data/lib/ea/diagram/element_renderers/class_renderer.rb +0 -323
  524. data/lib/ea/diagram/element_renderers/connector_renderer.rb +0 -41
  525. data/lib/ea/diagram/element_renderers/package_renderer.rb +0 -61
  526. data/lib/ea/diagram/element_renderers.rb +0 -43
  527. data/lib/ea/diagram/extractor.rb +0 -566
  528. data/lib/ea/diagram/layout_engine.rb +0 -170
  529. data/lib/ea/diagram/path_builder.rb +0 -202
  530. data/lib/ea/diagram/style_parser.rb +0 -42
  531. data/lib/ea/diagram/style_resolver.rb +0 -291
  532. data/lib/ea/diagram/svg_renderer.rb +0 -274
  533. data/lib/ea/diagram/util.rb +0 -73
  534. data/lib/ea/svg/bounds_calculator.rb +0 -74
  535. data/lib/ea/svg/connector_path.rb +0 -138
  536. data/lib/ea/svg/element_box.rb +0 -256
  537. data/lib/ea/svg/renderer.rb +0 -77
  538. data/lib/ea/svg/style_resolver.rb +0 -72
  539. data/lib/ea/transformations/configuration.rb +0 -333
  540. data/lib/ea/transformations/format_registry.rb +0 -366
  541. data/lib/ea/transformations/parsers/base_parser.rb +0 -482
  542. data/lib/ea/transformations/parsers/qea_parser.rb +0 -401
  543. data/lib/ea/transformations/parsers/xmi_parser.rb +0 -243
  544. data/lib/ea/transformations/transformation_engine.rb +0 -390
@@ -5,7 +5,12 @@ module Ea
5
5
  module Qea
6
6
  # Normalizes EA's identifier formats into stable strings used
7
7
  # as Ea::Model element ids. EA represents identity as
8
- # `{GUID}` strings. We strip the braces to give the bare GUID.
8
+ # `{GUID}` strings.
9
+ #
10
+ # Two output formats:
11
+ # - `from_guid` — bare GUID without braces (canonical model id)
12
+ # - `to_eaid` — `EAID_<guid with dashes-to-underscores>` matching
13
+ # the reference SVG filename convention
9
14
  module IdNormalizer
10
15
  module_function
11
16
 
@@ -15,6 +20,15 @@ module Ea
15
20
  ea_guid.to_s.gsub(/[{}]/, "")
16
21
  end
17
22
 
23
+ # Convert a GUID to the EAID_ filename format used by EA's
24
+ # SVG export: strip braces, replace dashes with underscores,
25
+ # prefix with EAID_.
26
+ def to_eaid(ea_guid)
27
+ return nil if ea_guid.nil? || ea_guid.empty?
28
+
29
+ "EAID_" + ea_guid.to_s.gsub(/[{}]/, "").tr("-", "_")
30
+ end
31
+
18
32
  def synthetic(prefix, *parts)
19
33
  "#{prefix}:#{parts.join(":")}"
20
34
  end
@@ -22,3 +36,4 @@ module Ea
22
36
  end
23
37
  end
24
38
  end
39
+
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ # Translates EA t_object rows of Object_Type="Object" into
7
+ # Ea::Model::InstanceSpecification instances. Slots are parsed
8
+ # from the RunState field, which uses a packed format:
9
+ #
10
+ # @VAR;Variable=name;Value=val;Op===;@ENDVAR;@VAR;...
11
+ #
12
+ # Each @VAR...@ENDVAR block is one Slot.
13
+ class InstanceBuilder
14
+ RUN_STATE_PATTERN = /
15
+ @VAR;
16
+ (?:Variable=(?<name>[^;]*);)?
17
+ (?:Value=(?<value>[^;]*);)?
18
+ (?:Op=(?<op>[^;]*);)?
19
+ @ENDVAR;
20
+ /x.freeze
21
+
22
+ attr_reader :database
23
+
24
+ def initialize(database)
25
+ @database = database
26
+ end
27
+
28
+ def build_all
29
+ objects = database.collections[:objects] || []
30
+ objects.filter_map { |row| build_one(row) if instance?(row) }
31
+ end
32
+
33
+ def build_one(row)
34
+ Ea::Model::InstanceSpecification.new(
35
+ id: IdNormalizer.from_guid(row.ea_guid),
36
+ name: row.name.to_s,
37
+ classifier_id: classifier_id_for(row),
38
+ classifier_name: classifier_name_for(row),
39
+ package_id: package_id_for(row),
40
+ package_name: package_name_for(row),
41
+ qualified_name: qualified_name_for(row),
42
+ slots: slots_for(row),
43
+ annotations: AnnotationBuilder.from_note(row.note, row.ea_guid,
44
+ kind: "documentation")
45
+ )
46
+ end
47
+
48
+ private
49
+
50
+ def instance?(row)
51
+ row.object_type == "Object"
52
+ end
53
+
54
+ def classifier_id_for(row)
55
+ classifier = database.find_object(row.classifier.to_i) if row.classifier&.to_i&.positive?
56
+ return nil unless classifier
57
+
58
+ IdNormalizer.from_guid(classifier.ea_guid)
59
+ end
60
+
61
+ def classifier_name_for(row)
62
+ classifier = database.find_object(row.classifier.to_i) if row.classifier&.to_i&.positive?
63
+ return nil unless classifier
64
+
65
+ classifier.name.to_s
66
+ end
67
+
68
+ def package_id_for(row)
69
+ return nil unless row.package_id&.to_i&.positive?
70
+
71
+ pkg = database.find_package(row.package_id.to_i)
72
+ return nil unless pkg
73
+
74
+ IdNormalizer.from_guid(pkg.ea_guid)
75
+ end
76
+
77
+ def package_name_for(row)
78
+ return nil unless row.package_id&.to_i&.positive?
79
+
80
+ pkg = database.find_package(row.package_id.to_i)
81
+ pkg&.name
82
+ end
83
+
84
+ # EA's t_object.Name may carry a qualified name with a ":"
85
+ # prefix (e.g. "gml:CodeType"). When the instance is in a
86
+ # nested package the qualified name uses the package path.
87
+ def qualified_name_for(row)
88
+ name = row.name.to_s
89
+ return name if name.empty?
90
+
91
+ pkg_name = package_name_for(row)
92
+ pkg_name ? "#{pkg_name}::#{name}" : name
93
+ end
94
+
95
+ # Parse the RunState field into Slot instances. Each
96
+ # @VAR;Variable=X;Value=Y;Op=Z;@ENDVAR; is one slot.
97
+ # When Op is absent we default to "=".
98
+ def slots_for(row)
99
+ run_state = row.runstate.to_s
100
+ return [] if run_state.empty?
101
+
102
+ run_state.scan(RUN_STATE_PATTERN).map do |(name, value, op)|
103
+ Ea::Model::Slot.new(
104
+ name: name.to_s,
105
+ value: value.to_s,
106
+ op: (op.to_s.empty? ? "=" : op)
107
+ )
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ # Builds an `Ea::Model::Legend` for a Text element by parsing
7
+ # the sibling `t_xref` row of name="CustomProperties" on the
8
+ # same GUID. EA encodes legend configuration in the xref's
9
+ # Description field using `@PROP=...@ENDPROP;` blocks, one
10
+ # per item plus one for the title (`LEGEND_STYLE_SETTINGS`).
11
+ #
12
+ # Returns nil when no matching xref exists (the Text element
13
+ # is not a legend — caller should render it as a plain note).
14
+ class LegendBuilder
15
+ LEGEND_XREF_NAME = "CustomProperties"
16
+ LEGEND_TYPE_OBJECT_STYLE = "LEGEND_OBJECTSTYLE"
17
+ LEGEND_TYPE_STYLE_SETTINGS = "LEGEND_STYLE_SETTINGS"
18
+
19
+ PROP_PATTERN = /
20
+ @PROP=
21
+ @NAME=(?<name>[^@]*?)@ENDNAME;
22
+ @TYPE=(?<type>[^@]*?)@ENDTYPE;
23
+ @VALU=(?<value>[^@]*?)@ENDVALU;
24
+ (?:@PRMT=(?<prmt>[^@]*?)@ENDPRMT;)?
25
+ @ENDPROP;
26
+ /x.freeze
27
+
28
+ attr_reader :database
29
+
30
+ def initialize(database)
31
+ @database = database
32
+ end
33
+
34
+ def build_for(ea_guid)
35
+ description = legend_description_for(ea_guid)
36
+ return nil unless description
37
+
38
+ build_from_description(description)
39
+ end
40
+
41
+ private
42
+
43
+ def legend_description_for(ea_guid)
44
+ xref = database.xrefs_for_client(ea_guid).find { |row| row.name == LEGEND_XREF_NAME }
45
+ return nil unless xref
46
+
47
+ xref.description
48
+ end
49
+
50
+ def build_from_description(description)
51
+ props = description.scan(PROP_PATTERN)
52
+ return nil if props.empty?
53
+
54
+ items = items_from(props)
55
+ style = style_settings_from(props)
56
+
57
+ build_legend(items: items, style: style)
58
+ end
59
+
60
+ def items_from(props)
61
+ props.select { |_, type, _, _| type == LEGEND_TYPE_OBJECT_STYLE }
62
+ .each_with_index
63
+ .map do |(name, _, value, prmt), display_index|
64
+ build_item(name: name, value: value, prmt: prmt,
65
+ display_index: display_index)
66
+ end
67
+ end
68
+
69
+ def style_settings_from(props)
70
+ entry = props.find { |_, type, _, _| type == LEGEND_TYPE_STYLE_SETTINGS }
71
+ return {} unless entry
72
+
73
+ parse_kv(entry[2])
74
+ end
75
+
76
+ def build_legend(items:, style:)
77
+ Ea::Model::Legend.new(
78
+ title: style["TITLE"] || Ea::Model::Legend::DEFAULT_TITLE,
79
+ heading_size: int_or_nil(style["HEADINGSIZE"]),
80
+ font_color: int_or(style["FONTCOLOR"],
81
+ Ea::Model::Legend::DEFAULT_FONT_COLOR),
82
+ background_color: int_or(style["BACKGROUNDCOLOR"],
83
+ Ea::Model::Legend::DEFAULT_BACKGROUND_COLOR),
84
+ border_color: int_or(style["BORDERCOLOR"],
85
+ Ea::Model::Legend::DEFAULT_BORDER_COLOR),
86
+ background_is_default: background_is_default?(style),
87
+ items: items
88
+ )
89
+ end
90
+
91
+ def build_item(name:, value:, prmt:, display_index:)
92
+ attrs = parse_kv(value)
93
+ Ea::Model::LegendItem.new(
94
+ name: name.to_s,
95
+ background_color: int_or(attrs["Back_Ground_Color"], 0),
96
+ pen_color: int_or(attrs["Pen_Color"], 0),
97
+ pen_size: int_or(attrs["Pen_Size"], 1),
98
+ sort_index: int_or(prmt, display_index)
99
+ )
100
+ end
101
+
102
+ # EA packs key=value pairs as `#KEY#=VAL;` inside the @VALU
103
+ # payload. Both KEY and VAL are delimited by `#` or `;`.
104
+ def parse_kv(payload)
105
+ return {} if payload.nil? || payload.empty?
106
+
107
+ payload.to_s.split(";").each_with_object({}) do |pair, hash|
108
+ key, val = pair.split("=", 2)
109
+ next unless key && val
110
+ next if val.empty?
111
+
112
+ hash[key.delete("#")] = val
113
+ end
114
+ end
115
+
116
+ def background_is_default?(style)
117
+ value = style["BACKGROUNDISDEFAULT"]
118
+ return true if value.nil?
119
+
120
+ value == "1" || value.to_s.downcase == "true"
121
+ end
122
+
123
+ def int_or(raw, fallback)
124
+ Integer(raw.to_s)
125
+ rescue ArgumentError, TypeError
126
+ fallback
127
+ end
128
+
129
+ def int_or_nil(raw)
130
+ Integer(raw.to_s)
131
+ rescue ArgumentError, TypeError
132
+ nil
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ # Extracts Note elements from EA's t_object table. Notes have
7
+ # object_type="Note" and are filtered out by ClassifierBuilder.
8
+ # Their body text lives in t_object.Note; the diagram renderer
9
+ # emits them as dog-eared rects with the body wrapped inside.
10
+ #
11
+ # Text elements (object_type="Text") with LegendOpts= in
12
+ # StyleEx are auto-generated legend blocks rather than notes;
13
+ # their configuration is parsed from t_xref and attached as
14
+ # an `Ea::Model::Legend` payload on the Note.
15
+ class NoteBuilder
16
+ NOTE_OBJECT_TYPE = "Note".freeze
17
+ TEXT_OBJECT_TYPE = "Text".freeze
18
+ LEGEND_STYLEEX_TOKEN = "LegendOpts=".freeze
19
+
20
+ attr_reader :database
21
+
22
+ def initialize(database)
23
+ @database = database
24
+ end
25
+
26
+ def build_all
27
+ objects = database.collections[:objects] || []
28
+ objects.filter_map { |obj| build_one(obj) }
29
+ end
30
+
31
+ private
32
+
33
+ def build_one(obj)
34
+ return nil unless note_type?(obj.object_type)
35
+
36
+ Ea::Model::Note.new(
37
+ id: IdNormalizer.from_guid(obj.ea_guid),
38
+ name: obj.name,
39
+ body: obj.note,
40
+ note_type: obj.object_type,
41
+ legend: legend_for(obj)
42
+ )
43
+ end
44
+
45
+ def note_type?(object_type)
46
+ object_type == NOTE_OBJECT_TYPE || object_type == TEXT_OBJECT_TYPE
47
+ end
48
+
49
+ def legend_for(obj)
50
+ return nil unless obj.object_type == TEXT_OBJECT_TYPE
51
+ return nil unless obj.styleex.to_s.include?(LEGEND_STYLEEX_TOKEN)
52
+
53
+ LegendBuilder.new(database).build_for(obj.ea_guid)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -18,7 +18,12 @@ module Ea
18
18
  "Enumeration" => "Ea::Model::Enumeration",
19
19
  "Interface" => "Ea::Model::Interface",
20
20
  "PrimitiveType" => "Ea::Model::PrimitiveType",
21
- "Primitive" => "Ea::Model::PrimitiveType"
21
+ "Primitive" => "Ea::Model::PrimitiveType",
22
+ "Signal" => "Ea::Model::Signal",
23
+ # EA "Object" rows are UML InstanceSpecifications. We
24
+ # don't yet model InstanceSpecification as a distinct
25
+ # class — they render like a simple named Klass.
26
+ "Object" => "Ea::Model::Klass"
22
27
  }.freeze
23
28
 
24
29
  def class_for(object_type)
@@ -27,6 +27,7 @@ module Ea
27
27
  visibility: visibility_from_scope(row.scope),
28
28
  is_static: boolean(row.isstatic),
29
29
  is_abstract: boolean(row.abstract),
30
+ is_reception: row.reception?,
30
31
  parameters: build_parameters(row),
31
32
  annotations: AnnotationBuilder.from_note(row.notes, row.ea_guid,
32
33
  kind: "documentation")
@@ -24,6 +24,7 @@ module Ea
24
24
  name: pkg.name,
25
25
  parent_id: parent_id_for(pkg),
26
26
  qualified_name: pkg.xmlpath,
27
+ stereotype_refs: stereotype_refs_for(pkg),
27
28
  annotations: AnnotationBuilder.from_note(pkg.notes, pkg.ea_guid)
28
29
  )
29
30
  end
@@ -38,6 +39,21 @@ module Ea
38
39
 
39
40
  IdNormalizer.from_guid(parent.ea_guid)
40
41
  end
42
+
43
+ # EA stores the package's applied stereotype on the t_object
44
+ # row that mirrors the t_package entry (matched by ea_guid).
45
+ # t_package itself has no Stereotype column — only t_object
46
+ # does. Returns [] when no t_object row exists or no
47
+ # stereotype is set.
48
+ def stereotype_refs_for(pkg)
49
+ obj = database.find_object_by_guid(pkg.ea_guid)
50
+ return [] unless obj
51
+
52
+ stereotype = obj.stereotype
53
+ return [] if stereotype.nil? || stereotype.to_s.empty?
54
+
55
+ [stereotype.to_s]
56
+ end
41
57
  end
42
58
  end
43
59
  end
@@ -22,6 +22,7 @@ module Ea
22
22
  klass = ConnectorRelationshipMap.class_for(row.connector_type)
23
23
  args = common_args(row)
24
24
  args.merge!(association_args(row)) if klass == Ea::Model::Association
25
+ args.merge!(generalization_args(row)) if klass == Ea::Model::Generalization
25
26
  klass.new(**args)
26
27
  end
27
28
 
@@ -32,11 +33,23 @@ module Ea
32
33
  id: IdNormalizer.from_guid(row.ea_guid),
33
34
  name: row.name,
34
35
  qualified_name: row.name,
36
+ stereotype: row.stereotype,
35
37
  annotations: AnnotationBuilder.from_note(row.notes, row.ea_guid,
36
38
  kind: "documentation")
37
39
  }
38
40
  end
39
41
 
42
+ # EA's Generalization connector points from the specific
43
+ # (child) to the general (parent): Start_Object_ID is the
44
+ # child, End_Object_ID is the parent. Direction is
45
+ # conventionally "Source -> Destination".
46
+ def generalization_args(row)
47
+ {
48
+ specific_id: id_for_object_id(row.start_object_id),
49
+ general_id: id_for_object_id(row.end_object_id)
50
+ }
51
+ end
52
+
40
53
  def association_args(row)
41
54
  {
42
55
  source_id: id_for_object_id(row.start_object_id),
@@ -63,19 +76,22 @@ module Ea
63
76
  IdNormalizer.from_guid(obj.ea_guid)
64
77
  end
65
78
 
79
+ # Returns nil when cardinality is absent so the renderer can
80
+ # distinguish "no multiplicity specified" from an explicit
81
+ # "1". EA only renders the "1" label when the modeller set it
82
+ # explicitly (SourceCard/DestCard = "1").
66
83
  def parse_lower(cardinality)
67
- return 1 if cardinality.nil? || cardinality.empty?
84
+ return nil if cardinality.nil? || cardinality.empty?
68
85
 
69
- # EA format: "1", "0..*", "1..1", etc. Take the first number.
70
86
  begin
71
87
  Integer(cardinality.split("..").first)
72
88
  rescue StandardError
73
- 1
89
+ nil
74
90
  end
75
91
  end
76
92
 
77
93
  def parse_upper(cardinality)
78
- return 1 if cardinality.nil? || cardinality.empty?
94
+ return nil if cardinality.nil? || cardinality.empty?
79
95
 
80
96
  upper = cardinality.split("..").last
81
97
  return -1 if upper == "*"
@@ -83,7 +99,7 @@ module Ea
83
99
  begin
84
100
  Integer(upper)
85
101
  rescue StandardError
86
- 1
102
+ nil
87
103
  end
88
104
  end
89
105
 
@@ -4,9 +4,11 @@ module Ea
4
4
  module Sources
5
5
  module Qea
6
6
  # Builds Ea::Model::TaggedValue instances from EA's
7
- # t_objectproperties (and t_attributetags for attribute-level
8
- # tagged values). Each row is one key/value pair; we keep the
9
- # optional stereotype scope if EA recorded one.
7
+ # t_taggedvalue AND t_objectproperties tables. EA stores UML
8
+ # profile tagged values in t_taggedvalue (keyed by ElementID
9
+ # GUID), and per-object custom properties in t_objectproperties
10
+ # (keyed by Object_ID integer). Both surface as tagged values
11
+ # in the SVG output and merge into one list per classifier.
10
12
  class TaggedValueBuilder
11
13
  attr_reader :database
12
14
 
@@ -14,21 +16,38 @@ module Ea
14
16
  @database = database
15
17
  end
16
18
 
17
- def for_object(ea_guid)
19
+ # Build tagged values for an EA object. Pass both the
20
+ # GUID (for t_taggedvalue lookups) and the integer EA
21
+ # Object_ID (for t_objectproperties lookups).
22
+ def for_object(ea_guid, ea_object_id: nil)
18
23
  rows = database.tagged_values_for_element(ea_guid) || []
19
- rows.map { |row| build_from_row(row, ea_guid) }
24
+ tv = rows.map { |row| build_from_tagged_value(row, ea_guid) }
25
+ return tv unless ea_object_id
26
+
27
+ prop_rows = database.properties_for_object(ea_object_id) || []
28
+ tv + prop_rows.map { |row| build_from_object_property(row) }
20
29
  end
21
30
 
22
31
  def for_attribute(attribute_row)
23
- return [] unless attribute_row.respond_to?(:ea_guid)
32
+ return [] unless attribute_row.is_a?(Ea::Qea::Models::EaAttribute)
24
33
 
25
34
  rows = database.tagged_values_for_element(attribute_row.ea_guid) || []
26
- rows.map { |row| build_from_row(row, attribute_row.ea_guid) }
35
+ rows.map { |row| build_from_tagged_value(row, attribute_row.ea_guid) }
36
+ end
37
+
38
+ def build_from_tagged_value(row, owner_guid)
39
+ Ea::Model::TaggedValue.new(
40
+ id: IdNormalizer.synthetic("tv", owner_guid,
41
+ row.tag&.gsub(/\W+/, "_")),
42
+ key: row.tag,
43
+ value: row.value
44
+ )
27
45
  end
28
46
 
29
- def build_from_row(row, owner_guid)
47
+ def build_from_object_property(row)
30
48
  Ea::Model::TaggedValue.new(
31
- id: IdNormalizer.synthetic("tv", owner_guid, row.property&.gsub(/\W+/, "_")),
49
+ id: IdNormalizer.synthetic("op", row.ea_object_id.to_s,
50
+ row.property&.gsub(/\W+/, "_")),
32
51
  key: row.property,
33
52
  value: row.value
34
53
  )
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ module Xref
7
+ # Legend definition record. Produced when the Description contains
8
+ # `@PROP=` blocks with `@TYPE=LEGEND_*`. EA encodes legend
9
+ # entries as color/style rules keyed by name.
10
+ #
11
+ # Example source:
12
+ # @PROP=@NAME=GMLに定義されたクラス@ENDNAME;
13
+ # @TYPE=LEGEND_OBJECTSTYLE@ENDTYPE;
14
+ # @VALU=#Back_Ground_Color#=13434828;#Pen_Color#=0;
15
+ # #Pen_Size#=1;#Legend_Type#=LEGEND_OBJECTSTYLE;
16
+ # @ENDVALU;@PRMT=0@ENDPRMT;@ENDPROP;
17
+ LegendDefinition = Struct.new(:name, :legend_type, :colors,
18
+ :parameter, keyword_init: true) do
19
+ # EA encodes colors as signed 32-bit integers (VB-style).
20
+ # Convert to `#RRGGBB` hex string for SVG consumption.
21
+ # @param key [Symbol] color key, e.g. :back_ground_color
22
+ # @return [String, nil] hex color (`#RRGGBB`) or nil
23
+ def hex_color(key)
24
+ value = colors&.dig(key)
25
+ return nil if value.nil?
26
+
27
+ int = value.to_i
28
+ int = (int + 2**32) % 2**32 if int.negative?
29
+ format("#%06X", int & 0xFFFFFF)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end