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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +128 -1
- data/Rakefile +3 -0
- data/TODO.complete/01-t-xref-parsing.md +91 -0
- data/TODO.complete/02-per-label-geometry.md +50 -0
- data/TODO.complete/03-object-properties-constraints.md +57 -0
- data/TODO.complete/04-operation-params.md +37 -0
- data/TODO.complete/05-pdata-flag-matrix.md +52 -0
- data/TODO.complete/06-emf-image-rendering.md +34 -0
- data/TODO.complete/07-auxiliary-tables.md +47 -0
- data/TODO.complete/10-mdg-stereotype-registry.md +40 -0
- data/TODO.complete/11-gml-xsd-generation.md +42 -0
- data/TODO.complete/13-shapescript-interpreter.md +28 -0
- data/TODO.complete/20-cli-diff.md +46 -0
- data/TODO.complete/21-cli-render.md +26 -0
- data/TODO.complete/22-cli-export.md +29 -0
- data/TODO.complete/23-cli-mdg.md +23 -0
- data/TODO.complete/30-headerlines-ocp-refactor.md +72 -0
- data/TODO.complete/31-public-send-cleanup.md +61 -0
- data/TODO.complete/32-spec-coverage.md +33 -0
- data/TODO.complete/33-blocked-parity-gaps.md +41 -0
- data/TODO.complete/36-stereotype-icon-wiring.md +32 -0
- data/TODO.complete/37-cli-lint.md +32 -0
- data/TODO.complete/38-cli-query.md +26 -0
- data/TODO.complete/39-cli-info.md +23 -0
- data/TODO.complete/40-ocl-evaluator.md +31 -0
- data/TODO.complete/41-round-trip-spec.md +18 -0
- data/TODO.complete/42-html-diff-report.md +18 -0
- data/TODO.complete/43-json-schema-export.md +20 -0
- data/TODO.complete/44-remaining-qea-tables.md +40 -0
- data/TODO.complete/45-shapescript-extensions.md +25 -0
- data/TODO.complete/46-xsd-consolidation.md +20 -0
- data/TODO.complete/47-headerlines-legacy-removal.md +20 -0
- data/TODO.complete/51-remove-respond-to.md +25 -0
- data/TODO.complete/52-xsd-decouple-fixtures.md +23 -0
- data/TODO.complete/53-xmi-public-send-cleanup.md +26 -0
- data/TODO.complete/54-wire-stereotype-icon-renderer.md +20 -0
- data/TODO.complete/55-wire-shapescript.md +20 -0
- data/TODO.complete/56-wire-emf-renderer.md +27 -0
- data/TODO.complete/57-wire-ocl-evaluator.md +23 -0
- data/TODO.complete/58-diff-modifications.md +21 -0
- data/TODO.complete/59-delete-toy-xmi-export.md +30 -0
- data/TODO.complete/60-curate-json-export.md +20 -0
- data/TODO.complete/61-plantuml-relationships.md +22 -0
- data/TODO.complete/62-changelog.md +17 -0
- data/TODO.complete/63-dependabot-fix.md +18 -0
- data/TODO.complete/STATUS.md +85 -0
- data/TODO.diagrams/01-svg-measurement-harness.md +31 -0
- data/TODO.diagrams/02-polygon-over-rendering.md +29 -0
- data/TODO.diagrams/03-text-over-rendering.md +30 -0
- data/TODO.diagrams/04-rect-under-rendering.md +33 -0
- data/TODO.diagrams/05-canvas-dimension-parity.md +51 -0
- data/TODO.diagrams/06-group-layering-parity.md +39 -0
- data/TODO.diagrams/07-text-content-fidelity.md +65 -0
- data/TODO.diagrams/08-qea-direct-pipeline.md +46 -0
- data/TODO.diagrams/12-connector-labels.md +40 -0
- data/TODO.diagrams/12-per-entity-svg-layer-ordering.md +24 -0
- data/TODO.diagrams/13-marker-polygon-vs-path-dispatch.md +20 -0
- data/TODO.diagrams/13-per-text-type-font-sizes.md +33 -0
- data/TODO.diagrams/14-center-header-text.md +33 -0
- data/TODO.diagrams/14-filter-diagram-frame-elements.md +18 -0
- data/TODO.diagrams/15-deep-dead-code-audit.md +47 -0
- data/TODO.diagrams/15-per-element-font-from-style.md +24 -0
- data/TODO.diagrams/16-canvas-marker-extent.md +30 -0
- data/TODO.diagrams/16-document-remaining-parity-gaps.md +91 -0
- data/TODO.diagrams/17-italic-abstract-classes.md +28 -0
- data/TODO.diagrams/17-remaining-parity-gaps.md +90 -0
- data/TODO.diagrams/18-canvas-from-image-bounds.md +20 -0
- data/TODO.diagrams/18-property-lookup-index.md +42 -0
- data/TODO.diagrams/19-connector-sx-sy-routing.md +27 -0
- data/TODO.diagrams/19-replace-respond-to-with-is-a.md +31 -0
- data/TODO.diagrams/20-split-labels-emitter.md +56 -0
- data/TODO.diagrams/20-visibility-glyph-font-encoding.md +27 -0
- data/TODO.diagrams/21-aggregation-navigability-arrow.md +21 -0
- data/TODO.diagrams/21-auto-generated-legend-block.md +65 -0
- data/TODO.diagrams/22-ghost-element-discriminator.md +57 -0
- data/TODO.diagrams/22-style-based-path-grouping.md +28 -0
- data/TODO.diagrams/23-marker-polygon-deduplication.md +47 -0
- data/TODO.diagrams/23-multiplicity-label-positioning.md +29 -0
- data/TODO.diagrams/24-marker-anchor-structural.md +35 -0
- data/TODO.diagrams/24-track-marker-anchors-structurally.md +66 -0
- data/TODO.diagrams/24-truncate-properties-to-fit-box.md +24 -0
- data/TODO.diagrams/26-phantom-general-parent-rendering.md +59 -0
- data/TODO.diagrams/27-path-grouping-mode-detection.md +43 -0
- data/TODO.diagrams/28-architecture-cleanup-batch.md +47 -0
- data/TODO.diagrams/30-decompose-elements-groups-for.md +52 -0
- data/TODO.diagrams/32-examples-coverage-batch.md +78 -0
- data/TODO.diagrams/36-instance-specification-model.md +42 -0
- data/TODO.diagrams/37-element-icon-decorations.md +41 -0
- data/TODO.diagrams/38-html-note-and-receptions.md +32 -0
- data/TODO.diagrams/39-property-stereotype-discriminator.md +40 -0
- data/TODO.diagrams/40-standalone-1-discriminator.md +38 -0
- data/TODO.diagrams/41-xmi-to-svg-pipeline.md +113 -0
- data/TODO.diagrams/42-close-all-parity-deltas.md +69 -0
- data/TODO.diagrams/43-marker-deduplication-strategy.md +54 -0
- data/TODO.diagrams/44-element-icon-decorations.md +34 -0
- data/TODO.diagrams/45-phantom-parent-class-boxes.md +40 -0
- data/TODO.diagrams/46-user-drawn-legend-swatches.md +44 -0
- data/TODO.diagrams/47-attribute-line-fidelity.md +72 -0
- data/TODO.diagrams/48-spec-coverage-critical-emitter-components.md +45 -0
- data/TODO.diagrams/49-spec-coverage-orchestrators.md +25 -0
- data/TODO.diagrams/50-examples-parity.md +85 -0
- data/TODO.diagrams/51-property-oy-gap-and-remaining-gaps.md +48 -0
- data/TODO.diagrams/52-marker-registration-architecture.md +89 -0
- data/TODO.diagrams/53-remaining-example-gaps.md +86 -0
- data/TODO.diagrams/54-display-name-nested-classes.md +40 -0
- data/TODO.diagrams/55-visibility-icon-discriminator.md +58 -0
- data/TODO.diagrams/56-package-contents-rendering.md +148 -0
- data/TODO.diagrams/57-instance-specification-model.md +100 -0
- data/TODO.diagrams/58-domain-model-note-text.md +67 -0
- data/TODO.diagrams/59-classes-arrow-discriminator.md +57 -0
- data/TODO.diagrams/60-testschema-association-marker.md +40 -0
- data/TODO.diagrams/61-legend-block-rendering.md +55 -0
- data/TODO.diagrams/61-plateau-parity-gaps.md +59 -0
- data/TODO.diagrams/62-ghost-element-rendering.md +56 -0
- data/TODO.diagrams/63-qualified-name-wrap.md +36 -0
- data/TODO.diagrams/64-property-stereotype-labels.md +22 -0
- data/TODO.diagrams/65-path-coordinate-frame.md +62 -0
- data/TODO.diagrams/67-path-coordinate-frame.md +62 -0
- data/TODO.diagrams/68-inherited-attributes.md +65 -0
- data/TODO.diagrams/69-package-anchor-conditional.md +29 -0
- data/TODO.diagrams/70-mdg-technology-support.md +68 -0
- data/TODO.diagrams/71-off-canvas-parent-ghost.md +45 -0
- data/TODO.diagrams/72-basic-attr-divider-paths.md +39 -0
- data/TODO.diagrams/73-plateau-polygon-gap.md +69 -0
- data/TODO.diagrams/74-plateau-rect-gap.md +22 -0
- data/TODO.diagrams/75-plateau-text-gap.md +39 -0
- data/TODO.diagrams/76-basic-poly-path-gaps.md +41 -0
- data/TODO.diagrams/77-basic-text-overrender.md +40 -0
- data/TODO.diagrams/78-headerlines-ocp-refactor.md +65 -0
- data/TODO.diagrams/79-architecture-backlog.md +65 -0
- data/TODO.diagrams/80-basic-triangle-polygons.md +51 -0
- data/TODO.diagrams/81-plateau-path-overrender.md +53 -0
- data/TODO.diagrams/82-appearance-canvas-offset.md +54 -0
- data/TODO.diagrams/83-3d-diagram-subtitle.md +58 -0
- data/TODO.diagrams/84-canvas-offset-mismatches.md +61 -0
- data/TODO.diagrams/85-phantom-property-overrender.md +95 -0
- data/TODO.diagrams/86-foreign-package-rule.md +62 -0
- data/TODO.diagrams/87-internal-tech-encryption.md +80 -0
- data/TODO.diagrams/EXAMPLES_STATUS.md +88 -0
- data/TODO.diagrams/README.md +69 -0
- data/TODO.diagrams/STATUS.md +73 -0
- data/TODO.fixup/01-default-grouped-paths.md +34 -0
- data/TODO.fixup/02-marker-consolidation-per-connector.md +20 -0
- data/TODO.fixup/03-diagram-font-inference.md +28 -0
- data/TODO.fixup/04-extract-bounds-calculator.md +25 -0
- data/TODO.fixup/05-marker-registry-ocp.md +32 -0
- data/TODO.fixup/06-connector-path-bend-routing.md +28 -0
- data/TODO.fixup/07-edge-attachment-formula.md +22 -0
- data/TODO.fixup/08-visual-diff-harness.md +28 -0
- data/TODO.fixup/09-extract-element-compartments.md +34 -0
- data/TODO.fixup/10-canvas-width-overshoot-fix.md +20 -0
- data/TODO.fixup/11-layer-sequencer-extraction.md +18 -0
- data/TODO.fixup/12-path-style-harmony.md +23 -0
- data/TODO.fixup/13-datatype-stereotype-fallback.md +28 -0
- data/TODO.fixup/14-ea-exe-reverse-engineering-notes.md +56 -0
- data/TODO.fixup/15-enumeration-literal-rendering.md +30 -0
- data/TODO.fixup/16-operation-compartment-rendering.md +42 -0
- data/TODO.fixup/17-path-count-calibration.md +27 -0
- data/TODO.fixup/18-polygon-count-calibration.md +32 -0
- data/TODO.fixup/19-rect-count-undershoot-investigation.md +40 -0
- data/TODO.fixup/20-stereotype-color-from-classifier.md +18 -0
- data/TODO.fixup/21-visibility-filter-for-properties.md +24 -0
- data/TODO.fixup/22-multiplicity-display-rule-calibration.md +25 -0
- data/TODO.fixup/23-diagram-frame-title-element.md +31 -0
- data/TODO.fixup/24-note-element-rendering.md +23 -0
- data/TODO.fixup/25-ea-exe-disassembly-findings.md +177 -0
- data/TODO.fixup/26-svg-renderer-architecture.md +78 -0
- data/TODO.fixup/27-svg-element-emitters-disassembly.md +135 -0
- data/TODO.fixup/28-geometry-parser-identification.md +57 -0
- data/TODO.fixup/29-qea-svg-xmi-triple-comparison.md +185 -0
- data/TODO.fixup/30-theme-system-stub.md +46 -0
- data/TODO.fixup/31-theme-value-object-registry.md +17 -0
- data/TODO.fixup/32-styleex-on-diagram-model.md +22 -0
- data/TODO.fixup/33-text-encoding-decimals-rotation.md +29 -0
- data/TODO.fixup/34-per-type-fill-via-theme.md +18 -0
- data/TODO.fixup/35-stroke-width-per-theme.md +7 -0
- data/TODO.fixup/36-qea-rect-coord-normalization.md +24 -0
- data/TODO.fixup/37-package-shape-rendering.md +22 -0
- data/TODO.fixup/38-note-element-rendering.md +15 -0
- data/TODO.fixup/39-styleex-flag-honoring.md +25 -0
- data/TODO.fixup/40-connector-dashed-lines.md +15 -0
- data/TODO.fixup/41-diagram-frame-showborder-detection.md +23 -0
- data/TODO.fixup/42-textrenderer-centralization.md +10 -0
- data/TODO.fixup/43-colorresolver-unification.md +24 -0
- data/TODO.fixup/44-theme-aware-textrenderer.md +17 -0
- data/TODO.fixup/45-theme-aware-fontresolver.md +22 -0
- data/TODO.fixup/46-style-parameterized-by-theme.md +21 -0
- data/TODO.fixup/47-canvas-frame-inset.md +19 -0
- data/TODO.fixup/48-background-rect-format.md +15 -0
- data/TODO.fixup/49-all-samples-integration-test.md +17 -0
- data/TODO.fixup/50-svg-emitter-architecture-docs.md +33 -0
- data/TODO.fixup/51-promote-theme-to-ea-namespace.md +28 -0
- data/TODO.fixup/52-theme-definition-integrated-fills.md +26 -0
- data/TODO.fixup/53-theme-registry-yaml-loading.md +50 -0
- data/TODO.fixup/54-diagram-theme-api.md +50 -0
- data/TODO.fixup/55-update-eaemitter-to-ea-theme.md +23 -0
- data/TODO.fixup/56-theme-yaml-files.md +65 -0
- data/TODO.fixup/57-theme-specs-and-integration.md +27 -0
- data/TODO.fixup/58-ea-entry-point-theme-autoload.md +20 -0
- data/TODO.fixup/59-thread-theme-through-all-renderers.md +29 -0
- data/TODO.fixup/60-cli-theme-option.md +22 -0
- data/TODO.fixup/61-theme-validation.md +28 -0
- data/TODO.fixup/62-theme-builder-dsl.md +31 -0
- data/TODO.fixup/63-theme-integration-rendering-spec.md +24 -0
- data/TODO.fixup/64-theme-writeback-styleex.md +21 -0
- data/TODO.fixup/65-remove-dead-eaemitter-theme-files.md +30 -0
- data/TODO.fixup/66-styleex-writeback.md +22 -0
- data/TODO.fixup/67-delete-dead-code.md +28 -0
- data/TODO.fixup/68-figure-parity-status.md +82 -0
- data/TODO.fixup/69-package-polygon-rendering.md +54 -0
- data/TODO.fixup/70-tagged-values-compartment.md +39 -0
- data/TODO.fixup/71-auto-detect-theme-from-xmi.md +37 -0
- data/TODO.fixup/72-sample-exports-as-regression-specs.md +37 -0
- data/TODO.fixup/73-delete-dead-transformations.md +14 -0
- data/TODO.fixup/74-delete-dead-diagram-renderers.md +18 -0
- data/TODO.fixup/75-visibility-legend-icons.md +23 -0
- data/TODO.fixup/76-plateau-text-undershoot.md +27 -0
- data/TODO.fixup/77-canvas-frame-margins.md +28 -0
- data/TODO.fixup/78-note-element-rendering.md +30 -0
- data/TODO.fixup/79-operation-compartment-rendering.md +27 -0
- data/TODO.fixup/80-tagged-value-stereotype-visibility.md +37 -0
- data/TODO.fixup/81-figure-parity-current-state.md +63 -0
- data/config/qea_schema.yml +356 -0
- data/config/themes/119.yml +98 -0
- data/config/themes/default.yml +14 -0
- data/examples/exports/20251010_current_plateau_v5/UML_EA.DTD +6856 -0
- data/examples/exports/20251010_current_plateau_v5/model.xml +96709 -0
- data/examples/exports/ArcGISWorkspace_template/UML_EA.DTD +6856 -0
- data/examples/exports/ArcGISWorkspace_template/model.xml +1427 -0
- data/examples/exports/UmlModel_template/UML_EA.DTD +6856 -0
- data/examples/exports/UmlModel_template/model.xml +23 -0
- data/examples/exports/basic/UML_EA.DTD +6856 -0
- data/examples/exports/basic/model.xml +8039 -0
- data/examples/exports/simple/UML_EA.DTD +6856 -0
- data/examples/exports/simple/model.xml +585 -0
- data/examples/exports/simple_example/UML_EA.DTD +6856 -0
- data/examples/exports/simple_example/model.xml +23 -0
- data/examples/exports/test/UML_EA.DTD +6856 -0
- data/examples/exports/test/model.xml +1638 -0
- data/examples/qea/basic.qea +0 -0
- data/frontend/README.md +51 -0
- data/frontend/dist/app.iife.js +24 -0
- data/frontend/dist/style.css +1 -0
- data/frontend/index.html +26 -0
- data/frontend/package-lock.json +2359 -0
- data/frontend/package.json +26 -0
- data/frontend/src/App.vue +59 -0
- data/frontend/src/app.ts +8 -0
- data/frontend/src/components/AppHeader.vue +127 -0
- data/frontend/src/components/AppSidebar.vue +119 -0
- data/frontend/src/components/ClassDetails.vue +247 -0
- data/frontend/src/components/DiagramView.vue +79 -0
- data/frontend/src/components/PackageDetails.vue +92 -0
- data/frontend/src/components/PackageTreeNode.vue +64 -0
- data/frontend/src/components/SearchResults.vue +48 -0
- data/frontend/src/components/WelcomeView.vue +87 -0
- data/frontend/src/shims-vue.d.ts +5 -0
- data/frontend/src/stores/dataStore.ts +79 -0
- data/frontend/src/stores/uiStore.ts +167 -0
- data/frontend/src/styles/main.css +1566 -0
- data/frontend/src/types.ts +221 -0
- data/frontend/tests/e2e/__screenshots__/new-dark-mode.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-package-details.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-search-modal.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-sidebar-collapsed.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-welcome.png +0 -0
- data/frontend/tests/e2e/render.spec.ts +188 -0
- data/frontend/tsconfig.json +20 -0
- data/frontend/vite.config.ts +26 -0
- data/frontend/vitest.config.ts +8 -0
- data/lib/ea/cli/app.rb +57 -2
- data/lib/ea/cli/command/diagrams.rb +37 -28
- data/lib/ea/cli/command/diff.rb +46 -0
- data/lib/ea/cli/command/export.rb +101 -0
- data/lib/ea/cli/command/info.rb +75 -0
- data/lib/ea/cli/command/lint.rb +48 -0
- data/lib/ea/cli/command/mdg.rb +98 -0
- data/lib/ea/cli/command/query.rb +34 -0
- data/lib/ea/cli/command/render.rb +135 -0
- data/lib/ea/cli/command/svg.rb +8 -15
- data/lib/ea/cli/command.rb +7 -0
- data/lib/ea/diagram/display_config.rb +184 -0
- data/lib/ea/diagram.rb +4 -41
- data/lib/ea/diff/change.rb +30 -0
- data/lib/ea/diff/comparator.rb +109 -0
- data/lib/ea/diff/html_reporter.rb +91 -0
- data/lib/ea/diff.rb +15 -0
- data/lib/ea/export/json/generator.rb +118 -0
- data/lib/ea/export/json.rb +12 -0
- data/lib/ea/export/json_schema/generator.rb +77 -0
- data/lib/ea/export/json_schema.rb +12 -0
- data/lib/ea/export/plantuml/generator.rb +140 -0
- data/lib/ea/export/plantuml.rb +12 -0
- data/lib/ea/export/xsd/class_mapping.rb +65 -0
- data/lib/ea/export/xsd/generator.rb +137 -0
- data/lib/ea/export/xsd/namespace_registry.rb +82 -0
- data/lib/ea/export/xsd.rb +19 -0
- data/lib/ea/export.rb +13 -0
- data/lib/ea/image/emf_renderer.rb +42 -0
- data/lib/ea/image.rb +12 -0
- data/lib/ea/lint/engine.rb +30 -0
- data/lib/ea/lint/lint_rule.rb +38 -0
- data/lib/ea/lint/offense.rb +18 -0
- data/lib/ea/lint/rules/cyclic_generalization.rb +62 -0
- data/lib/ea/lint/rules/duplicate_name.rb +28 -0
- data/lib/ea/lint/rules/missing_stereotype.rb +40 -0
- data/lib/ea/lint/rules/naming_convention.rb +44 -0
- data/lib/ea/lint/rules/orphan_element.rb +28 -0
- data/lib/ea/lint/rules.rb +14 -0
- data/lib/ea/lint.rb +13 -0
- data/lib/ea/mdg/document.rb +120 -0
- data/lib/ea/mdg/loader.rb +118 -0
- data/lib/ea/mdg/registry.rb +98 -0
- data/lib/ea/mdg/stereotype_alias_registry.rb +77 -0
- data/lib/ea/mdg/xml/applies_to.rb +18 -0
- data/lib/ea/mdg/xml/apply.rb +18 -0
- data/lib/ea/mdg/xml/content.rb +18 -0
- data/lib/ea/mdg/xml/documentation.rb +24 -0
- data/lib/ea/mdg/xml/stereotype.rb +28 -0
- data/lib/ea/mdg/xml/stereotypes.rb +18 -0
- data/lib/ea/mdg/xml/tag.rb +28 -0
- data/lib/ea/mdg/xml/tagged_values.rb +18 -0
- data/lib/ea/mdg/xml/technology.rb +28 -0
- data/lib/ea/mdg/xml/uml_profile.rb +22 -0
- data/lib/ea/mdg/xml/uml_profiles.rb +18 -0
- data/lib/ea/mdg/xml.rb +24 -0
- data/lib/ea/mdg.rb +25 -0
- data/lib/ea/model/classifier.rb +4 -0
- data/lib/ea/model/constraint.rb +24 -0
- data/lib/ea/model/diagram.rb +85 -0
- data/lib/ea/model/diagram_connector.rb +34 -0
- data/lib/ea/model/diagram_element.rb +16 -0
- data/lib/ea/model/document.rb +47 -2
- data/lib/ea/model/ghost_label.rb +32 -0
- data/lib/ea/model/instance_specification.rb +34 -0
- data/lib/ea/model/legend.rb +43 -0
- data/lib/ea/model/legend_item.rb +26 -0
- data/lib/ea/model/note.rb +28 -0
- data/lib/ea/model/operation.rb +2 -0
- data/lib/ea/model/property.rb +2 -0
- data/lib/ea/model/relationship.rb +2 -0
- data/lib/ea/model/signal.rb +14 -0
- data/lib/ea/model/slot.rb +19 -0
- data/lib/ea/model.rb +8 -0
- data/lib/ea/ocl/evaluator.rb +94 -0
- data/lib/ea/ocl/nodes.rb +30 -0
- data/lib/ea/ocl/parser.rb +133 -0
- data/lib/ea/ocl/unsupported_error.rb +7 -0
- data/lib/ea/ocl.rb +13 -0
- data/lib/ea/qea/factory/document_builder.rb +24 -10
- data/lib/ea/qea/factory/generalization_builder.rb +9 -11
- data/lib/ea/qea/infrastructure/table_reader.rb +9 -0
- data/lib/ea/qea/models/ea_author.rb +35 -0
- data/lib/ea/qea/models/ea_glossary.rb +34 -0
- data/lib/ea/qea/models/ea_image.rb +53 -0
- data/lib/ea/qea/models/ea_implement.rb +32 -0
- data/lib/ea/qea/models/ea_list.rb +42 -0
- data/lib/ea/qea/models/ea_object_effort.rb +34 -0
- data/lib/ea/qea/models/ea_object_problem.rb +37 -0
- data/lib/ea/qea/models/ea_object_require.rb +34 -0
- data/lib/ea/qea/models/ea_object_resource.rb +34 -0
- data/lib/ea/qea/models/ea_object_risk.rb +34 -0
- data/lib/ea/qea/models/ea_object_scenario.rb +34 -0
- data/lib/ea/qea/models/ea_object_test.rb +35 -0
- data/lib/ea/qea/models/ea_object_trx.rb +34 -0
- data/lib/ea/qea/models/ea_operation.rb +7 -0
- data/lib/ea/qea/models/ea_palette.rb +30 -0
- data/lib/ea/qea/models/ea_palette_item.rb +36 -0
- data/lib/ea/qea/models/ea_phase.rb +40 -0
- data/lib/ea/qea/models/ea_role_constraint.rb +32 -0
- data/lib/ea/qea/models/ea_secrypt.rb +34 -0
- data/lib/ea/qea/models/ea_version.rb +39 -0
- data/lib/ea/qea/models/ea_xref.rb +11 -1
- data/lib/ea/qea/models.rb +19 -0
- data/lib/ea/qea/services/database_loader.rb +22 -1
- data/lib/ea/qea/validation/database/ocl_constraint_validator.rb +65 -0
- data/lib/ea/qea/validation/database.rb +2 -0
- data/lib/ea/qea/validation/validation_engine.rb +2 -0
- data/lib/ea/qea/validation.rb +2 -0
- data/lib/ea/query/builder.rb +127 -0
- data/lib/ea/query.rb +9 -0
- data/lib/ea/render/image_converter.rb +101 -0
- data/lib/ea/render.rb +10 -0
- data/lib/ea/shapescript/parser.rb +208 -0
- data/lib/ea/shapescript/renderer.rb +74 -0
- data/lib/ea/shapescript/shape.rb +34 -0
- data/lib/ea/shapescript.rb +24 -0
- data/lib/ea/sources/qea/adapter.rb +22 -5
- data/lib/ea/sources/qea/classifier_builder.rb +184 -7
- data/lib/ea/sources/qea/diagram_builder.rb +352 -24
- data/lib/ea/sources/qea/id_normalizer.rb +16 -1
- data/lib/ea/sources/qea/instance_builder.rb +113 -0
- data/lib/ea/sources/qea/legend_builder.rb +137 -0
- data/lib/ea/sources/qea/note_builder.rb +58 -0
- data/lib/ea/sources/qea/object_classifier_map.rb +6 -1
- data/lib/ea/sources/qea/operation_builder.rb +1 -0
- data/lib/ea/sources/qea/package_builder.rb +16 -0
- data/lib/ea/sources/qea/relationship_builder.rb +21 -5
- data/lib/ea/sources/qea/tagged_value_builder.rb +28 -9
- data/lib/ea/sources/qea/xref/legend_definition.rb +35 -0
- data/lib/ea/sources/qea/xref/parser.rb +150 -0
- data/lib/ea/sources/qea/xref/property_definition.rb +29 -0
- data/lib/ea/sources/qea/xref/record.rb +41 -0
- data/lib/ea/sources/qea/xref/stereotype_application.rb +37 -0
- data/lib/ea/sources/qea/xref.rb +27 -0
- data/lib/ea/sources/qea.rb +4 -0
- data/lib/ea/sources/xmi/adapter.rb +80 -2
- data/lib/ea/sources/xmi/classifier_builder.rb +10 -1
- data/lib/ea/sources/xmi/diagram_builder.rb +95 -18
- data/lib/ea/sources/xmi/extension_geometry_parser.rb +19 -2
- data/lib/ea/sources/xmi/extension_style_parser.rb +15 -2
- data/lib/ea/sources/xmi/note_builder.rb +46 -0
- data/lib/ea/sources/xmi/property_builder.rb +9 -0
- data/lib/ea/sources/xmi/stereotype_builder.rb +54 -0
- data/lib/ea/sources/xmi/tag_builder.rb +45 -0
- data/lib/ea/sources/xmi/umldi_keyword_extractor.rb +87 -0
- data/lib/ea/sources/xmi.rb +4 -0
- data/lib/ea/spa/output/sharded_multi_file_strategy.rb +18 -3
- data/lib/ea/spa/output/single_file_strategy.rb +8 -2
- data/lib/ea/spa/output/strategy.rb +34 -0
- data/lib/ea/svg/connector_router.rb +134 -0
- data/lib/ea/svg/ea_emitter/background.rb +2 -3
- data/lib/ea/svg/ea_emitter/bounds_calculator.rb +142 -0
- data/lib/ea/svg/ea_emitter/canvas.rb +56 -33
- data/lib/ea/svg/ea_emitter/color_resolver.rb +56 -0
- data/lib/ea/svg/ea_emitter/compartment/attributes.rb +28 -0
- data/lib/ea/svg/ea_emitter/compartment/constraints.rb +30 -0
- data/lib/ea/svg/ea_emitter/compartment/enum_literals.rb +86 -0
- data/lib/ea/svg/ea_emitter/compartment/header.rb +32 -0
- data/lib/ea/svg/ea_emitter/compartment/header_divider.rb +57 -0
- data/lib/ea/svg/ea_emitter/compartment/instance_slots.rb +96 -0
- data/lib/ea/svg/ea_emitter/compartment/instance_underline.rb +67 -0
- data/lib/ea/svg/ea_emitter/compartment/note_body.rb +52 -0
- data/lib/ea/svg/ea_emitter/compartment/note_text.rb +102 -0
- data/lib/ea/svg/ea_emitter/compartment/operations.rb +38 -0
- data/lib/ea/svg/ea_emitter/compartment/package_contents.rb +72 -0
- data/lib/ea/svg/ea_emitter/compartment/package_from_parent.rb +136 -0
- data/lib/ea/svg/ea_emitter/compartment/shape.rb +82 -0
- data/lib/ea/svg/ea_emitter/compartment/stereotype_icon.rb +32 -0
- data/lib/ea/svg/ea_emitter/compartment/tagged_values.rb +30 -0
- data/lib/ea/svg/ea_emitter/compartment.rb +60 -0
- data/lib/ea/svg/ea_emitter/connectors.rb +46 -12
- data/lib/ea/svg/ea_emitter/diagram_frame.rb +143 -0
- data/lib/ea/svg/ea_emitter/document.rb +49 -25
- data/lib/ea/svg/ea_emitter/element/attribute_line_builder.rb +117 -0
- data/lib/ea/svg/ea_emitter/element/attribute_renderer.rb +119 -0
- data/lib/ea/svg/ea_emitter/element/bcol_decoder.rb +29 -0
- data/lib/ea/svg/ea_emitter/element/compartment_geometry.rb +113 -0
- data/lib/ea/svg/ea_emitter/element/constraint_renderer.rb +53 -0
- data/lib/ea/svg/ea_emitter/element/divider_renderer.rb +17 -0
- data/lib/ea/svg/ea_emitter/element/enumeration_literal_renderer.rb +34 -0
- data/lib/ea/svg/ea_emitter/element/filter.rb +44 -0
- data/lib/ea/svg/ea_emitter/element/hand_draw_shape_renderer.rb +77 -0
- data/lib/ea/svg/ea_emitter/element/header_line_pipeline.rb +43 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/instance_spec.rb +27 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/name.rb +76 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/parent_ghost.rb +25 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/stereotype_label.rb +68 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider.rb +19 -0
- data/lib/ea/svg/ea_emitter/element/header_lines.rb +27 -0
- data/lib/ea/svg/ea_emitter/element/header_renderer.rb +48 -0
- data/lib/ea/svg/ea_emitter/element/icon_renderer.rb +109 -0
- data/lib/ea/svg/ea_emitter/element/legend_renderer.rb +177 -0
- data/lib/ea/svg/ea_emitter/element/note_shape_renderer.rb +112 -0
- data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +100 -0
- data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +181 -0
- data/lib/ea/svg/ea_emitter/element/row_icon_renderer.rb +156 -0
- data/lib/ea/svg/ea_emitter/element/shape_renderer.rb +17 -0
- data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +119 -0
- data/lib/ea/svg/ea_emitter/element/tagged_value_renderer.rb +50 -0
- data/lib/ea/svg/ea_emitter/element/text_escape.rb +20 -0
- data/lib/ea/svg/ea_emitter/element.rb +37 -0
- data/lib/ea/svg/ea_emitter/elements.rb +283 -95
- data/lib/ea/svg/ea_emitter/font_resolver.rb +111 -0
- data/lib/ea/svg/ea_emitter/ghost_labels.rb +66 -0
- data/lib/ea/svg/ea_emitter/label/end_label.rb +266 -0
- data/lib/ea/svg/ea_emitter/label/midpoint_label.rb +101 -0
- data/lib/ea/svg/ea_emitter/label/registry.rb +50 -0
- data/lib/ea/svg/ea_emitter/label.rb +23 -0
- data/lib/ea/svg/ea_emitter/labels.rb +93 -27
- data/lib/ea/svg/ea_emitter/layer.rb +20 -0
- data/lib/ea/svg/ea_emitter/layer_sequencer.rb +93 -0
- data/lib/ea/svg/ea_emitter/marker/arrow_path.rb +52 -0
- data/lib/ea/svg/ea_emitter/marker/diamond.rb +57 -0
- data/lib/ea/svg/ea_emitter/marker/kind.rb +35 -0
- data/lib/ea/svg/ea_emitter/marker/open_triangle.rb +27 -0
- data/lib/ea/svg/ea_emitter/marker/package_import.rb +39 -0
- data/lib/ea/svg/ea_emitter/marker/plus.rb +40 -0
- data/lib/ea/svg/ea_emitter/marker/registry.rb +42 -0
- data/lib/ea/svg/ea_emitter/marker.rb +37 -0
- data/lib/ea/svg/ea_emitter/markers.rb +351 -64
- data/lib/ea/svg/ea_emitter/render_context.rb +52 -0
- data/lib/ea/svg/ea_emitter/style.rb +15 -0
- data/lib/ea/svg/ea_emitter/text_renderer.rb +92 -0
- data/lib/ea/svg/ea_emitter.rb +33 -11
- data/lib/ea/svg/parity/checker.rb +105 -0
- data/lib/ea/svg/parity/source.rb +59 -0
- data/lib/ea/svg/parity/suite.rb +113 -0
- data/lib/ea/svg/parity.rb +14 -0
- data/lib/ea/svg.rb +2 -15
- data/lib/ea/theme/definition.rb +176 -0
- data/lib/ea/theme/loader.rb +125 -0
- data/lib/ea/theme/registry.rb +96 -0
- data/lib/ea/theme.rb +37 -0
- data/lib/ea/transformations.rb +5 -88
- data/lib/ea/validation/xmi_parity.rb +80 -0
- data/lib/ea/validation.rb +12 -0
- data/lib/ea/version.rb +4 -1
- data/lib/ea.rb +15 -4
- data/lib/tasks/svg_bench.rake +87 -0
- data/lutaml-uml-0.5.1.gem +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes-list.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-2-gradients.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-3-colors.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance-fonts.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-5-behavior.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-6-sequence.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-2-objects.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-3-links.png +0 -0
- metadata +464 -26
- data/lib/ea/diagram/configuration.rb +0 -379
- data/lib/ea/diagram/element_renderers/base_renderer.rb +0 -77
- data/lib/ea/diagram/element_renderers/class_renderer.rb +0 -323
- data/lib/ea/diagram/element_renderers/connector_renderer.rb +0 -41
- data/lib/ea/diagram/element_renderers/package_renderer.rb +0 -61
- data/lib/ea/diagram/element_renderers.rb +0 -43
- data/lib/ea/diagram/extractor.rb +0 -566
- data/lib/ea/diagram/layout_engine.rb +0 -170
- data/lib/ea/diagram/path_builder.rb +0 -202
- data/lib/ea/diagram/style_parser.rb +0 -42
- data/lib/ea/diagram/style_resolver.rb +0 -291
- data/lib/ea/diagram/svg_renderer.rb +0 -274
- data/lib/ea/diagram/util.rb +0 -73
- data/lib/ea/svg/bounds_calculator.rb +0 -74
- data/lib/ea/svg/connector_path.rb +0 -138
- data/lib/ea/svg/element_box.rb +0 -256
- data/lib/ea/svg/renderer.rb +0 -77
- data/lib/ea/svg/style_resolver.rb +0 -72
- data/lib/ea/transformations/configuration.rb +0 -333
- data/lib/ea/transformations/format_registry.rb +0 -366
- data/lib/ea/transformations/parsers/base_parser.rb +0 -482
- data/lib/ea/transformations/parsers/qea_parser.rb +0 -401
- data/lib/ea/transformations/parsers/xmi_parser.rb +0 -243
- data/lib/ea/transformations/transformation_engine.rb +0 -390
|
@@ -26,6 +26,12 @@ module Ea
|
|
|
26
26
|
package_id: package_id_for(diagram_row),
|
|
27
27
|
diagram_type: diagram_row.diagram_type,
|
|
28
28
|
bounds: bounds_for(diagram_row),
|
|
29
|
+
style: diagram_row.pdata,
|
|
30
|
+
style_ex: diagram_row.styleex,
|
|
31
|
+
show_package_contents: package_contents_enabled?(diagram_row),
|
|
32
|
+
hand_draw: hand_draw_enabled?(diagram_row),
|
|
33
|
+
show_notes: show_notes_enabled?(diagram_row),
|
|
34
|
+
show_parents: show_parents_enabled?(diagram_row),
|
|
29
35
|
elements: build_elements(diagram_row),
|
|
30
36
|
connectors: build_connectors(diagram_row),
|
|
31
37
|
annotations: AnnotationBuilder.from_note(diagram_row.notes,
|
|
@@ -34,6 +40,42 @@ module Ea
|
|
|
34
40
|
)
|
|
35
41
|
end
|
|
36
42
|
|
|
43
|
+
# EA encodes "show package contents" as a non-zero integer in
|
|
44
|
+
# t_diagram.ShowPackageContents (1 in basic.qea, 255 in
|
|
45
|
+
# test.qea). When true, the package body lists its child
|
|
46
|
+
# classifiers/sub-packages as "+ Name" rows.
|
|
47
|
+
def package_contents_enabled?(diagram_row)
|
|
48
|
+
return false if diagram_row.showpackagecontents.nil?
|
|
49
|
+
|
|
50
|
+
diagram_row.showpackagecontents.to_i.nonzero?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# HandDraw=1 in t_diagram.StyleEx triggers EA's wavy
|
|
54
|
+
# hand-drawn border style for every element on the diagram.
|
|
55
|
+
def hand_draw_enabled?(diagram_row)
|
|
56
|
+
style_ex = diagram_row.styleex.to_s
|
|
57
|
+
match = style_ex.match(/HandDraw=(\d)/)
|
|
58
|
+
match && match[1] == "1"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# ShowNotes=1 in t_diagram.StyleEx renders the classifier's
|
|
62
|
+
# documentation note as wrapped text in the element body.
|
|
63
|
+
def show_notes_enabled?(diagram_row)
|
|
64
|
+
style_ex = diagram_row.styleex.to_s
|
|
65
|
+
match = style_ex.match(/ShowNotes=(\d)/)
|
|
66
|
+
match && match[1] == "1"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# HideParents=1 in t_diagram.PDATA suppresses EA's "off-canvas
|
|
70
|
+
# parent class" ghost line — the italic parent name that EA
|
|
71
|
+
# normally prepends to a placed element's header when its
|
|
72
|
+
# generalization parent is not placed on the diagram.
|
|
73
|
+
def show_parents_enabled?(diagram_row)
|
|
74
|
+
pdata = diagram_row.pdata.to_s
|
|
75
|
+
match = pdata.match(/HideParents=(\d)/)
|
|
76
|
+
match ? match[1] != "1" : true
|
|
77
|
+
end
|
|
78
|
+
|
|
37
79
|
private
|
|
38
80
|
|
|
39
81
|
def build_elements(diagram_row)
|
|
@@ -42,41 +84,245 @@ module Ea
|
|
|
42
84
|
end
|
|
43
85
|
|
|
44
86
|
def build_element(obj_row, diagram_row)
|
|
87
|
+
style_hash = DiagramStyleParser.parse(obj_row.objectstyle)
|
|
45
88
|
Ea::Model::DiagramElement.new(
|
|
46
89
|
id: IdNormalizer.synthetic("de", diagram_row.diagram_id,
|
|
47
90
|
obj_row.instance_id),
|
|
48
91
|
diagram_id: IdNormalizer.from_guid(diagram_row.ea_guid),
|
|
49
92
|
model_element_ref: ref_for_object(obj_row),
|
|
50
93
|
bounds: bounds_from_rect(obj_row),
|
|
51
|
-
style:
|
|
94
|
+
style: style_hash,
|
|
95
|
+
font_family: style_hash[:font],
|
|
96
|
+
font_bold: truthy?(style_hash[:bold]),
|
|
97
|
+
font_italic: truthy?(style_hash[:italic]),
|
|
98
|
+
show_tagged_values: truthy?(style_hash[:tag])
|
|
52
99
|
)
|
|
53
100
|
end
|
|
54
101
|
|
|
102
|
+
def truthy?(raw)
|
|
103
|
+
raw == "1" || raw == "-1"
|
|
104
|
+
end
|
|
105
|
+
|
|
55
106
|
def build_connectors(diagram_row)
|
|
56
107
|
links = database.diagram_links_for(diagram_row.diagram_id) || []
|
|
57
|
-
links.map { |link_row| build_connector(link_row, diagram_row) }
|
|
108
|
+
explicit = links.map { |link_row| build_connector(link_row, diagram_row) }
|
|
109
|
+
phantom = phantom_connectors(diagram_row, explicit)
|
|
110
|
+
explicit + phantom
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# EA renders PHANTOM CONNECTORS for associations that exist
|
|
114
|
+
# in t_connector between two elements BOTH placed on the
|
|
115
|
+
# current diagram, even when no t_diagramlinks entry exists
|
|
116
|
+
# for that connector on this diagram. The phantom connector
|
|
117
|
+
# renders as a straight line between the two element edges,
|
|
118
|
+
# with SourceRole labels and appropriate markers.
|
|
119
|
+
#
|
|
120
|
+
# Only Association and Aggregation types produce phantom
|
|
121
|
+
# connectors. Generalization, Dependency, and Package types
|
|
122
|
+
# do NOT get phantom rendering.
|
|
123
|
+
PHANTOM_CONNECTOR_TYPES = %w[Association Aggregation].freeze
|
|
124
|
+
|
|
125
|
+
def phantom_connectors(diagram_row, explicit_connectors)
|
|
126
|
+
placed_ids = placed_object_ids_on(diagram_row.diagram_id)
|
|
127
|
+
explicit_conn_ids = explicit_connectors.map(&:relationship_ref).compact.to_set
|
|
128
|
+
|
|
129
|
+
all_connectors = database.collections[:connectors] || []
|
|
130
|
+
all_connectors.filter_map.with_index do |conn, idx|
|
|
131
|
+
next nil unless PHANTOM_CONNECTOR_TYPES.include?(conn.connector_type)
|
|
132
|
+
next nil unless placed_ids.include?(conn.start_object_id.to_i)
|
|
133
|
+
next nil unless placed_ids.include?(conn.end_object_id.to_i)
|
|
134
|
+
|
|
135
|
+
ref = ref_for_raw_connector(conn)
|
|
136
|
+
next nil if explicit_conn_ids.include?(ref)
|
|
137
|
+
|
|
138
|
+
build_phantom_connector(conn, diagram_row, idx)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def ref_for_raw_connector(conn)
|
|
143
|
+
IdNormalizer.from_guid(conn.ea_guid)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Default label geometry for phantom connectors. EA renders
|
|
147
|
+
# SourceRole + «property» + multiplicity at default offsets
|
|
148
|
+
# from the source endpoint when no explicit geometry exists.
|
|
149
|
+
DEFAULT_LABEL_BOXES = {
|
|
150
|
+
llb: { "ox" => 0, "oy" => 0 },
|
|
151
|
+
llt: { "ox" => 0, "oy" => 0 }
|
|
152
|
+
}.freeze
|
|
153
|
+
|
|
154
|
+
def build_phantom_connector(conn, diagram_row, idx)
|
|
155
|
+
source_placement = diagram_object_placement(diagram_row.diagram_id,
|
|
156
|
+
conn.start_object_id)
|
|
157
|
+
target_placement = diagram_object_placement(diagram_row.diagram_id,
|
|
158
|
+
conn.end_object_id)
|
|
159
|
+
return nil unless source_placement && target_placement
|
|
160
|
+
|
|
161
|
+
source_bounds = bounds_from_rect(source_placement)
|
|
162
|
+
target_bounds = bounds_from_rect(target_placement)
|
|
163
|
+
source_point = bounds_edge_point(source_bounds, target_bounds)
|
|
164
|
+
target_point = bounds_edge_point(target_bounds, source_bounds)
|
|
165
|
+
|
|
166
|
+
waypoints = [
|
|
167
|
+
Ea::Model::Waypoint.new(position: Ea::Model::Point.new(x: source_point[0], y: source_point[1])),
|
|
168
|
+
Ea::Model::Waypoint.new(position: Ea::Model::Point.new(x: target_point[0], y: target_point[1]))
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
Ea::Model::DiagramConnector.new(
|
|
172
|
+
id: IdNormalizer.synthetic("pc", diagram_row.diagram_id, idx),
|
|
173
|
+
diagram_id: IdNormalizer.from_guid(diagram_row.ea_guid),
|
|
174
|
+
relationship_ref: ref_for_raw_connector(conn),
|
|
175
|
+
connector_type: conn.connector_type,
|
|
176
|
+
direction: conn.direction,
|
|
177
|
+
waypoints: waypoints,
|
|
178
|
+
label_boxes: DEFAULT_LABEL_BOXES,
|
|
179
|
+
style: {},
|
|
180
|
+
hidden: false,
|
|
181
|
+
has_geometry_offsets: false
|
|
182
|
+
)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Computes the edge attachment point on source bounds that
|
|
186
|
+
# faces the target bounds. Uses the center-to-center direction
|
|
187
|
+
# to pick the nearest edge intersection.
|
|
188
|
+
def bounds_edge_point(bounds, other_bounds)
|
|
189
|
+
cx = bounds.x + bounds.width / 2
|
|
190
|
+
cy = bounds.y + bounds.height / 2
|
|
191
|
+
ocx = other_bounds.x + other_bounds.width / 2
|
|
192
|
+
ocy = other_bounds.y + other_bounds.height / 2
|
|
193
|
+
|
|
194
|
+
dx = ocx - cx
|
|
195
|
+
dy = ocy - cy
|
|
196
|
+
|
|
197
|
+
if dx.abs * bounds.height > dy.abs * bounds.width
|
|
198
|
+
x = dx > 0 ? bounds.x + bounds.width : bounds.x
|
|
199
|
+
y = cy + dy * (x - cx).abs / dx.abs rescue cy
|
|
200
|
+
[x, y]
|
|
201
|
+
else
|
|
202
|
+
y = dy > 0 ? bounds.y + bounds.height : bounds.y
|
|
203
|
+
x = cx + dx * (y - cy).abs / dy.abs rescue cx
|
|
204
|
+
[x, y]
|
|
205
|
+
end
|
|
58
206
|
end
|
|
59
207
|
|
|
60
208
|
def build_connector(link_row, diagram_row)
|
|
209
|
+
connector = database.find_connector(link_row.connectorid)
|
|
210
|
+
geom = parse_geometry_fields(link_row.geometry)
|
|
61
211
|
Ea::Model::DiagramConnector.new(
|
|
62
212
|
id: IdNormalizer.synthetic("dc", diagram_row.diagram_id,
|
|
63
213
|
link_row.instance_id),
|
|
64
214
|
diagram_id: IdNormalizer.from_guid(diagram_row.ea_guid),
|
|
65
215
|
relationship_ref: ref_for_connector(link_row),
|
|
216
|
+
connector_type: connector&.connector_type,
|
|
217
|
+
direction: connector&.direction,
|
|
66
218
|
waypoints: waypoints_for_link(link_row, diagram_row),
|
|
67
|
-
|
|
219
|
+
label_boxes: geom[:label_boxes] || {},
|
|
220
|
+
style: DiagramStyleParser.parse(link_row.style),
|
|
221
|
+
hidden: hidden?(link_row),
|
|
222
|
+
ghost_labels: ghost_labels_for(connector, diagram_row, link_row),
|
|
223
|
+
has_geometry_offsets: geometry_has_offsets?(link_row.geometry)
|
|
68
224
|
)
|
|
69
225
|
end
|
|
70
226
|
|
|
71
|
-
# EA's t_diagramlinks.Geometry
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
227
|
+
# EA's t_diagramlinks.Geometry may carry explicit SX/SY/EX/EY
|
|
228
|
+
# offset fields (even zeros) OR omit them entirely. The
|
|
229
|
+
# presence of SX= gates the «import» package_anchor marker:
|
|
230
|
+
# basic.qea Package Imports has SX=0;SY=0 → marker renders;
|
|
231
|
+
# plateau Urban Planning ADE2 omits SX/SY → no marker.
|
|
232
|
+
def geometry_has_offsets?(geometry_str)
|
|
233
|
+
return false if geometry_str.nil? || geometry_str.empty?
|
|
234
|
+
|
|
235
|
+
geometry_str.to_s.include?("SX=")
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# EA's t_diagramlinks.Hidden is 1 when the user has hidden
|
|
239
|
+
# the connector on this diagram (right-click → Hide). Hidden
|
|
240
|
+
# connectors are absent from the reference SVG output.
|
|
241
|
+
def hidden?(link_row)
|
|
242
|
+
link_row.hidden.to_i.nonzero?
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Builds GhostLabel entries for connector endpoints that
|
|
246
|
+
# reference objects NOT placed on the current diagram. EA
|
|
247
|
+
# renders the off-canvas classifier's name as italic text
|
|
248
|
+
# near the connector's on-canvas endpoint so the reader
|
|
249
|
+
# knows what the connector attaches to outside the diagram.
|
|
250
|
+
def ghost_labels_for(connector, diagram_row, link_row)
|
|
251
|
+
return [] unless connector
|
|
252
|
+
|
|
253
|
+
placed_ids = placed_object_ids_on(diagram_row.diagram_id)
|
|
254
|
+
ghosts = []
|
|
255
|
+
%i[source target].each do |end_kind|
|
|
256
|
+
ea_object_id = end_kind == :source ? connector.start_object_id : connector.end_object_id
|
|
257
|
+
next unless ea_object_id&.to_i&.positive?
|
|
258
|
+
next if placed_ids.include?(ea_object_id.to_i)
|
|
259
|
+
|
|
260
|
+
obj = database.find_object(ea_object_id.to_i)
|
|
261
|
+
next unless obj
|
|
262
|
+
next if obj.name.to_s.strip.empty?
|
|
263
|
+
|
|
264
|
+
anchor_point = ghost_anchor(end_kind, connector, link_row, diagram_row)
|
|
265
|
+
next unless anchor_point
|
|
266
|
+
|
|
267
|
+
ghosts << Ea::Model::GhostLabel.new(
|
|
268
|
+
id: IdNormalizer.synthetic("ghost", link_row.instance_id, ea_object_id.to_i),
|
|
269
|
+
name: obj.name.to_s,
|
|
270
|
+
end_kind: end_kind.to_s,
|
|
271
|
+
anchor_x: anchor_point[0],
|
|
272
|
+
anchor_y: anchor_point[1]
|
|
273
|
+
)
|
|
274
|
+
end
|
|
275
|
+
ghosts
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Returns the Array of EA object IDs that have a placement
|
|
279
|
+
# (t_diagramobjects row) on the given diagram.
|
|
280
|
+
def placed_object_ids_on(diagram_id)
|
|
281
|
+
objects = database.diagram_objects_for(diagram_id) || []
|
|
282
|
+
objects.map(&:ea_object_id)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# Ghost label anchors at the on-canvas end of the connector.
|
|
286
|
+
# When the source is off-canvas, anchor at the target's
|
|
287
|
+
# first waypoint; vice versa for target off-canvas.
|
|
288
|
+
def ghost_anchor(end_kind, connector, link_row, diagram_row)
|
|
289
|
+
placement = if end_kind == :source
|
|
290
|
+
target_placement_for(connector, diagram_row)
|
|
291
|
+
else
|
|
292
|
+
source_placement_for(connector, diagram_row)
|
|
293
|
+
end
|
|
294
|
+
return nil unless placement
|
|
295
|
+
|
|
296
|
+
# Use the on-canvas end's edge center as the anchor.
|
|
297
|
+
b = bounds_from_rect(placement)
|
|
298
|
+
# Offset to the right of the element so the label sits
|
|
299
|
+
# outside the on-canvas element. EA's actual offset is
|
|
300
|
+
# roughly +12px in x and -8px in y from the element's
|
|
301
|
+
# right-center edge.
|
|
302
|
+
[b.x + b.width + 4, b.y + (b.height / 2) - 8]
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def source_placement_for(connector, diagram_row)
|
|
306
|
+
diagram_object_placement(diagram_row.diagram_id, connector.start_object_id)
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def target_placement_for(connector, diagram_row)
|
|
310
|
+
diagram_object_placement(diagram_row.diagram_id, connector.end_object_id)
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# EA stores connector routing in two places:
|
|
314
|
+
#
|
|
315
|
+
# - t_diagramlinks.Path: explicit intermediate waypoints as
|
|
316
|
+
# `x:y;x:y;...` in the element-rect coordinate frame.
|
|
317
|
+
# Present when the user has manually routed the connector
|
|
318
|
+
# or when EA's auto-router produced multi-bend paths.
|
|
319
|
+
# - t_diagramlinks.Geometry SX/SY/EX/EY: delta fields that
|
|
320
|
+
# describe the source-side and target-side bend offsets
|
|
321
|
+
# relative to the edge attachment points.
|
|
322
|
+
#
|
|
323
|
+
# Path takes precedence when present — it's the authoritative
|
|
324
|
+
# routing. SX/SY/EX/EY are fallback for auto-routed direct
|
|
325
|
+
# connectors with at most one bend on each end.
|
|
80
326
|
#
|
|
81
327
|
# If we can't resolve the source or target element placement
|
|
82
328
|
# for this diagram, we emit no waypoints (the connector will
|
|
@@ -96,21 +342,40 @@ module Ea
|
|
|
96
342
|
|
|
97
343
|
source_point = element_edge_point(source_placement, :source, edge_out)
|
|
98
344
|
target_point = element_edge_point(target_placement, :target, edge_out)
|
|
345
|
+
|
|
346
|
+
intermediate = intermediate_waypoints(link_row)
|
|
347
|
+
if intermediate.any?
|
|
348
|
+
points = [source_point, *intermediate, target_point]
|
|
349
|
+
else
|
|
350
|
+
points = sx_sy_ex_ey_waypoints(source_point, target_point, geom)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
points.map do |x, y|
|
|
354
|
+
Ea::Model::Waypoint.new(position: Ea::Model::Point.new(x: x, y: y))
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# Parse t_diagramlinks.Path into [[x, y], ...] intermediate
|
|
359
|
+
# waypoints. Format: `x1:y1;x2:y2;...`. Empty string returns [].
|
|
360
|
+
def intermediate_waypoints(link_row)
|
|
361
|
+
raw = link_row.path.to_s
|
|
362
|
+
return [] if raw.empty?
|
|
363
|
+
|
|
364
|
+
raw.scan(/(-?\d+):(-?\d+)/).map { |x, y| [x.to_i, y.to_i] }
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# Fallback when no explicit Path: compute source/target bend
|
|
368
|
+
# points from the SX/SY/EX/EY delta fields.
|
|
369
|
+
def sx_sy_ex_ey_waypoints(source_point, target_point, geom)
|
|
99
370
|
points = [source_point]
|
|
100
|
-
# SX/SY: delta from source point to first bend.
|
|
101
371
|
if geom[:sx] && geom[:sy] && (geom[:sx].nonzero? || geom[:sy].nonzero?)
|
|
102
372
|
points << [source_point[0] + geom[:sx], source_point[1] + geom[:sy]]
|
|
103
373
|
end
|
|
104
|
-
# EX/EY: delta from second bend to target point. Reverse
|
|
105
|
-
# to get from target back to the bend.
|
|
106
374
|
if geom[:ex] && geom[:ey] && (geom[:ex].nonzero? || geom[:ey].nonzero?)
|
|
107
375
|
points << [target_point[0] - geom[:ex], target_point[1] - geom[:ey]]
|
|
108
376
|
end
|
|
109
377
|
points << target_point
|
|
110
|
-
|
|
111
|
-
points.map do |x, y|
|
|
112
|
-
Ea::Model::Waypoint.new(position: Ea::Model::Point.new(x: x, y: y))
|
|
113
|
-
end
|
|
378
|
+
points
|
|
114
379
|
end
|
|
115
380
|
|
|
116
381
|
# Parse the SX, SY, EX, EY, EDGE key=value pairs out of the
|
|
@@ -126,7 +391,61 @@ module Ea
|
|
|
126
391
|
sy: pick_int(s, /SY=(-?\d+)/),
|
|
127
392
|
ex: pick_int(s, /EX=(-?\d+)/),
|
|
128
393
|
ey: pick_int(s, /EY=(-?\d+)/),
|
|
129
|
-
edge: pick_int(s, /EDGE=(-?\d+)/)
|
|
394
|
+
edge: pick_int(s, /EDGE=(-?\d+)/),
|
|
395
|
+
label_boxes: parse_label_boxes(s)
|
|
396
|
+
}
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# Parse EA's $LLB=, $LLT=, $LRB=, $LRT= label box geometry.
|
|
400
|
+
# Format: $LLB=CX=<int>:CY=<int>:OX=<int>:OY=<int>:HDN=0:BLD=0:
|
|
401
|
+
# ITA=0:UND=0:CLR=-1:ALN=0:DIR=0:ROT=0;
|
|
402
|
+
# Only the FIRST segment carries the leading `$`; subsequent
|
|
403
|
+
# segments use bare `KEY=` after the semicolon separator.
|
|
404
|
+
#
|
|
405
|
+
# Returns Hash keyed by :llb, :llt, :lrt, :lrb (and optional
|
|
406
|
+
# :lmt, :lmb, :irhs, :ilhs). Each value is a Hash with:
|
|
407
|
+
# - "ox", "oy" — label offset from anchor (always set)
|
|
408
|
+
# - "cx", "cy" — cell size for label box
|
|
409
|
+
# - "hidden" — bool, label suppressed when true
|
|
410
|
+
# - "bold", "italic", "underline" — bool font flags
|
|
411
|
+
# - "color" — signed int (VB OLE_COLOR; -1 = default)
|
|
412
|
+
# - "alignment" — 0=left, 1=center, 2=right
|
|
413
|
+
# - "direction" — text direction enum
|
|
414
|
+
# - "rotation" — degrees
|
|
415
|
+
def parse_label_boxes(geom_str)
|
|
416
|
+
boxes = {}
|
|
417
|
+
%i[llb llt lrt lrb lmt lmb irhs ilhs].each do |key|
|
|
418
|
+
match = geom_str.match(/(?:\$|;)\s*#{key.to_s.upcase}=([^;]*)/i)
|
|
419
|
+
next unless match && !match[1].empty?
|
|
420
|
+
|
|
421
|
+
parsed = parse_label_style(match[1])
|
|
422
|
+
next unless parsed
|
|
423
|
+
|
|
424
|
+
boxes[key] = parsed
|
|
425
|
+
end
|
|
426
|
+
boxes
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# Parse one label box's colon-separated key=value body into
|
|
430
|
+
# a Hash with string keys. Returns nil if OX/OY are absent.
|
|
431
|
+
def parse_label_style(body)
|
|
432
|
+
ox = body.match(/OX=(-?\d+)/)
|
|
433
|
+
oy = body.match(/OY=(-?\d+)/)
|
|
434
|
+
return nil unless ox && oy
|
|
435
|
+
|
|
436
|
+
{
|
|
437
|
+
"ox" => ox[1].to_i,
|
|
438
|
+
"oy" => oy[1].to_i,
|
|
439
|
+
"cx" => pick_int(body, /CX=(-?\d+)/),
|
|
440
|
+
"cy" => pick_int(body, /CY=(-?\d+)/),
|
|
441
|
+
"hidden" => pick_int(body, /HDN=(\d+)/) == 1,
|
|
442
|
+
"bold" => pick_int(body, /BLD=(\d+)/) == 1,
|
|
443
|
+
"italic" => pick_int(body, /ITA=(\d+)/) == 1,
|
|
444
|
+
"underline" => pick_int(body, /UND=(\d+)/) == 1,
|
|
445
|
+
"color" => pick_int(body, /CLR=(-?\d+)/),
|
|
446
|
+
"alignment" => pick_int(body, /ALN=(\d+)/),
|
|
447
|
+
"direction" => pick_int(body, /DIR=(\d+)/),
|
|
448
|
+
"rotation" => pick_int(body, /ROT=(-?\d+)/)
|
|
130
449
|
}
|
|
131
450
|
end
|
|
132
451
|
|
|
@@ -186,12 +505,21 @@ module Ea
|
|
|
186
505
|
)
|
|
187
506
|
end
|
|
188
507
|
|
|
508
|
+
# EA's t_diagramobjects stores rect with the origin at the
|
|
509
|
+
# bottom-left (math convention) — recttop > rectbottom for
|
|
510
|
+
# valid bounds, and y values are often negative. Convert to
|
|
511
|
+
# screen-space (origin top-left) by taking min/max and using
|
|
512
|
+
# absolute width/height.
|
|
189
513
|
def bounds_from_rect(obj_row)
|
|
514
|
+
left = obj_row.rectleft || 0
|
|
515
|
+
right = obj_row.rectright || 0
|
|
516
|
+
top = obj_row.recttop || 0
|
|
517
|
+
bottom = obj_row.rectbottom || 0
|
|
190
518
|
Ea::Model::Bounds.new(
|
|
191
|
-
x:
|
|
192
|
-
y:
|
|
193
|
-
width: (
|
|
194
|
-
height: (
|
|
519
|
+
x: [left, right].min,
|
|
520
|
+
y: [top, bottom].min,
|
|
521
|
+
width: (right - left).abs,
|
|
522
|
+
height: (bottom - top).abs
|
|
195
523
|
)
|
|
196
524
|
end
|
|
197
525
|
|
|
@@ -5,7 +5,12 @@ module Ea
|
|
|
5
5
|
module Qea
|
|
6
6
|
# Normalizes EA's identifier formats into stable strings used
|
|
7
7
|
# as Ea::Model element ids. EA represents identity as
|
|
8
|
-
# `{GUID}` strings.
|
|
8
|
+
# `{GUID}` strings.
|
|
9
|
+
#
|
|
10
|
+
# Two output formats:
|
|
11
|
+
# - `from_guid` — bare GUID without braces (canonical model id)
|
|
12
|
+
# - `to_eaid` — `EAID_<guid with dashes-to-underscores>` matching
|
|
13
|
+
# the reference SVG filename convention
|
|
9
14
|
module IdNormalizer
|
|
10
15
|
module_function
|
|
11
16
|
|
|
@@ -15,6 +20,15 @@ module Ea
|
|
|
15
20
|
ea_guid.to_s.gsub(/[{}]/, "")
|
|
16
21
|
end
|
|
17
22
|
|
|
23
|
+
# Convert a GUID to the EAID_ filename format used by EA's
|
|
24
|
+
# SVG export: strip braces, replace dashes with underscores,
|
|
25
|
+
# prefix with EAID_.
|
|
26
|
+
def to_eaid(ea_guid)
|
|
27
|
+
return nil if ea_guid.nil? || ea_guid.empty?
|
|
28
|
+
|
|
29
|
+
"EAID_" + ea_guid.to_s.gsub(/[{}]/, "").tr("-", "_")
|
|
30
|
+
end
|
|
31
|
+
|
|
18
32
|
def synthetic(prefix, *parts)
|
|
19
33
|
"#{prefix}:#{parts.join(":")}"
|
|
20
34
|
end
|
|
@@ -22,3 +36,4 @@ module Ea
|
|
|
22
36
|
end
|
|
23
37
|
end
|
|
24
38
|
end
|
|
39
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Sources
|
|
5
|
+
module Qea
|
|
6
|
+
# Translates EA t_object rows of Object_Type="Object" into
|
|
7
|
+
# Ea::Model::InstanceSpecification instances. Slots are parsed
|
|
8
|
+
# from the RunState field, which uses a packed format:
|
|
9
|
+
#
|
|
10
|
+
# @VAR;Variable=name;Value=val;Op===;@ENDVAR;@VAR;...
|
|
11
|
+
#
|
|
12
|
+
# Each @VAR...@ENDVAR block is one Slot.
|
|
13
|
+
class InstanceBuilder
|
|
14
|
+
RUN_STATE_PATTERN = /
|
|
15
|
+
@VAR;
|
|
16
|
+
(?:Variable=(?<name>[^;]*);)?
|
|
17
|
+
(?:Value=(?<value>[^;]*);)?
|
|
18
|
+
(?:Op=(?<op>[^;]*);)?
|
|
19
|
+
@ENDVAR;
|
|
20
|
+
/x.freeze
|
|
21
|
+
|
|
22
|
+
attr_reader :database
|
|
23
|
+
|
|
24
|
+
def initialize(database)
|
|
25
|
+
@database = database
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def build_all
|
|
29
|
+
objects = database.collections[:objects] || []
|
|
30
|
+
objects.filter_map { |row| build_one(row) if instance?(row) }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def build_one(row)
|
|
34
|
+
Ea::Model::InstanceSpecification.new(
|
|
35
|
+
id: IdNormalizer.from_guid(row.ea_guid),
|
|
36
|
+
name: row.name.to_s,
|
|
37
|
+
classifier_id: classifier_id_for(row),
|
|
38
|
+
classifier_name: classifier_name_for(row),
|
|
39
|
+
package_id: package_id_for(row),
|
|
40
|
+
package_name: package_name_for(row),
|
|
41
|
+
qualified_name: qualified_name_for(row),
|
|
42
|
+
slots: slots_for(row),
|
|
43
|
+
annotations: AnnotationBuilder.from_note(row.note, row.ea_guid,
|
|
44
|
+
kind: "documentation")
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def instance?(row)
|
|
51
|
+
row.object_type == "Object"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def classifier_id_for(row)
|
|
55
|
+
classifier = database.find_object(row.classifier.to_i) if row.classifier&.to_i&.positive?
|
|
56
|
+
return nil unless classifier
|
|
57
|
+
|
|
58
|
+
IdNormalizer.from_guid(classifier.ea_guid)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def classifier_name_for(row)
|
|
62
|
+
classifier = database.find_object(row.classifier.to_i) if row.classifier&.to_i&.positive?
|
|
63
|
+
return nil unless classifier
|
|
64
|
+
|
|
65
|
+
classifier.name.to_s
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def package_id_for(row)
|
|
69
|
+
return nil unless row.package_id&.to_i&.positive?
|
|
70
|
+
|
|
71
|
+
pkg = database.find_package(row.package_id.to_i)
|
|
72
|
+
return nil unless pkg
|
|
73
|
+
|
|
74
|
+
IdNormalizer.from_guid(pkg.ea_guid)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def package_name_for(row)
|
|
78
|
+
return nil unless row.package_id&.to_i&.positive?
|
|
79
|
+
|
|
80
|
+
pkg = database.find_package(row.package_id.to_i)
|
|
81
|
+
pkg&.name
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# EA's t_object.Name may carry a qualified name with a ":"
|
|
85
|
+
# prefix (e.g. "gml:CodeType"). When the instance is in a
|
|
86
|
+
# nested package the qualified name uses the package path.
|
|
87
|
+
def qualified_name_for(row)
|
|
88
|
+
name = row.name.to_s
|
|
89
|
+
return name if name.empty?
|
|
90
|
+
|
|
91
|
+
pkg_name = package_name_for(row)
|
|
92
|
+
pkg_name ? "#{pkg_name}::#{name}" : name
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Parse the RunState field into Slot instances. Each
|
|
96
|
+
# @VAR;Variable=X;Value=Y;Op=Z;@ENDVAR; is one slot.
|
|
97
|
+
# When Op is absent we default to "=".
|
|
98
|
+
def slots_for(row)
|
|
99
|
+
run_state = row.runstate.to_s
|
|
100
|
+
return [] if run_state.empty?
|
|
101
|
+
|
|
102
|
+
run_state.scan(RUN_STATE_PATTERN).map do |(name, value, op)|
|
|
103
|
+
Ea::Model::Slot.new(
|
|
104
|
+
name: name.to_s,
|
|
105
|
+
value: value.to_s,
|
|
106
|
+
op: (op.to_s.empty? ? "=" : op)
|
|
107
|
+
)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|