ea 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (529) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +3 -0
  3. data/TODO.complete/01-t-xref-parsing.md +91 -0
  4. data/TODO.complete/02-per-label-geometry.md +50 -0
  5. data/TODO.complete/03-object-properties-constraints.md +57 -0
  6. data/TODO.complete/04-operation-params.md +37 -0
  7. data/TODO.complete/05-pdata-flag-matrix.md +52 -0
  8. data/TODO.complete/06-emf-image-rendering.md +34 -0
  9. data/TODO.complete/07-auxiliary-tables.md +47 -0
  10. data/TODO.complete/10-mdg-stereotype-registry.md +40 -0
  11. data/TODO.complete/11-gml-xsd-generation.md +42 -0
  12. data/TODO.complete/13-shapescript-interpreter.md +28 -0
  13. data/TODO.complete/20-cli-diff.md +46 -0
  14. data/TODO.complete/21-cli-render.md +26 -0
  15. data/TODO.complete/22-cli-export.md +29 -0
  16. data/TODO.complete/23-cli-mdg.md +23 -0
  17. data/TODO.complete/30-headerlines-ocp-refactor.md +72 -0
  18. data/TODO.complete/31-public-send-cleanup.md +61 -0
  19. data/TODO.complete/32-spec-coverage.md +33 -0
  20. data/TODO.complete/33-blocked-parity-gaps.md +41 -0
  21. data/TODO.complete/36-stereotype-icon-wiring.md +32 -0
  22. data/TODO.complete/37-cli-lint.md +32 -0
  23. data/TODO.complete/38-cli-query.md +26 -0
  24. data/TODO.complete/39-cli-info.md +23 -0
  25. data/TODO.complete/40-ocl-evaluator.md +31 -0
  26. data/TODO.complete/41-round-trip-spec.md +18 -0
  27. data/TODO.complete/42-html-diff-report.md +18 -0
  28. data/TODO.complete/43-json-schema-export.md +20 -0
  29. data/TODO.complete/44-remaining-qea-tables.md +40 -0
  30. data/TODO.complete/45-shapescript-extensions.md +25 -0
  31. data/TODO.complete/46-xsd-consolidation.md +20 -0
  32. data/TODO.complete/47-headerlines-legacy-removal.md +20 -0
  33. data/TODO.complete/STATUS.md +109 -0
  34. data/TODO.diagrams/01-svg-measurement-harness.md +31 -0
  35. data/TODO.diagrams/02-polygon-over-rendering.md +29 -0
  36. data/TODO.diagrams/03-text-over-rendering.md +30 -0
  37. data/TODO.diagrams/04-rect-under-rendering.md +33 -0
  38. data/TODO.diagrams/05-canvas-dimension-parity.md +51 -0
  39. data/TODO.diagrams/06-group-layering-parity.md +39 -0
  40. data/TODO.diagrams/07-text-content-fidelity.md +65 -0
  41. data/TODO.diagrams/08-qea-direct-pipeline.md +46 -0
  42. data/TODO.diagrams/12-connector-labels.md +40 -0
  43. data/TODO.diagrams/12-per-entity-svg-layer-ordering.md +24 -0
  44. data/TODO.diagrams/13-marker-polygon-vs-path-dispatch.md +20 -0
  45. data/TODO.diagrams/13-per-text-type-font-sizes.md +33 -0
  46. data/TODO.diagrams/14-center-header-text.md +33 -0
  47. data/TODO.diagrams/14-filter-diagram-frame-elements.md +18 -0
  48. data/TODO.diagrams/15-deep-dead-code-audit.md +47 -0
  49. data/TODO.diagrams/15-per-element-font-from-style.md +24 -0
  50. data/TODO.diagrams/16-canvas-marker-extent.md +30 -0
  51. data/TODO.diagrams/16-document-remaining-parity-gaps.md +91 -0
  52. data/TODO.diagrams/17-italic-abstract-classes.md +28 -0
  53. data/TODO.diagrams/17-remaining-parity-gaps.md +90 -0
  54. data/TODO.diagrams/18-canvas-from-image-bounds.md +20 -0
  55. data/TODO.diagrams/18-property-lookup-index.md +42 -0
  56. data/TODO.diagrams/19-connector-sx-sy-routing.md +27 -0
  57. data/TODO.diagrams/19-replace-respond-to-with-is-a.md +31 -0
  58. data/TODO.diagrams/20-split-labels-emitter.md +56 -0
  59. data/TODO.diagrams/20-visibility-glyph-font-encoding.md +27 -0
  60. data/TODO.diagrams/21-aggregation-navigability-arrow.md +21 -0
  61. data/TODO.diagrams/21-auto-generated-legend-block.md +65 -0
  62. data/TODO.diagrams/22-ghost-element-discriminator.md +57 -0
  63. data/TODO.diagrams/22-style-based-path-grouping.md +28 -0
  64. data/TODO.diagrams/23-marker-polygon-deduplication.md +47 -0
  65. data/TODO.diagrams/23-multiplicity-label-positioning.md +29 -0
  66. data/TODO.diagrams/24-marker-anchor-structural.md +35 -0
  67. data/TODO.diagrams/24-track-marker-anchors-structurally.md +66 -0
  68. data/TODO.diagrams/24-truncate-properties-to-fit-box.md +24 -0
  69. data/TODO.diagrams/26-phantom-general-parent-rendering.md +59 -0
  70. data/TODO.diagrams/27-path-grouping-mode-detection.md +43 -0
  71. data/TODO.diagrams/28-architecture-cleanup-batch.md +47 -0
  72. data/TODO.diagrams/30-decompose-elements-groups-for.md +52 -0
  73. data/TODO.diagrams/32-examples-coverage-batch.md +78 -0
  74. data/TODO.diagrams/36-instance-specification-model.md +42 -0
  75. data/TODO.diagrams/37-element-icon-decorations.md +41 -0
  76. data/TODO.diagrams/38-html-note-and-receptions.md +32 -0
  77. data/TODO.diagrams/39-property-stereotype-discriminator.md +40 -0
  78. data/TODO.diagrams/40-standalone-1-discriminator.md +38 -0
  79. data/TODO.diagrams/41-xmi-to-svg-pipeline.md +113 -0
  80. data/TODO.diagrams/42-close-all-parity-deltas.md +69 -0
  81. data/TODO.diagrams/43-marker-deduplication-strategy.md +54 -0
  82. data/TODO.diagrams/44-element-icon-decorations.md +34 -0
  83. data/TODO.diagrams/45-phantom-parent-class-boxes.md +40 -0
  84. data/TODO.diagrams/46-user-drawn-legend-swatches.md +44 -0
  85. data/TODO.diagrams/47-attribute-line-fidelity.md +72 -0
  86. data/TODO.diagrams/48-spec-coverage-critical-emitter-components.md +45 -0
  87. data/TODO.diagrams/49-spec-coverage-orchestrators.md +25 -0
  88. data/TODO.diagrams/50-examples-parity.md +85 -0
  89. data/TODO.diagrams/51-property-oy-gap-and-remaining-gaps.md +48 -0
  90. data/TODO.diagrams/52-marker-registration-architecture.md +89 -0
  91. data/TODO.diagrams/53-remaining-example-gaps.md +86 -0
  92. data/TODO.diagrams/54-display-name-nested-classes.md +40 -0
  93. data/TODO.diagrams/55-visibility-icon-discriminator.md +58 -0
  94. data/TODO.diagrams/56-package-contents-rendering.md +148 -0
  95. data/TODO.diagrams/57-instance-specification-model.md +100 -0
  96. data/TODO.diagrams/58-domain-model-note-text.md +67 -0
  97. data/TODO.diagrams/59-classes-arrow-discriminator.md +57 -0
  98. data/TODO.diagrams/60-testschema-association-marker.md +40 -0
  99. data/TODO.diagrams/61-legend-block-rendering.md +55 -0
  100. data/TODO.diagrams/61-plateau-parity-gaps.md +59 -0
  101. data/TODO.diagrams/62-ghost-element-rendering.md +56 -0
  102. data/TODO.diagrams/63-qualified-name-wrap.md +36 -0
  103. data/TODO.diagrams/64-property-stereotype-labels.md +22 -0
  104. data/TODO.diagrams/65-path-coordinate-frame.md +62 -0
  105. data/TODO.diagrams/67-path-coordinate-frame.md +62 -0
  106. data/TODO.diagrams/68-inherited-attributes.md +65 -0
  107. data/TODO.diagrams/69-package-anchor-conditional.md +29 -0
  108. data/TODO.diagrams/70-mdg-technology-support.md +68 -0
  109. data/TODO.diagrams/71-off-canvas-parent-ghost.md +45 -0
  110. data/TODO.diagrams/72-basic-attr-divider-paths.md +39 -0
  111. data/TODO.diagrams/73-plateau-polygon-gap.md +69 -0
  112. data/TODO.diagrams/74-plateau-rect-gap.md +22 -0
  113. data/TODO.diagrams/75-plateau-text-gap.md +39 -0
  114. data/TODO.diagrams/76-basic-poly-path-gaps.md +41 -0
  115. data/TODO.diagrams/77-basic-text-overrender.md +40 -0
  116. data/TODO.diagrams/78-headerlines-ocp-refactor.md +65 -0
  117. data/TODO.diagrams/79-architecture-backlog.md +65 -0
  118. data/TODO.diagrams/80-basic-triangle-polygons.md +51 -0
  119. data/TODO.diagrams/81-plateau-path-overrender.md +53 -0
  120. data/TODO.diagrams/82-appearance-canvas-offset.md +54 -0
  121. data/TODO.diagrams/83-3d-diagram-subtitle.md +58 -0
  122. data/TODO.diagrams/84-canvas-offset-mismatches.md +61 -0
  123. data/TODO.diagrams/85-phantom-property-overrender.md +95 -0
  124. data/TODO.diagrams/86-foreign-package-rule.md +62 -0
  125. data/TODO.diagrams/87-internal-tech-encryption.md +80 -0
  126. data/TODO.diagrams/EXAMPLES_STATUS.md +88 -0
  127. data/TODO.diagrams/README.md +69 -0
  128. data/TODO.diagrams/STATUS.md +73 -0
  129. data/TODO.fixup/01-default-grouped-paths.md +34 -0
  130. data/TODO.fixup/02-marker-consolidation-per-connector.md +20 -0
  131. data/TODO.fixup/03-diagram-font-inference.md +28 -0
  132. data/TODO.fixup/04-extract-bounds-calculator.md +25 -0
  133. data/TODO.fixup/05-marker-registry-ocp.md +32 -0
  134. data/TODO.fixup/06-connector-path-bend-routing.md +28 -0
  135. data/TODO.fixup/07-edge-attachment-formula.md +22 -0
  136. data/TODO.fixup/08-visual-diff-harness.md +28 -0
  137. data/TODO.fixup/09-extract-element-compartments.md +34 -0
  138. data/TODO.fixup/10-canvas-width-overshoot-fix.md +20 -0
  139. data/TODO.fixup/11-layer-sequencer-extraction.md +18 -0
  140. data/TODO.fixup/12-path-style-harmony.md +23 -0
  141. data/TODO.fixup/13-datatype-stereotype-fallback.md +28 -0
  142. data/TODO.fixup/14-ea-exe-reverse-engineering-notes.md +56 -0
  143. data/TODO.fixup/15-enumeration-literal-rendering.md +30 -0
  144. data/TODO.fixup/16-operation-compartment-rendering.md +42 -0
  145. data/TODO.fixup/17-path-count-calibration.md +27 -0
  146. data/TODO.fixup/18-polygon-count-calibration.md +32 -0
  147. data/TODO.fixup/19-rect-count-undershoot-investigation.md +40 -0
  148. data/TODO.fixup/20-stereotype-color-from-classifier.md +18 -0
  149. data/TODO.fixup/21-visibility-filter-for-properties.md +24 -0
  150. data/TODO.fixup/22-multiplicity-display-rule-calibration.md +25 -0
  151. data/TODO.fixup/23-diagram-frame-title-element.md +31 -0
  152. data/TODO.fixup/24-note-element-rendering.md +23 -0
  153. data/TODO.fixup/25-ea-exe-disassembly-findings.md +177 -0
  154. data/TODO.fixup/26-svg-renderer-architecture.md +78 -0
  155. data/TODO.fixup/27-svg-element-emitters-disassembly.md +135 -0
  156. data/TODO.fixup/28-geometry-parser-identification.md +57 -0
  157. data/TODO.fixup/29-qea-svg-xmi-triple-comparison.md +185 -0
  158. data/TODO.fixup/30-theme-system-stub.md +46 -0
  159. data/TODO.fixup/31-theme-value-object-registry.md +17 -0
  160. data/TODO.fixup/32-styleex-on-diagram-model.md +22 -0
  161. data/TODO.fixup/33-text-encoding-decimals-rotation.md +29 -0
  162. data/TODO.fixup/34-per-type-fill-via-theme.md +18 -0
  163. data/TODO.fixup/35-stroke-width-per-theme.md +7 -0
  164. data/TODO.fixup/36-qea-rect-coord-normalization.md +24 -0
  165. data/TODO.fixup/37-package-shape-rendering.md +22 -0
  166. data/TODO.fixup/38-note-element-rendering.md +15 -0
  167. data/TODO.fixup/39-styleex-flag-honoring.md +25 -0
  168. data/TODO.fixup/40-connector-dashed-lines.md +15 -0
  169. data/TODO.fixup/41-diagram-frame-showborder-detection.md +23 -0
  170. data/TODO.fixup/42-textrenderer-centralization.md +10 -0
  171. data/TODO.fixup/43-colorresolver-unification.md +24 -0
  172. data/TODO.fixup/44-theme-aware-textrenderer.md +17 -0
  173. data/TODO.fixup/45-theme-aware-fontresolver.md +22 -0
  174. data/TODO.fixup/46-style-parameterized-by-theme.md +21 -0
  175. data/TODO.fixup/47-canvas-frame-inset.md +19 -0
  176. data/TODO.fixup/48-background-rect-format.md +15 -0
  177. data/TODO.fixup/49-all-samples-integration-test.md +17 -0
  178. data/TODO.fixup/50-svg-emitter-architecture-docs.md +33 -0
  179. data/TODO.fixup/51-promote-theme-to-ea-namespace.md +28 -0
  180. data/TODO.fixup/52-theme-definition-integrated-fills.md +26 -0
  181. data/TODO.fixup/53-theme-registry-yaml-loading.md +50 -0
  182. data/TODO.fixup/54-diagram-theme-api.md +50 -0
  183. data/TODO.fixup/55-update-eaemitter-to-ea-theme.md +23 -0
  184. data/TODO.fixup/56-theme-yaml-files.md +65 -0
  185. data/TODO.fixup/57-theme-specs-and-integration.md +27 -0
  186. data/TODO.fixup/58-ea-entry-point-theme-autoload.md +20 -0
  187. data/TODO.fixup/59-thread-theme-through-all-renderers.md +29 -0
  188. data/TODO.fixup/60-cli-theme-option.md +22 -0
  189. data/TODO.fixup/61-theme-validation.md +28 -0
  190. data/TODO.fixup/62-theme-builder-dsl.md +31 -0
  191. data/TODO.fixup/63-theme-integration-rendering-spec.md +24 -0
  192. data/TODO.fixup/64-theme-writeback-styleex.md +21 -0
  193. data/TODO.fixup/65-remove-dead-eaemitter-theme-files.md +30 -0
  194. data/TODO.fixup/66-styleex-writeback.md +22 -0
  195. data/TODO.fixup/67-delete-dead-code.md +28 -0
  196. data/TODO.fixup/68-figure-parity-status.md +82 -0
  197. data/TODO.fixup/69-package-polygon-rendering.md +54 -0
  198. data/TODO.fixup/70-tagged-values-compartment.md +39 -0
  199. data/TODO.fixup/71-auto-detect-theme-from-xmi.md +37 -0
  200. data/TODO.fixup/72-sample-exports-as-regression-specs.md +37 -0
  201. data/TODO.fixup/73-delete-dead-transformations.md +14 -0
  202. data/TODO.fixup/74-delete-dead-diagram-renderers.md +18 -0
  203. data/TODO.fixup/75-visibility-legend-icons.md +23 -0
  204. data/TODO.fixup/76-plateau-text-undershoot.md +27 -0
  205. data/TODO.fixup/77-canvas-frame-margins.md +28 -0
  206. data/TODO.fixup/78-note-element-rendering.md +30 -0
  207. data/TODO.fixup/79-operation-compartment-rendering.md +27 -0
  208. data/TODO.fixup/80-tagged-value-stereotype-visibility.md +37 -0
  209. data/TODO.fixup/81-figure-parity-current-state.md +63 -0
  210. data/config/qea_schema.yml +356 -0
  211. data/config/themes/119.yml +98 -0
  212. data/config/themes/default.yml +14 -0
  213. data/examples/exports/20251010_current_plateau_v5/UML_EA.DTD +6856 -0
  214. data/examples/exports/20251010_current_plateau_v5/model.xml +96709 -0
  215. data/examples/exports/ArcGISWorkspace_template/UML_EA.DTD +6856 -0
  216. data/examples/exports/ArcGISWorkspace_template/model.xml +1427 -0
  217. data/examples/exports/UmlModel_template/UML_EA.DTD +6856 -0
  218. data/examples/exports/UmlModel_template/model.xml +23 -0
  219. data/examples/exports/basic/UML_EA.DTD +6856 -0
  220. data/examples/exports/basic/model.xml +8039 -0
  221. data/examples/exports/simple/UML_EA.DTD +6856 -0
  222. data/examples/exports/simple/model.xml +585 -0
  223. data/examples/exports/simple_example/UML_EA.DTD +6856 -0
  224. data/examples/exports/simple_example/model.xml +23 -0
  225. data/examples/exports/test/UML_EA.DTD +6856 -0
  226. data/examples/exports/test/model.xml +1638 -0
  227. data/examples/qea/basic.qea +0 -0
  228. data/frontend/README.md +51 -0
  229. data/frontend/dist/app.iife.js +24 -0
  230. data/frontend/dist/style.css +1 -0
  231. data/frontend/index.html +26 -0
  232. data/frontend/package-lock.json +2359 -0
  233. data/frontend/package.json +26 -0
  234. data/frontend/src/App.vue +59 -0
  235. data/frontend/src/app.ts +8 -0
  236. data/frontend/src/components/AppHeader.vue +127 -0
  237. data/frontend/src/components/AppSidebar.vue +119 -0
  238. data/frontend/src/components/ClassDetails.vue +247 -0
  239. data/frontend/src/components/DiagramView.vue +79 -0
  240. data/frontend/src/components/PackageDetails.vue +92 -0
  241. data/frontend/src/components/PackageTreeNode.vue +64 -0
  242. data/frontend/src/components/SearchResults.vue +48 -0
  243. data/frontend/src/components/WelcomeView.vue +87 -0
  244. data/frontend/src/shims-vue.d.ts +5 -0
  245. data/frontend/src/stores/dataStore.ts +79 -0
  246. data/frontend/src/stores/uiStore.ts +167 -0
  247. data/frontend/src/styles/main.css +1566 -0
  248. data/frontend/src/types.ts +221 -0
  249. data/frontend/tests/e2e/__screenshots__/new-dark-mode.png +0 -0
  250. data/frontend/tests/e2e/__screenshots__/new-package-details.png +0 -0
  251. data/frontend/tests/e2e/__screenshots__/new-search-modal.png +0 -0
  252. data/frontend/tests/e2e/__screenshots__/new-sidebar-collapsed.png +0 -0
  253. data/frontend/tests/e2e/__screenshots__/new-welcome.png +0 -0
  254. data/frontend/tests/e2e/render.spec.ts +188 -0
  255. data/frontend/tsconfig.json +20 -0
  256. data/frontend/vite.config.ts +26 -0
  257. data/frontend/vitest.config.ts +8 -0
  258. data/lib/ea/cli/app.rb +57 -2
  259. data/lib/ea/cli/command/diagrams.rb +37 -28
  260. data/lib/ea/cli/command/diff.rb +46 -0
  261. data/lib/ea/cli/command/export.rb +80 -0
  262. data/lib/ea/cli/command/info.rb +75 -0
  263. data/lib/ea/cli/command/lint.rb +48 -0
  264. data/lib/ea/cli/command/mdg.rb +98 -0
  265. data/lib/ea/cli/command/query.rb +34 -0
  266. data/lib/ea/cli/command/render.rb +135 -0
  267. data/lib/ea/cli/command/svg.rb +8 -15
  268. data/lib/ea/cli/command.rb +7 -0
  269. data/lib/ea/diagram/display_config.rb +184 -0
  270. data/lib/ea/diagram.rb +4 -41
  271. data/lib/ea/diff/change.rb +30 -0
  272. data/lib/ea/diff/comparator.rb +82 -0
  273. data/lib/ea/diff/html_reporter.rb +91 -0
  274. data/lib/ea/diff.rb +15 -0
  275. data/lib/ea/export/json/generator.rb +42 -0
  276. data/lib/ea/export/json.rb +12 -0
  277. data/lib/ea/export/json_schema/generator.rb +77 -0
  278. data/lib/ea/export/json_schema.rb +12 -0
  279. data/lib/ea/export/plantuml/generator.rb +73 -0
  280. data/lib/ea/export/plantuml.rb +12 -0
  281. data/lib/ea/export/xmi/generator.rb +77 -0
  282. data/lib/ea/export/xmi.rb +11 -0
  283. data/lib/ea/export/xsd/class_mapping.rb +65 -0
  284. data/lib/ea/export/xsd/generator.rb +142 -0
  285. data/lib/ea/export/xsd/namespace_registry.rb +82 -0
  286. data/lib/ea/export/xsd.rb +19 -0
  287. data/lib/ea/export.rb +14 -0
  288. data/lib/ea/image/emf_renderer.rb +42 -0
  289. data/lib/ea/image.rb +12 -0
  290. data/lib/ea/lint/engine.rb +30 -0
  291. data/lib/ea/lint/lint_rule.rb +38 -0
  292. data/lib/ea/lint/offense.rb +18 -0
  293. data/lib/ea/lint/rules/cyclic_generalization.rb +62 -0
  294. data/lib/ea/lint/rules/duplicate_name.rb +28 -0
  295. data/lib/ea/lint/rules/missing_stereotype.rb +35 -0
  296. data/lib/ea/lint/rules/naming_convention.rb +44 -0
  297. data/lib/ea/lint/rules/orphan_element.rb +28 -0
  298. data/lib/ea/lint/rules.rb +14 -0
  299. data/lib/ea/lint.rb +13 -0
  300. data/lib/ea/mdg/document.rb +120 -0
  301. data/lib/ea/mdg/loader.rb +118 -0
  302. data/lib/ea/mdg/registry.rb +98 -0
  303. data/lib/ea/mdg/stereotype_alias_registry.rb +77 -0
  304. data/lib/ea/mdg/xml/applies_to.rb +18 -0
  305. data/lib/ea/mdg/xml/apply.rb +18 -0
  306. data/lib/ea/mdg/xml/content.rb +18 -0
  307. data/lib/ea/mdg/xml/documentation.rb +24 -0
  308. data/lib/ea/mdg/xml/stereotype.rb +28 -0
  309. data/lib/ea/mdg/xml/stereotypes.rb +18 -0
  310. data/lib/ea/mdg/xml/tag.rb +28 -0
  311. data/lib/ea/mdg/xml/tagged_values.rb +18 -0
  312. data/lib/ea/mdg/xml/technology.rb +28 -0
  313. data/lib/ea/mdg/xml/uml_profile.rb +22 -0
  314. data/lib/ea/mdg/xml/uml_profiles.rb +18 -0
  315. data/lib/ea/mdg/xml.rb +24 -0
  316. data/lib/ea/mdg.rb +25 -0
  317. data/lib/ea/model/classifier.rb +4 -0
  318. data/lib/ea/model/constraint.rb +24 -0
  319. data/lib/ea/model/diagram.rb +85 -0
  320. data/lib/ea/model/diagram_connector.rb +34 -0
  321. data/lib/ea/model/diagram_element.rb +16 -0
  322. data/lib/ea/model/document.rb +47 -2
  323. data/lib/ea/model/ghost_label.rb +32 -0
  324. data/lib/ea/model/instance_specification.rb +34 -0
  325. data/lib/ea/model/legend.rb +43 -0
  326. data/lib/ea/model/legend_item.rb +26 -0
  327. data/lib/ea/model/note.rb +28 -0
  328. data/lib/ea/model/operation.rb +2 -0
  329. data/lib/ea/model/property.rb +2 -0
  330. data/lib/ea/model/relationship.rb +2 -0
  331. data/lib/ea/model/signal.rb +14 -0
  332. data/lib/ea/model/slot.rb +19 -0
  333. data/lib/ea/model.rb +8 -0
  334. data/lib/ea/ocl/evaluator.rb +94 -0
  335. data/lib/ea/ocl/nodes.rb +30 -0
  336. data/lib/ea/ocl/parser.rb +133 -0
  337. data/lib/ea/ocl/unsupported_error.rb +7 -0
  338. data/lib/ea/ocl.rb +13 -0
  339. data/lib/ea/qea/factory/document_builder.rb +24 -10
  340. data/lib/ea/qea/factory/generalization_builder.rb +9 -11
  341. data/lib/ea/qea/infrastructure/table_reader.rb +9 -0
  342. data/lib/ea/qea/models/ea_author.rb +35 -0
  343. data/lib/ea/qea/models/ea_glossary.rb +34 -0
  344. data/lib/ea/qea/models/ea_image.rb +53 -0
  345. data/lib/ea/qea/models/ea_implement.rb +32 -0
  346. data/lib/ea/qea/models/ea_list.rb +42 -0
  347. data/lib/ea/qea/models/ea_object_effort.rb +34 -0
  348. data/lib/ea/qea/models/ea_object_problem.rb +37 -0
  349. data/lib/ea/qea/models/ea_object_require.rb +34 -0
  350. data/lib/ea/qea/models/ea_object_resource.rb +34 -0
  351. data/lib/ea/qea/models/ea_object_risk.rb +34 -0
  352. data/lib/ea/qea/models/ea_object_scenario.rb +34 -0
  353. data/lib/ea/qea/models/ea_object_test.rb +35 -0
  354. data/lib/ea/qea/models/ea_object_trx.rb +34 -0
  355. data/lib/ea/qea/models/ea_operation.rb +7 -0
  356. data/lib/ea/qea/models/ea_palette.rb +30 -0
  357. data/lib/ea/qea/models/ea_palette_item.rb +36 -0
  358. data/lib/ea/qea/models/ea_phase.rb +40 -0
  359. data/lib/ea/qea/models/ea_role_constraint.rb +32 -0
  360. data/lib/ea/qea/models/ea_secrypt.rb +34 -0
  361. data/lib/ea/qea/models/ea_version.rb +39 -0
  362. data/lib/ea/qea/models/ea_xref.rb +11 -1
  363. data/lib/ea/qea/models.rb +19 -0
  364. data/lib/ea/qea/services/database_loader.rb +22 -1
  365. data/lib/ea/query/builder.rb +108 -0
  366. data/lib/ea/query.rb +9 -0
  367. data/lib/ea/render/image_converter.rb +101 -0
  368. data/lib/ea/render.rb +10 -0
  369. data/lib/ea/shapescript/parser.rb +208 -0
  370. data/lib/ea/shapescript/renderer.rb +74 -0
  371. data/lib/ea/shapescript/shape.rb +34 -0
  372. data/lib/ea/shapescript.rb +24 -0
  373. data/lib/ea/sources/qea/adapter.rb +22 -5
  374. data/lib/ea/sources/qea/classifier_builder.rb +184 -7
  375. data/lib/ea/sources/qea/diagram_builder.rb +352 -24
  376. data/lib/ea/sources/qea/id_normalizer.rb +16 -1
  377. data/lib/ea/sources/qea/instance_builder.rb +113 -0
  378. data/lib/ea/sources/qea/legend_builder.rb +137 -0
  379. data/lib/ea/sources/qea/note_builder.rb +58 -0
  380. data/lib/ea/sources/qea/object_classifier_map.rb +6 -1
  381. data/lib/ea/sources/qea/operation_builder.rb +1 -0
  382. data/lib/ea/sources/qea/package_builder.rb +16 -0
  383. data/lib/ea/sources/qea/relationship_builder.rb +21 -5
  384. data/lib/ea/sources/qea/tagged_value_builder.rb +28 -9
  385. data/lib/ea/sources/qea/xref/legend_definition.rb +35 -0
  386. data/lib/ea/sources/qea/xref/parser.rb +150 -0
  387. data/lib/ea/sources/qea/xref/property_definition.rb +29 -0
  388. data/lib/ea/sources/qea/xref/record.rb +41 -0
  389. data/lib/ea/sources/qea/xref/stereotype_application.rb +37 -0
  390. data/lib/ea/sources/qea/xref.rb +27 -0
  391. data/lib/ea/sources/qea.rb +4 -0
  392. data/lib/ea/sources/xmi/adapter.rb +80 -2
  393. data/lib/ea/sources/xmi/classifier_builder.rb +10 -1
  394. data/lib/ea/sources/xmi/diagram_builder.rb +95 -18
  395. data/lib/ea/sources/xmi/extension_geometry_parser.rb +19 -2
  396. data/lib/ea/sources/xmi/extension_style_parser.rb +15 -2
  397. data/lib/ea/sources/xmi/note_builder.rb +46 -0
  398. data/lib/ea/sources/xmi/property_builder.rb +9 -0
  399. data/lib/ea/sources/xmi/stereotype_builder.rb +54 -0
  400. data/lib/ea/sources/xmi/tag_builder.rb +45 -0
  401. data/lib/ea/sources/xmi/umldi_keyword_extractor.rb +87 -0
  402. data/lib/ea/sources/xmi.rb +4 -0
  403. data/lib/ea/spa/output/sharded_multi_file_strategy.rb +18 -3
  404. data/lib/ea/spa/output/single_file_strategy.rb +8 -2
  405. data/lib/ea/spa/output/strategy.rb +34 -0
  406. data/lib/ea/svg/connector_router.rb +134 -0
  407. data/lib/ea/svg/ea_emitter/background.rb +2 -3
  408. data/lib/ea/svg/ea_emitter/bounds_calculator.rb +142 -0
  409. data/lib/ea/svg/ea_emitter/canvas.rb +56 -33
  410. data/lib/ea/svg/ea_emitter/color_resolver.rb +56 -0
  411. data/lib/ea/svg/ea_emitter/compartment/attributes.rb +28 -0
  412. data/lib/ea/svg/ea_emitter/compartment/constraints.rb +30 -0
  413. data/lib/ea/svg/ea_emitter/compartment/enum_literals.rb +86 -0
  414. data/lib/ea/svg/ea_emitter/compartment/header.rb +32 -0
  415. data/lib/ea/svg/ea_emitter/compartment/header_divider.rb +57 -0
  416. data/lib/ea/svg/ea_emitter/compartment/instance_slots.rb +96 -0
  417. data/lib/ea/svg/ea_emitter/compartment/instance_underline.rb +67 -0
  418. data/lib/ea/svg/ea_emitter/compartment/note_body.rb +52 -0
  419. data/lib/ea/svg/ea_emitter/compartment/note_text.rb +102 -0
  420. data/lib/ea/svg/ea_emitter/compartment/operations.rb +38 -0
  421. data/lib/ea/svg/ea_emitter/compartment/package_contents.rb +72 -0
  422. data/lib/ea/svg/ea_emitter/compartment/package_from_parent.rb +136 -0
  423. data/lib/ea/svg/ea_emitter/compartment/shape.rb +82 -0
  424. data/lib/ea/svg/ea_emitter/compartment/tagged_values.rb +30 -0
  425. data/lib/ea/svg/ea_emitter/compartment.rb +58 -0
  426. data/lib/ea/svg/ea_emitter/connectors.rb +46 -12
  427. data/lib/ea/svg/ea_emitter/diagram_frame.rb +143 -0
  428. data/lib/ea/svg/ea_emitter/document.rb +15 -24
  429. data/lib/ea/svg/ea_emitter/element/attribute_line_builder.rb +117 -0
  430. data/lib/ea/svg/ea_emitter/element/attribute_renderer.rb +119 -0
  431. data/lib/ea/svg/ea_emitter/element/bcol_decoder.rb +29 -0
  432. data/lib/ea/svg/ea_emitter/element/compartment_geometry.rb +113 -0
  433. data/lib/ea/svg/ea_emitter/element/constraint_renderer.rb +53 -0
  434. data/lib/ea/svg/ea_emitter/element/divider_renderer.rb +17 -0
  435. data/lib/ea/svg/ea_emitter/element/enumeration_literal_renderer.rb +34 -0
  436. data/lib/ea/svg/ea_emitter/element/filter.rb +44 -0
  437. data/lib/ea/svg/ea_emitter/element/hand_draw_shape_renderer.rb +77 -0
  438. data/lib/ea/svg/ea_emitter/element/header_line_pipeline.rb +43 -0
  439. data/lib/ea/svg/ea_emitter/element/header_line_provider/instance_spec.rb +27 -0
  440. data/lib/ea/svg/ea_emitter/element/header_line_provider/name.rb +76 -0
  441. data/lib/ea/svg/ea_emitter/element/header_line_provider/parent_ghost.rb +25 -0
  442. data/lib/ea/svg/ea_emitter/element/header_line_provider/stereotype_label.rb +68 -0
  443. data/lib/ea/svg/ea_emitter/element/header_line_provider.rb +19 -0
  444. data/lib/ea/svg/ea_emitter/element/header_lines.rb +27 -0
  445. data/lib/ea/svg/ea_emitter/element/header_renderer.rb +48 -0
  446. data/lib/ea/svg/ea_emitter/element/icon_renderer.rb +109 -0
  447. data/lib/ea/svg/ea_emitter/element/legend_renderer.rb +177 -0
  448. data/lib/ea/svg/ea_emitter/element/note_shape_renderer.rb +112 -0
  449. data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +100 -0
  450. data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +181 -0
  451. data/lib/ea/svg/ea_emitter/element/row_icon_renderer.rb +156 -0
  452. data/lib/ea/svg/ea_emitter/element/shape_renderer.rb +17 -0
  453. data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +82 -0
  454. data/lib/ea/svg/ea_emitter/element/tagged_value_renderer.rb +50 -0
  455. data/lib/ea/svg/ea_emitter/element/text_escape.rb +20 -0
  456. data/lib/ea/svg/ea_emitter/element.rb +37 -0
  457. data/lib/ea/svg/ea_emitter/elements.rb +283 -95
  458. data/lib/ea/svg/ea_emitter/font_resolver.rb +111 -0
  459. data/lib/ea/svg/ea_emitter/ghost_labels.rb +66 -0
  460. data/lib/ea/svg/ea_emitter/label/end_label.rb +266 -0
  461. data/lib/ea/svg/ea_emitter/label/midpoint_label.rb +101 -0
  462. data/lib/ea/svg/ea_emitter/label/registry.rb +50 -0
  463. data/lib/ea/svg/ea_emitter/label.rb +23 -0
  464. data/lib/ea/svg/ea_emitter/labels.rb +93 -27
  465. data/lib/ea/svg/ea_emitter/layer.rb +20 -0
  466. data/lib/ea/svg/ea_emitter/layer_sequencer.rb +93 -0
  467. data/lib/ea/svg/ea_emitter/marker/arrow_path.rb +52 -0
  468. data/lib/ea/svg/ea_emitter/marker/diamond.rb +57 -0
  469. data/lib/ea/svg/ea_emitter/marker/kind.rb +35 -0
  470. data/lib/ea/svg/ea_emitter/marker/open_triangle.rb +27 -0
  471. data/lib/ea/svg/ea_emitter/marker/package_import.rb +39 -0
  472. data/lib/ea/svg/ea_emitter/marker/plus.rb +40 -0
  473. data/lib/ea/svg/ea_emitter/marker/registry.rb +42 -0
  474. data/lib/ea/svg/ea_emitter/marker.rb +37 -0
  475. data/lib/ea/svg/ea_emitter/markers.rb +351 -64
  476. data/lib/ea/svg/ea_emitter/render_context.rb +52 -0
  477. data/lib/ea/svg/ea_emitter/style.rb +15 -0
  478. data/lib/ea/svg/ea_emitter/text_renderer.rb +92 -0
  479. data/lib/ea/svg/ea_emitter.rb +33 -11
  480. data/lib/ea/svg/parity/checker.rb +105 -0
  481. data/lib/ea/svg/parity/source.rb +59 -0
  482. data/lib/ea/svg/parity/suite.rb +113 -0
  483. data/lib/ea/svg/parity.rb +14 -0
  484. data/lib/ea/svg.rb +2 -15
  485. data/lib/ea/theme/definition.rb +176 -0
  486. data/lib/ea/theme/loader.rb +125 -0
  487. data/lib/ea/theme/registry.rb +96 -0
  488. data/lib/ea/theme.rb +37 -0
  489. data/lib/ea/transformations.rb +5 -88
  490. data/lib/ea/version.rb +4 -1
  491. data/lib/ea.rb +14 -4
  492. data/lib/tasks/svg_bench.rake +87 -0
  493. data/lutaml-uml-0.5.1.gem +0 -0
  494. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes-list.png +0 -0
  495. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes.png +0 -0
  496. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-2-gradients.png +0 -0
  497. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-3-colors.png +0 -0
  498. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance-fonts.png +0 -0
  499. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance.png +0 -0
  500. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-5-behavior.png +0 -0
  501. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-6-sequence.png +0 -0
  502. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram.png +0 -0
  503. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-2-objects.png +0 -0
  504. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-3-links.png +0 -0
  505. metadata +449 -26
  506. data/lib/ea/diagram/configuration.rb +0 -379
  507. data/lib/ea/diagram/element_renderers/base_renderer.rb +0 -77
  508. data/lib/ea/diagram/element_renderers/class_renderer.rb +0 -323
  509. data/lib/ea/diagram/element_renderers/connector_renderer.rb +0 -41
  510. data/lib/ea/diagram/element_renderers/package_renderer.rb +0 -61
  511. data/lib/ea/diagram/element_renderers.rb +0 -43
  512. data/lib/ea/diagram/extractor.rb +0 -566
  513. data/lib/ea/diagram/layout_engine.rb +0 -170
  514. data/lib/ea/diagram/path_builder.rb +0 -202
  515. data/lib/ea/diagram/style_parser.rb +0 -42
  516. data/lib/ea/diagram/style_resolver.rb +0 -291
  517. data/lib/ea/diagram/svg_renderer.rb +0 -274
  518. data/lib/ea/diagram/util.rb +0 -73
  519. data/lib/ea/svg/bounds_calculator.rb +0 -74
  520. data/lib/ea/svg/connector_path.rb +0 -138
  521. data/lib/ea/svg/element_box.rb +0 -256
  522. data/lib/ea/svg/renderer.rb +0 -77
  523. data/lib/ea/svg/style_resolver.rb +0 -72
  524. data/lib/ea/transformations/configuration.rb +0 -333
  525. data/lib/ea/transformations/format_registry.rb +0 -366
  526. data/lib/ea/transformations/parsers/base_parser.rb +0 -482
  527. data/lib/ea/transformations/parsers/qea_parser.rb +0 -401
  528. data/lib/ea/transformations/parsers/xmi_parser.rb +0 -243
  529. data/lib/ea/transformations/transformation_engine.rb +0 -390
