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,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
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ module Xref
7
+ # Pure parser for EA's `t_xref.Description` mini-language.
8
+ # Stateless — every call returns a fresh `Xref::Record`.
9
+ #
10
+ # Supported formats:
11
+ # @STEREO;Name=X;FQName=Y;@ENDSTEREO;
12
+ # @PROP=@NAME=..@ENDNAME;@TYPE=..@ENDTYPE;@VALU=..@ENDVALU;@PRMT=..@ENDPRMT;@ENDPROP;
13
+ # <key>=<value>;<key>=<value>;
14
+ #
15
+ # Unknown formats fall through to key=value parsing.
16
+ module Parser
17
+ module_function
18
+
19
+ # Parse one Description string into a typed Record.
20
+ # @param description [String, nil]
21
+ # @return [Ea::Sources::Qea::Xref::Record]
22
+ def parse(description)
23
+ return Record.new if description.nil? || description.empty?
24
+
25
+ Record.new(
26
+ stereotype: parse_stereotype(description),
27
+ properties: parse_properties(description),
28
+ legends: parse_legends(description),
29
+ key_values: parse_key_values(description),
30
+ raw: description
31
+ )
32
+ end
33
+
34
+ # -- per-format extractors ----------------------------------
35
+
36
+ # Matches both legacy `@STEREO;Name=X;GUID={..};` and the
37
+ # plateau form `@STEREO;Name=X;FQName=Y;@ENDSTEREO;`.
38
+ STEREO_REGEX = /
39
+ @STEREO;
40
+ (.*?)
41
+ (?:@ENDSTEREO;|\z)
42
+ /mx.freeze
43
+
44
+ def parse_stereotype(description)
45
+ match = description.match(STEREO_REGEX)
46
+ return nil unless match
47
+
48
+ fields = parse_semicolon_fields(match[1])
49
+ StereotypeApplication.new(
50
+ name: fields["Name"],
51
+ fqname: fields["FQName"]
52
+ )
53
+ end
54
+
55
+ # One Description can carry multiple @PROP=...@ENDPROP; blocks.
56
+ # Split, then classify each as PropertyDefinition or LegendDefinition.
57
+ PROP_REGEX = /
58
+ @PROP=
59
+ (.*?)
60
+ @ENDPROP;
61
+ /mx.freeze
62
+
63
+ def parse_properties(description)
64
+ blocks = description.scan(PROP_REGEX).flatten
65
+ blocks.each_with_object([]) do |block, acc|
66
+ record = build_prop_record(block)
67
+ acc << record if record
68
+ end
69
+ end
70
+
71
+ def parse_legends(description)
72
+ parse_properties(description).select do |prop|
73
+ prop.is_a?(LegendDefinition)
74
+ end
75
+ end
76
+
77
+ def build_prop_record(block)
78
+ name = extract_tagged_value(block, "NAME")
79
+ type = extract_tagged_value(block, "TYPE")
80
+ value = extract_tagged_value(block, "VALU")
81
+ parameter = extract_tagged_value(block, "PRMT")
82
+
83
+ if type&.start_with?("LEGEND_")
84
+ LegendDefinition.new(
85
+ name: name,
86
+ legend_type: type,
87
+ colors: parse_color_value(value),
88
+ parameter: parameter
89
+ )
90
+ else
91
+ PropertyDefinition.new(
92
+ name: name,
93
+ type: type,
94
+ value: value,
95
+ parameter: parameter
96
+ )
97
+ end
98
+ end
99
+
100
+ # Extracts the content between @TAG=...@ENDTAG; markers.
101
+ # Returns nil when the tag is absent.
102
+ def extract_tagged_value(block, tag)
103
+ pattern = /@#{tag}=(.*?)@END#{tag};/m.freeze
104
+ match = block.match(pattern)
105
+ match ? match[1] : nil
106
+ end
107
+
108
+ # EA encodes legend VALU as `#key#=value;` pairs.
109
+ # Returns a hash keyed by underscored symbol.
110
+ def parse_color_value(value)
111
+ return {} if value.nil? || value.empty?
112
+
113
+ value.scan(/#([^#=]+)#=([^;]*);/).each_with_object({}) do |(k, v), acc|
114
+ acc[k.downcase.to_sym] = v
115
+ end
116
+ end
117
+
118
+ # Legacy `Key=Value;Key=Value;` parsing. Skips content that
119
+ # looks like @STEREO or @PROP blocks to avoid noise.
120
+ def parse_key_values(description)
121
+ stripped = description
122
+ .gsub(/@STEREO;.*?(?:@ENDSTEREO;|\z)/m, "")
123
+ .gsub(/@PROP=.*?@ENDPROP;/m, "")
124
+ pairs = stripped.split(";").map { |s| s.split("=", 2) }
125
+ pairs.each_with_object({}) do |(k, v), acc|
126
+ next if k.nil? || k.empty? || v.nil?
127
+
128
+ acc[k.downcase.to_sym] = v
129
+ end
130
+ end
131
+
132
+ # Parse `Key=Value;Key=value;` into a string-keyed hash.
133
+ # Used internally for the @STEREO body fields.
134
+ def parse_semicolon_fields(body)
135
+ body.split(";").each_with_object({}) do |part, acc|
136
+ key, value = part.split("=", 2)
137
+ next if key.nil? || key.empty? || value.nil?
138
+
139
+ # Strip @END* terminators that may be attached when the
140
+ # body wasn't cleanly split (defensive — usually already
141
+ # consumed by STEREO_REGEX).
142
+ cleaned = value.sub(/@END\w+\z/, "")
143
+ acc[key] = cleaned
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ module Xref
7
+ # Tagged-value / property definition record. Produced when the
8
+ # Description contains one or more `@PROP=@NAME=..@ENDNAME;`
9
+ # blocks for non-legend properties.
10
+ #
11
+ # Example source:
12
+ # @PROP=@NAME=isActive@ENDNAME;@TYPE=Boolean@ENDTYPE;
13
+ # @VALU=@ENDVALU;@PRMT=@ENDPRMT;@ENDPROP;
14
+ PropertyDefinition = Struct.new(:name, :type, :value, :parameter,
15
+ keyword_init: true) do
16
+ # @return [Boolean] true when the value field is non-empty
17
+ def default_value?
18
+ !value.nil? && !value.empty?
19
+ end
20
+
21
+ # @return [Boolean] true when the property is declared as Boolean
22
+ def boolean?
23
+ type == "Boolean"
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ module Xref
7
+ # Parsed t_xref.Description. Holds typed records for each block
8
+ # found in the source string. One Description can carry multiple
9
+ # records (e.g., several `@PROP=` blocks defining a stereotype's
10
+ # tagged-value set).
11
+ #
12
+ # Consumers branch on record type without re-parsing strings.
13
+ Record = Struct.new(:stereotype, :properties, :legends,
14
+ :key_values, :raw, keyword_init: true) do
15
+ def initialize(*)
16
+ super
17
+ self.properties ||= []
18
+ self.legends ||= []
19
+ self.key_values ||= {}
20
+ end
21
+
22
+ # @return [Boolean] true when a stereotype application is present
23
+ def stereotype?
24
+ !stereotype.nil?
25
+ end
26
+
27
+ # @return [Boolean] true when at least one legend is present
28
+ def legend?
29
+ !legends.empty?
30
+ end
31
+
32
+ # Convenience: first legend's name (the legend's title).
33
+ # @return [String, nil]
34
+ def legend_title
35
+ legends.first&.name
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ module Xref
7
+ # Stereotype application record. Produced when the Description
8
+ # starts with `@STEREO;`. Captures the short name and the
9
+ # fully-qualified `tech::Stereo` form when present.
10
+ #
11
+ # Example source:
12
+ # @STEREO;Name=FeatureType;FQName=GML::FeatureType;@ENDSTEREO;
13
+ StereotypeApplication = Struct.new(:name, :fqname, keyword_init: true) do
14
+ # Returns the technology prefix from FQName, or nil.
15
+ # @return [String, nil]
16
+ def technology
17
+ return nil unless fqname
18
+
19
+ idx = fqname.index("::")
20
+ idx ? fqname[0, idx] : nil
21
+ end
22
+
23
+ # Returns the stereotype name without technology prefix.
24
+ # Falls back to +name+ when FQName is absent.
25
+ # @return [String, nil]
26
+ def unqualified_name
27
+ if fqname && fqname.include?("::")
28
+ fqname.split("::", 2).last
29
+ else
30
+ name
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end