@@ -8,12 +8,14 @@ module Ea
8
8
  # consumers dispatch without `is_a?` ladders.
9
9
  class Relationship < Base
10
10
  attribute :qualified_name, :string
11
+ attribute :stereotype, :string
11
12
  attribute :relationship_kind, :string, default: -> { "relationship" }
12
13
 
13
14
  json do
14
15
  map "id", to: :id
15
16
  map "name", to: :name
16
17
  map "qualifiedName", to: :qualified_name
18
+ map "stereotype", to: :stereotype
17
19
  map "relationshipKind", to: :relationship_kind, render_default: true
18
20
  end
19
21
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Model
5
+ # UML Signal: a classifier whose instances represent
6
+ # asynchronous communications between objects. Distinct from
7
+ # Klass because EA renders its header with the «signal»
8
+ # stereotype fallback and supports a separate "receptions"
9
+ # compartment.
10
+ class Signal < Classifier
11
+ attribute :model_kind, :string, default: -> { "signal" }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Model
5
+ # A single slot (variable = value) on an InstanceSpecification.
6
+ class Slot < Base
7
+ attribute :name, :string
8
+ attribute :value, :string
9
+ attribute :op, :string, default: -> { "=" }
10
+
11
+ json do
12
+ map "id", to: :id
13
+ map "name", to: :name
14
+ map "value", to: :value
15
+ map "op", to: :op
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/ea/model.rb CHANGED
@@ -23,17 +23,25 @@ module Ea
23
23
  autoload :Waypoint, "ea/model/waypoint"
24
24
  autoload :Annotation, "ea/model/annotation"
25
25
  autoload :TaggedValue, "ea/model/tagged_value"
26
+ autoload :Constraint, "ea/model/constraint"
27
+ autoload :Slot, "ea/model/slot"
28
+ autoload :InstanceSpecification, "ea/model/instance_specification"
26
29
  autoload :Stereotype, "ea/model/stereotype"
27
30
  autoload :EnumerationLiteral, "ea/model/enumeration_literal"
28
31
  autoload :Parameter, "ea/model/parameter"
29
32
  autoload :Property, "ea/model/property"
30
33
  autoload :Operation, "ea/model/operation"
34
+ autoload :Note, "ea/model/note"
35
+ autoload :Legend, "ea/model/legend"
36
+ autoload :LegendItem, "ea/model/legend_item"
37
+ autoload :GhostLabel, "ea/model/ghost_label"
31
38
  autoload :Classifier, "ea/model/classifier"
32
39
  autoload :Klass, "ea/model/klass"
33
40
  autoload :DataType, "ea/model/data_type"
34
41
  autoload :PrimitiveType, "ea/model/primitive_type"
35
42
  autoload :Interface, "ea/model/interface"
36
43
  autoload :Enumeration, "ea/model/enumeration"
44
+ autoload :Signal, "ea/model/signal"
37
45
  autoload :Package, "ea/model/package"
38
46
  autoload :Relationship, "ea/model/relationship"
39
47
  autoload :Association, "ea/model/association"
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Ocl
5
+ # Evaluates an AST produced by Parser against a context object
6
+ # (the model element under test). Dispatches on node class.
7
+ module Evaluator
8
+ module_function
9
+
10
+ # @param ast [Nodes::*]
11
+ # @param context [Object] the model element (self)
12
+ # @return [Boolean, Object] result of evaluation
13
+ def evaluate(ast, context)
14
+ case ast
15
+ when Nodes::Literal then ast.value
16
+ when Nodes::AttributeAccess then attribute_value(context, ast.name)
17
+ when Nodes::StringMatches then eval_string_matches(ast, context)
18
+ when Nodes::CollectionExists then eval_exists(ast, context)
19
+ when Nodes::CollectionForAll then eval_for_all(ast, context)
20
+ when Nodes::BinaryOp then eval_binary(ast, context)
21
+ when Nodes::UnaryOp then eval_unary(ast, context)
22
+ else
23
+ raise UnsupportedError,
24
+ "Cannot evaluate AST node: #{ast.class}"
25
+ end
26
+ end
27
+
28
+ def attribute_value(context, name)
29
+ return nil unless context
30
+
31
+ context.public_send(name)
32
+ end
33
+
34
+ def eval_string_matches(ast, context)
35
+ target = evaluate(ast.string, context).to_s
36
+ Regexp.new(ast.pattern).match?(target)
37
+ end
38
+
39
+ def eval_exists(ast, context)
40
+ collection = evaluate(ast.collection, context)
41
+ return false unless collection.respond_to?(:each)
42
+
43
+ var_name = ast.var
44
+ predicate = ast.predicate
45
+ collection.any? do |element|
46
+ evaluate(predicate, var_binding(context, var_name, element))
47
+ end
48
+ end
49
+
50
+ def eval_for_all(ast, context)
51
+ collection = evaluate(ast.collection, context)
52
+ return true unless collection.respond_to?(:each)
53
+
54
+ var_name = ast.var
55
+ predicate = ast.predicate
56
+ collection.all? do |element|
57
+ evaluate(predicate, var_binding(context, var_name, element))
58
+ end
59
+ end
60
+
61
+ def eval_binary(ast, context)
62
+ left = evaluate(ast.left, context)
63
+ right = evaluate(ast.right, context)
64
+ case ast.op
65
+ when :and then left && right
66
+ when :or then left || right
67
+ else raise UnsupportedError, "Unknown binary op: #{ast.op}"
68
+ end
69
+ end
70
+
71
+ def eval_unary(ast, context)
72
+ case ast.op
73
+ when :not then !evaluate(ast.operand, context)
74
+ else raise UnsupportedError, "Unknown unary op: #{ast.op}"
75
+ end
76
+ end
77
+
78
+ # Bind a variable name in the evaluation context. Uses a small
79
+ # Struct wrapper so attribute lookups still fall through to the
80
+ # original context.
81
+ VarBinding = Struct.new(:context, :var_name, :value, keyword_init: true) do
82
+ def public_send(method_name, *args)
83
+ return value if method_name == var_name
84
+
85
+ context.public_send(method_name, *args)
86
+ end
87
+ end
88
+
89
+ def var_binding(context, var_name, value)
90
+ VarBinding.new(context: context, var_name: var_name, value: value)
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Ocl
5
+ # AST node types for the parser. Each is a small Struct that the
6
+ # Evaluator dispatches on by class.
7
+ module Nodes
8
+ # `self.<attr>` access — read attribute on the context object.
9
+ AttributeAccess = Struct.new(:name, keyword_init: true)
10
+
11
+ # Literal value (number, string, boolean).
12
+ Literal = Struct.new(:value, keyword_init: true)
13
+
14
+ # `<collection>->exists(var | <pred>)` — true if any element matches.
15
+ CollectionExists = Struct.new(:collection, :var, :predicate,
16
+ keyword_init: true)
17
+
18
+ # `<collection>->forAll(var | <pred>)` — true if all match.
19
+ CollectionForAll = Struct.new(:collection, :var, :predicate,
20
+ keyword_init: true)
21
+
22
+ # `<string>.matches('<regex>')`
23
+ StringMatches = Struct.new(:string, :pattern, keyword_init: true)
24
+
25
+ # `<a> and <b>`, `<a> or <b>`, `not <a>`
26
+ BinaryOp = Struct.new(:op, :left, :right, keyword_init: true)
27
+ UnaryOp = Struct.new(:op, :operand, keyword_init: true)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Ocl
5
+ # Tokenizer + parser for an OCL invariant subset. Returns an AST
6
+ # built from Nodes::* structs.
7
+ #
8
+ # Supported forms:
9
+ # inv: <expr>
10
+ # self.<attr>
11
+ # "<literal>"
12
+ # <collection>->exists(x | <pred>)
13
+ # <collection>->forAll(x | <pred>)
14
+ # <string>.matches('<regex>')
15
+ # <expr> and <expr>
16
+ # <expr> or <expr>
17
+ # not <expr>
18
+ module Parser
19
+ module_function
20
+
21
+ # @param source [String] OCL expression with optional `inv:` prefix
22
+ # @return [Array<Nodes::*>] AST nodes (one per top-level expression)
23
+ def parse(source)
24
+ return [] if source.nil? || source.empty?
25
+
26
+ body = strip_inv_prefix(source)
27
+ parse_expression(body)
28
+ end
29
+
30
+ def strip_inv_prefix(source)
31
+ s = source.strip
32
+ s.sub(/\Ainv\s*:\s*/, "")
33
+ end
34
+
35
+ # Parse one expression. Returns a single AST node.
36
+ def parse_expression(source)
37
+ s = source.strip
38
+
39
+ # Strip outer parens
40
+ s = s[1..-2].strip while s.start_with?("(") && s.end_with?(")")
41
+
42
+ # 'not <expr>'
43
+ if s.start_with?("not ")
44
+ return Nodes::UnaryOp.new(op: :not,
45
+ operand: parse_expression(s[4..]))
46
+ end
47
+
48
+ # 'and' / 'or' at top level (find the operator not inside parens)
49
+ if (split = split_on_logical_op(s))
50
+ op, left, right = split
51
+ return Nodes::BinaryOp.new(op: op,
52
+ left: parse_expression(left),
53
+ right: parse_expression(right))
54
+ end
55
+
56
+ # Collection ->exists(...) or ->forAll(...)
57
+ if (m = s.match(/^(.+?)->(exists|forAll)\s*\(\s*(\w+)\s*\|\s*(.+?)\s*\)\z/))
58
+ return collection_node(m[2].to_sym, m[1], m[3], m[4])
59
+ end
60
+
61
+ # String.matches('regex')
62
+ if (m = s.match(/^(.+?)\.matches\(\s*['"](.+?)['"]\s*\)\z/))
63
+ return Nodes::StringMatches.new(string: parse_expression(m[1]),
64
+ pattern: m[2])
65
+ end
66
+
67
+ # Boolean literal — check BEFORE bare identifier
68
+ return Nodes::Literal.new(value: true) if s == "true"
69
+ return Nodes::Literal.new(value: false) if s == "false"
70
+
71
+ # Numeric literal
72
+ return Nodes::Literal.new(value: s.to_i) if s.match?(/\A-?\d+\z/)
73
+ return Nodes::Literal.new(value: s.to_f) if s.match?(/\A-?\d+\.\d+\z/)
74
+
75
+ # String literal
76
+ return Nodes::Literal.new(value: s[1..-2]) if s.match?(/\A['"].*['"]\z/)
77
+
78
+ # 'self.<attr>' or '<var>.<attr>' — both produce AttributeAccess.
79
+ if (m = s.match(/\A(?:self\.|[a-z_]\w*\.)([a-z_]\w*)\z/i))
80
+ return Nodes::AttributeAccess.new(name: m[1])
81
+ end
82
+
83
+ # Bare identifier — variable reference.
84
+ if (m = s.match(/\A([a-z_]\w*)\z/))
85
+ return Nodes::AttributeAccess.new(name: m[1])
86
+ end
87
+
88
+ raise UnsupportedError,
89
+ "Unsupported OCL expression: #{source.inspect}"
90
+ end
91
+
92
+ def collection_node(kind, collection_src, var, pred_src)
93
+ Nodes.const_get(kind == :exists ? :CollectionExists : :CollectionForAll)
94
+ .new(collection: parse_expression(collection_src),
95
+ var: var,
96
+ predicate: parse_expression(pred_src))
97
+ end
98
+
99
+ # Find the topmost ` and ` or ` or ` outside of parens.
100
+ # Returns [op, left_str, right_str] or nil.
101
+ def split_on_logical_op(source)
102
+ depth = 0
103
+ # Look for ' or ' first (lower precedence than 'and' in OCL?)
104
+ # Actually OCL: 'or' < 'xor' < 'and' < 'implies'. We pick the
105
+ # rightmost top-level operator (left-associative).
106
+ %i[and or].each do |op|
107
+ keyword = " #{op} "
108
+ idx = find_top_level(source, keyword)
109
+ next unless idx
110
+
111
+ return [op, source[0...idx], source[(idx + keyword.length)..]]
112
+ end
113
+ nil
114
+ end
115
+
116
+ # Find substring at depth 0 (not inside parens).
117
+ def find_top_level(source, substring)
118
+ depth = 0
119
+ i = 0
120
+ while i < source.length
121
+ c = source[i]
122
+ depth += 1 if c == "("
123
+ depth -= 1 if c == ")"
124
+ if depth.zero? && source[i, substring.length] == substring
125
+ return i
126
+ end
127
+ i += 1
128
+ end
129
+ nil
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Ocl
5
+ class UnsupportedError < Ea::Error; end
6
+ end
7
+ end
data/lib/ea/ocl.rb ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ # OCL (Object Constraint Language) parser + evaluator namespace.
5
+ # Supports a subset of OCL invariant syntax sufficient for
6
+ # evaluating common EA constraints.
7
+ module Ocl
8
+ autoload :Parser, "ea/ocl/parser"
9
+ autoload :Evaluator, "ea/ocl/evaluator"
10
+ autoload :Nodes, "ea/ocl/nodes"
11
+ autoload :UnsupportedError, "ea/ocl/unsupported_error"
12
+ end
13
+ end
@@ -246,7 +246,7 @@ module Ea
246
246
  # @param valid_ids [Set<String>] Set of valid xmi_ids
247
247
  # @return [Boolean] True if valid or nil
248
248
  def check_association_end_valid?(assoc, attr, valid_ids)
249
- value = assoc.public_send(attr)
249
+ value = association_end_xmi_id(assoc, attr)
250
250
  return true if value.nil?
251
251
 
252
252
  valid_ids.include?(value)
@@ -258,20 +258,34 @@ module Ea
258
258
  # @param valid_ids [Set<String>] Set of valid xmi_ids
259
259
  # @param warnings [Array<String>] Warning accumulator
260
260
  def add_invalid_end_warning(assoc, attr, valid_ids, warnings) # rubocop:disable Metrics/MethodLength
261
- value = assoc.public_send(attr)
261
+ value = association_end_xmi_id(assoc, attr)
262
262
  return if value.nil?
263
263
 
264
264
  unless valid_ids.include?(value)
265
- # Get the corresponding name attribute for better error messages
266
- name_attr = attr.to_s.gsub("_xmi_id", "").to_sym
267
- name_value = begin
268
- assoc.public_send(name_attr)
269
- rescue StandardError
270
- nil
271
- end
265
+ name_value = association_end_name(assoc, attr)
272
266
 
273
267
  warnings << "Association #{assoc.xmi_id} references " \
274
- "invalid #{name_attr}: #{name_value}"
268
+ "invalid #{attr}: #{name_value}"
269
+ end
270
+ end
271
+
272
+ # Explicit dispatch for association end xmi_id attributes.
273
+ # Replaces dynamic `public_send(attr)` with a case statement
274
+ # so callers and types are statically known.
275
+ def association_end_xmi_id(assoc, attr)
276
+ case attr
277
+ when :member_end_xmi_id then assoc.member_end_xmi_id
278
+ when :owner_end_xmi_id then assoc.owner_end_xmi_id
279
+ else raise ArgumentError, "unknown association end: #{attr}"
280
+ end
281
+ end
282
+
283
+ # Explicit dispatch for association end display-name attributes.
284
+ def association_end_name(assoc, attr)
285
+ case attr
286
+ when :member_end_xmi_id then assoc.member_end
287
+ when :owner_end_xmi_id then assoc.owner_end
288
+ else raise ArgumentError, "unknown association end: #{attr}"
275
289
  end
276
290
  end
277
291
 
@@ -53,20 +53,18 @@ module Ea
53
53
  end
54
54
 
55
55
  def to_general_attribute(attr)
56
- base = base_attr_hash(attr)
57
- Lutaml::Uml::GeneralAttribute.new.tap do |gen_attr|
58
- base.each { |k, v| gen_attr.public_send(:"#{k}=", v) }
59
- gen_attr.is_derived = !!attr.is_derived
60
- gen_attr.has_association = !!attr.association
61
- end
56
+ Lutaml::Uml::GeneralAttribute.new(
57
+ **base_attr_hash(attr),
58
+ is_derived: !!attr.is_derived,
59
+ has_association: !!attr.association
60
+ )
62
61
  end
63
62
 
64
63
  def to_top_element_attribute(attr)
65
- base = base_attr_hash(attr)
66
- Lutaml::Uml::TopElementAttribute.new.tap do |top_attr|
67
- base.each { |k, v| top_attr.public_send(:"#{k}=", v) }
68
- top_attr.is_derived = !!attr.is_derived
69
- end
64
+ Lutaml::Uml::TopElementAttribute.new(
65
+ **base_attr_hash(attr),
66
+ is_derived: !!attr.is_derived
67
+ )
70
68
  end
71
69
 
72
70
  def base_attr_hash(attr)
@@ -35,6 +35,15 @@ module Ea
35
35
  @table_name = table_name
36
36
  end
37
37
 
38
+ # @return [Boolean] true when the table exists in the database
39
+ def table_exists?
40
+ row = @database.get_first_row(
41
+ "SELECT name FROM sqlite_master WHERE type='table' AND name=?",
42
+ [@table_name]
43
+ )
44
+ !row.nil?
45
+ end
46
+
38
47
  # Read all records from the table
39
48
  #
40
49
  # @param limit [Integer, nil] Maximum number of records to return
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Qea
5
+ module Models
6
+ # Author / contributor from t_authors.
7
+ #
8
+ # Schema:
9
+ # (AuthorID INTEGER PRIMARY KEY, Name TEXT, Email TEXT,
10
+ # Notes TEXT, ...)
11
+ class EaAuthor < BaseModel
12
+ attribute :author_id, :integer
13
+ attribute :name, :string
14
+ attribute :email, :string
15
+ attribute :notes, :string
16
+
17
+ def self.primary_key_column
18
+ :author_id
19
+ end
20
+
21
+ def self.table_name
22
+ "t_authors"
23
+ end
24
+
25
+ COLUMN_MAP = {
26
+ "AuthorID" => :author_id
27
+ }.freeze
28
+
29
+ def self.column_map
30
+ COLUMN_MAP
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Qea
5
+ module Models
6
+ # Glossary term from t_glossary.
7
+ #
8
+ # Schema:
9
+ # (Term TEXT, Type TEXT, Meaning TEXT, GlossaryID INTEGER PRIMARY KEY)
10
+ class EaGlossary < BaseModel
11
+ attribute :glossary_id, :integer
12
+ attribute :term, :string
13
+ attribute :type, :string
14
+ attribute :meaning, :string
15
+
16
+ def self.primary_key_column
17
+ :glossary_id
18
+ end
19
+
20
+ def self.table_name
21
+ "t_glossary"
22
+ end
23
+
24
+ COLUMN_MAP = {
25
+ "GlossaryID" => :glossary_id
26
+ }.freeze
27
+
28
+ def self.column_map
29
+ COLUMN_MAP
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Qea
5
+ module Models
6
+ # Raw-row model for `t_image`. EA stores pasted images as EMF
7
+ # (Enhanced Metafile) blobs in this table.
8
+ #
9
+ # `type` values observed in real QEAs:
10
+ # - "ENHMetafile" — Enhanced Windows Metafile (most common)
11
+ # - "Metafile" — Old-style WMF
12
+ # - "Bitmap" — DIB / PNG bitmap
13
+ class EaImage < BaseModel
14
+ attribute :image_id, :integer
15
+ attribute :name, :string
16
+ attribute :type, :string
17
+ attribute :image, :string
18
+
19
+ def self.primary_key_column
20
+ :image_id
21
+ end
22
+
23
+ def self.table_name
24
+ "t_image"
25
+ end
26
+
27
+ COLUMN_MAP = {
28
+ "ImageID" => :image_id
29
+ }.freeze
30
+
31
+ def self.column_map
32
+ COLUMN_MAP
33
+ end
34
+
35
+ # @return [Boolean] true when this row holds an EMF blob
36
+ def emf?
37
+ type&.casecmp?("ENHMetafile")
38
+ end
39
+
40
+ # @return [Boolean] true when this row holds a WMF blob
41
+ def wmf?
42
+ type&.casecmp?("Metafile")
43
+ end
44
+
45
+ # Raw image bytes. SQLite returns BLOB as ASCII-8BIT String.
46
+ # @return [String, nil] binary string
47
+ def bytes
48
+ image
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Qea
5
+ module Models
6
+ # UML-to-code mapping hint from t_implement.
7
+ class EaImplement < BaseModel
8
+ attribute :implement_id, :integer
9
+ attribute :object_id, :integer
10
+ attribute :language, :string
11
+ attribute :code, :string
12
+
13
+ def self.primary_key_column
14
+ :implement_id
15
+ end
16
+
17
+ def self.table_name
18
+ "t_implement"
19
+ end
20
+
21
+ COLUMN_MAP = {
22
+ "ImplementID" => :implement_id,
23
+ "Object_ID" => :object_id
24
+ }.freeze
25
+
26
+ def self.column_map
27
+ COLUMN_MAP
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Qea
5
+ module Models
6
+ # Enumeration list value from t_lists.
7
+ #
8
+ # Schema:
9
+ # (ListID TEXT PRIMARY KEY, Category TEXT, Name TEXT,
10
+ # NVal INTEGER, Notes TEXT)
11
+ class EaList < BaseModel
12
+ attribute :list_id, :string
13
+ attribute :category, :string
14
+ attribute :name, :string
15
+ attribute :nval, :integer
16
+ attribute :notes, :string
17
+
18
+ def self.primary_key_column
19
+ :list_id
20
+ end
21
+
22
+ def self.table_name
23
+ "t_lists"
24
+ end
25
+
26
+ COLUMN_MAP = {
27
+ "ListID" => :list_id,
28
+ "NVal" => :nval
29
+ }.freeze
30
+
31
+ def self.column_map
32
+ COLUMN_MAP
33
+ end
34
+
35
+ # @return [Boolean] true when this row carries a numeric value
36
+ def numeric?
37
+ !nval.nil?
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end