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.
- checksums.yaml +4 -4
- 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/STATUS.md +109 -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 +80 -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 +82 -0
- data/lib/ea/diff/html_reporter.rb +91 -0
- data/lib/ea/diff.rb +15 -0
- data/lib/ea/export/json/generator.rb +42 -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 +73 -0
- data/lib/ea/export/plantuml.rb +12 -0
- data/lib/ea/export/xmi/generator.rb +77 -0
- data/lib/ea/export/xmi.rb +11 -0
- data/lib/ea/export/xsd/class_mapping.rb +65 -0
- data/lib/ea/export/xsd/generator.rb +142 -0
- data/lib/ea/export/xsd/namespace_registry.rb +82 -0
- data/lib/ea/export/xsd.rb +19 -0
- data/lib/ea/export.rb +14 -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 +35 -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/query/builder.rb +108 -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/tagged_values.rb +30 -0
- data/lib/ea/svg/ea_emitter/compartment.rb +58 -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 +15 -24
- 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 +82 -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/version.rb +4 -1
- data/lib/ea.rb +14 -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 +449 -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
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Label
|
|
7
|
+
# Renders the role + «property» + multiplicity cluster at one
|
|
8
|
+
# endpoint of a UML association connector.
|
|
9
|
+
#
|
|
10
|
+
# EA encodes end-labels in two geometry boxes per endpoint:
|
|
11
|
+
#
|
|
12
|
+
# LLT/LRT - "label text" position (role name slot)
|
|
13
|
+
# LLB/LRB - "label box" position (multiplicity slot)
|
|
14
|
+
#
|
|
15
|
+
# Each box carries OX/OY offsets from the connector anchor.
|
|
16
|
+
# The «property» stereotype renders one line below the role
|
|
17
|
+
# name. EA records qualified role names ("pkg::role") for
|
|
18
|
+
# cross-package associations but suppresses label rendering
|
|
19
|
+
# for those — only simple role names render.
|
|
20
|
+
class EndLabel
|
|
21
|
+
PROPERTY_STEREOTYPE = "«property»"
|
|
22
|
+
|
|
23
|
+
attr_reader :canvas, :model_index, :document, :theme,
|
|
24
|
+
:font_family, :font_size, :font_unit
|
|
25
|
+
|
|
26
|
+
def initialize(canvas:, model_index:, document:, theme:,
|
|
27
|
+
font_family:, font_size:, font_unit:)
|
|
28
|
+
@canvas = canvas
|
|
29
|
+
@model_index = model_index
|
|
30
|
+
@document = document
|
|
31
|
+
@theme = theme
|
|
32
|
+
@font_family = font_family
|
|
33
|
+
@font_size = font_size
|
|
34
|
+
@font_unit = font_unit
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Returns an Array of `<text>` SVG strings for one endpoint.
|
|
38
|
+
#
|
|
39
|
+
# text_box - LLT or LRT box geometry (role position)
|
|
40
|
+
# mult_box - LLB or LRB box geometry (mult position)
|
|
41
|
+
# anchor - [x, y] of the connector endpoint
|
|
42
|
+
# connector - the DiagramConnector (for relationship lookup)
|
|
43
|
+
# end_kind - :source or :target
|
|
44
|
+
def texts(text_box:, mult_box:, anchor:, connector:, end_kind:)
|
|
45
|
+
from_property, role, mult = role_and_multiplicity(connector, end_kind)
|
|
46
|
+
if role_empty?(role) && mult_empty?(mult)
|
|
47
|
+
other_from_prop, other_role, other_mult = role_and_multiplicity(connector,
|
|
48
|
+
opposite(end_kind))
|
|
49
|
+
role = other_role if role_empty?(role)
|
|
50
|
+
mult ||= other_mult
|
|
51
|
+
from_property ||= other_from_prop
|
|
52
|
+
end
|
|
53
|
+
agg = aggregation_kind_for(connector, end_kind)
|
|
54
|
+
standalone = standalone_multiplicity(role, mult, aggregation: agg)
|
|
55
|
+
return [] if role_empty?(role) && (standalone.nil? || standalone.empty?)
|
|
56
|
+
|
|
57
|
+
text_pos = position_at(text_box, anchor)
|
|
58
|
+
mult_pos = position_at(mult_box, anchor) || text_pos
|
|
59
|
+
show_property = property_label?(connector)
|
|
60
|
+
|
|
61
|
+
build_texts(role:, mult:, standalone:,
|
|
62
|
+
text_pos:, mult_pos:, show_property:)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def build_texts(role:, mult:, standalone:, text_pos:, mult_pos:,
|
|
68
|
+
show_property:)
|
|
69
|
+
if role && !role.empty? && text_pos
|
|
70
|
+
x, y = text_pos
|
|
71
|
+
texts = [text_at(x, y, role)]
|
|
72
|
+
if show_property
|
|
73
|
+
texts << text_at(x, y + property_y_offset, PROPERTY_STEREOTYPE)
|
|
74
|
+
end
|
|
75
|
+
if mult && !mult.empty? && mult_pos
|
|
76
|
+
texts << text_at(*mult_pos, mult)
|
|
77
|
+
end
|
|
78
|
+
texts
|
|
79
|
+
elsif standalone && !standalone.empty? && mult_pos
|
|
80
|
+
[text_at(*mult_pos, standalone)]
|
|
81
|
+
else
|
|
82
|
+
[]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# EA renders «property» between role name and multiplicity
|
|
87
|
+
# ONLY for directed associations/aggregations — those with
|
|
88
|
+
# t_connector.Direction set to "Source -> Destination" or
|
|
89
|
+
# "Destination -> Source". "Unspecified" / "Bi-Directional"
|
|
90
|
+
# connectors do NOT get the stereotype label.
|
|
91
|
+
#
|
|
92
|
+
# Verified against both QEAs (output by the same EA in the
|
|
93
|
+
# same run):
|
|
94
|
+
# - basic.qea: 4 Associations with Direction="Unspecified"
|
|
95
|
+
# and role+mult → 0 «property» labels.
|
|
96
|
+
# - plateau QEA: aggregations with Direction="Destination
|
|
97
|
+
# -> Source" → «property» rendered (737 instances across
|
|
98
|
+
# 137 diagrams).
|
|
99
|
+
UNDIRECTED_DIRECTIONS = %w[Unspecified Bi-Directional].freeze
|
|
100
|
+
|
|
101
|
+
def property_label?(connector)
|
|
102
|
+
direction = connector.direction.to_s
|
|
103
|
+
return false if direction.empty?
|
|
104
|
+
return false if UNDIRECTED_DIRECTIONS.include?(direction)
|
|
105
|
+
|
|
106
|
+
true
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def role_empty?(role)
|
|
110
|
+
role.nil? || role.empty?
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def mult_empty?(mult)
|
|
114
|
+
mult.nil? || mult.empty?
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def opposite(end_kind)
|
|
118
|
+
end_kind == :source ? :target : :source
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def role_and_multiplicity(connector, end_kind)
|
|
122
|
+
assoc = association_for(connector)
|
|
123
|
+
return [nil, nil, nil] unless assoc
|
|
124
|
+
|
|
125
|
+
prop = property_at_end(assoc, end_kind)
|
|
126
|
+
return [true, *property_role(prop)] if prop
|
|
127
|
+
|
|
128
|
+
[false, *association_role(assoc, end_kind)]
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def property_at_end(association, end_kind)
|
|
132
|
+
id = end_kind == :source ? association.source_id : association.target_id
|
|
133
|
+
return nil unless id
|
|
134
|
+
|
|
135
|
+
return document.property_by_id(id) if document
|
|
136
|
+
|
|
137
|
+
fallback_property_lookup(id)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Linear-scan fallback for callers that did not wire in
|
|
141
|
+
# the Document. Production paths go through Document's
|
|
142
|
+
# property_index for O(1) lookup.
|
|
143
|
+
def fallback_property_lookup(id)
|
|
144
|
+
return nil unless model_index
|
|
145
|
+
|
|
146
|
+
model_index.each_value do |obj|
|
|
147
|
+
next unless obj.is_a?(Ea::Model::Classifier)
|
|
148
|
+
|
|
149
|
+
found = (obj.properties || []).find { |p| p.id == id }
|
|
150
|
+
return found if found
|
|
151
|
+
end
|
|
152
|
+
nil
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def property_role(property)
|
|
156
|
+
visibility = visibility_prefix(property)
|
|
157
|
+
name = property.name.to_s
|
|
158
|
+
role = name.empty? ? nil : "#{visibility}#{name}"
|
|
159
|
+
mult = multiplicity_text(property)
|
|
160
|
+
[role, mult]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def association_role(assoc, end_kind)
|
|
164
|
+
name = end_kind == :source ? assoc.source_role_name : assoc.target_role_name
|
|
165
|
+
lower = end_kind == :source ? assoc.source_multiplicity_lower : assoc.target_multiplicity_lower
|
|
166
|
+
upper = end_kind == :source ? assoc.source_multiplicity_upper : assoc.target_multiplicity_upper
|
|
167
|
+
return [nil, multiplicity_string(lower, upper)] if name.to_s.include?("::")
|
|
168
|
+
|
|
169
|
+
role = name.nil? || name.empty? ? nil : "+#{name}"
|
|
170
|
+
mult = multiplicity_string(lower, upper)
|
|
171
|
+
[role, mult]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def association_for(connector)
|
|
175
|
+
return nil unless model_index
|
|
176
|
+
|
|
177
|
+
rel = model_index[connector.relationship_ref]
|
|
178
|
+
return nil unless rel.is_a?(Ea::Model::Association)
|
|
179
|
+
|
|
180
|
+
rel
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Returns the aggregation kind for the given end (:shared,
|
|
184
|
+
# :composite, or nil). An aggregation marker on the OPPOSITE
|
|
185
|
+
# end means THIS end is the "part" end of an aggregation —
|
|
186
|
+
# EA shows "1" multiplicity for such ends.
|
|
187
|
+
def aggregation_kind_for(connector, end_kind)
|
|
188
|
+
assoc = association_for(connector)
|
|
189
|
+
return nil unless assoc
|
|
190
|
+
|
|
191
|
+
opposite_kind = opposite(end_kind)
|
|
192
|
+
agg = opposite_kind == :source ? assoc.source_aggregation : assoc.target_aggregation
|
|
193
|
+
return nil if agg.nil? || agg == "none"
|
|
194
|
+
|
|
195
|
+
agg.to_sym
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def position_at(box, anchor)
|
|
199
|
+
return nil unless box
|
|
200
|
+
|
|
201
|
+
offset_x = box["ox"] || box[:ox]
|
|
202
|
+
offset_y = box["oy"] || box[:oy]
|
|
203
|
+
return nil if offset_x.nil? && offset_y.nil?
|
|
204
|
+
|
|
205
|
+
[anchor[0] + offset_x.to_i, anchor[1] + offset_y.to_i]
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def property_y_offset
|
|
209
|
+
font_size + 6
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def multiplicity_text(property)
|
|
213
|
+
multiplicity_string(property.multiplicity_lower,
|
|
214
|
+
property.multiplicity_upper)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def multiplicity_string(lower, upper)
|
|
218
|
+
return nil if lower.nil? && upper.nil?
|
|
219
|
+
return "1" if lower == 1 && upper == 1
|
|
220
|
+
|
|
221
|
+
upper_str = upper == -1 ? "*" : upper.to_s
|
|
222
|
+
"#{lower || 0}..#{upper_str}"
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# EA suppresses standalone multiplicity when it is
|
|
226
|
+
# unspecified (nil). Explicit multiplicity renders
|
|
227
|
+
# regardless of aggregation kind. The QEA parser now
|
|
228
|
+
# returns nil for absent SourceCard/DestCard so default
|
|
229
|
+
# "1" is no longer produced — only explicit "1" reaches
|
|
230
|
+
# this method.
|
|
231
|
+
def standalone_multiplicity(role, mult, aggregation: nil)
|
|
232
|
+
return nil if role && !role.empty?
|
|
233
|
+
return nil if mult.nil?
|
|
234
|
+
|
|
235
|
+
mult
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def visibility_prefix(property)
|
|
239
|
+
case property.visibility
|
|
240
|
+
when "private" then "-"
|
|
241
|
+
when "protected" then "#"
|
|
242
|
+
when "package" then "~"
|
|
243
|
+
else "+"
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def text_at(x, y, content)
|
|
248
|
+
return nil if content.nil? || content.empty?
|
|
249
|
+
|
|
250
|
+
x_t = canvas ? canvas.translate_x(x) : x
|
|
251
|
+
y_t = canvas ? canvas.translate_y(y) : y
|
|
252
|
+
TextRenderer.new(content:, x: x_t, y: y_t,
|
|
253
|
+
family: font_family, size: font_size,
|
|
254
|
+
size_unit: font_unit,
|
|
255
|
+
fill: fill_color,
|
|
256
|
+
text_length: content.length * 6).to_svg
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def fill_color
|
|
260
|
+
"#000000"
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Label
|
|
7
|
+
# Renders the midpoint label for a connector. EA emits one
|
|
8
|
+
# of two things at the midpoint, but not both:
|
|
9
|
+
#
|
|
10
|
+
# 1. «stereotype» when the relationship has an applied
|
|
11
|
+
# stereotype (e.g. «import», «realize»).
|
|
12
|
+
# 2. The relationship's Name when no stereotype is applied
|
|
13
|
+
# and Name is non-empty (e.g. "Association A").
|
|
14
|
+
#
|
|
15
|
+
# Stereotype wins when both are present.
|
|
16
|
+
class MidpointLabel
|
|
17
|
+
attr_reader :canvas, :model_index, :font_family, :font_size,
|
|
18
|
+
:font_unit
|
|
19
|
+
|
|
20
|
+
def initialize(canvas:, model_index:, font_family:, font_size:,
|
|
21
|
+
font_unit:)
|
|
22
|
+
@canvas = canvas
|
|
23
|
+
@model_index = model_index
|
|
24
|
+
@font_family = font_family
|
|
25
|
+
@font_size = font_size
|
|
26
|
+
@font_unit = font_unit
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Returns the SVG `<text>` for the connector's midpoint
|
|
30
|
+
# label, or nil if there is no label to render.
|
|
31
|
+
def text_for(connector, points)
|
|
32
|
+
content = midpoint_content(connector)
|
|
33
|
+
return nil if content.nil?
|
|
34
|
+
|
|
35
|
+
mx, my = midpoint(points)
|
|
36
|
+
render_text(mx, my, content)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Returns the formatted «stereotype» string or nil. Used
|
|
40
|
+
# by Label::Registry to dispatch end-label vs midpoint.
|
|
41
|
+
def stereotype_label(connector)
|
|
42
|
+
rel = relationship_for(connector)
|
|
43
|
+
return nil unless rel
|
|
44
|
+
|
|
45
|
+
stereotype = rel.stereotype
|
|
46
|
+
return nil if stereotype.nil? || stereotype.to_s.empty?
|
|
47
|
+
|
|
48
|
+
"«#{stereotype}»"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Returns the midpoint label content: stereotype if
|
|
52
|
+
# present, else the relationship Name.
|
|
53
|
+
def midpoint_content(connector)
|
|
54
|
+
stereotype = stereotype_label(connector)
|
|
55
|
+
return stereotype if stereotype
|
|
56
|
+
|
|
57
|
+
rel = relationship_for(connector)
|
|
58
|
+
return nil unless rel
|
|
59
|
+
|
|
60
|
+
name = rel.name
|
|
61
|
+
return nil if name.nil? || name.to_s.empty?
|
|
62
|
+
|
|
63
|
+
name.to_s
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def relationship_for(connector)
|
|
67
|
+
return nil unless model_index
|
|
68
|
+
|
|
69
|
+
rel = model_index[connector.relationship_ref]
|
|
70
|
+
return nil unless rel.is_a?(Ea::Model::Relationship)
|
|
71
|
+
|
|
72
|
+
rel
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def midpoint(points)
|
|
78
|
+
mid_idx = points.size / 2
|
|
79
|
+
if points.size.odd?
|
|
80
|
+
points[mid_idx]
|
|
81
|
+
else
|
|
82
|
+
a = points[mid_idx - 1]
|
|
83
|
+
b = points[mid_idx]
|
|
84
|
+
[(a[0] + b[0]) / 2, (a[1] + b[1]) / 2]
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def render_text(x, y, content)
|
|
89
|
+
x_t = canvas ? canvas.translate_x(x) : x
|
|
90
|
+
y_t = canvas ? canvas.translate_y(y) : y
|
|
91
|
+
TextRenderer.new(content:, x: x_t, y: y_t,
|
|
92
|
+
family: font_family, size: font_size,
|
|
93
|
+
size_unit: font_unit,
|
|
94
|
+
fill: "#000000",
|
|
95
|
+
text_length: content.length * 6).to_svg
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Label
|
|
7
|
+
# Dispatches a connector to the appropriate label renderer(s).
|
|
8
|
+
#
|
|
9
|
+
# Rules:
|
|
10
|
+
# - Any relationship with an applied stereotype renders
|
|
11
|
+
# «stereotype» at the midpoint (Associations included —
|
|
12
|
+
# «import» on a Package connector is a common case).
|
|
13
|
+
# - Associations without a stereotype render EndLabel at
|
|
14
|
+
# each positioned LLT/LRT box.
|
|
15
|
+
# - Other relationship kinds without a stereotype render
|
|
16
|
+
# nothing.
|
|
17
|
+
#
|
|
18
|
+
# Open/closed: a new label kind registers here rather than
|
|
19
|
+
# forcing callers to edit case statements elsewhere.
|
|
20
|
+
class Registry
|
|
21
|
+
attr_reader :model_index
|
|
22
|
+
|
|
23
|
+
def initialize(model_index:)
|
|
24
|
+
@model_index = model_index
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Returns the formatted stereotype label when the
|
|
28
|
+
# connector should render at the midpoint, nil otherwise.
|
|
29
|
+
def midpoint?(connector)
|
|
30
|
+
MidpointLabel.new(canvas: nil, model_index: model_index,
|
|
31
|
+
font_family: nil, font_size: nil,
|
|
32
|
+
font_unit: nil).stereotype_label(connector)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Returns true if the connector's relationship is an
|
|
36
|
+
# Association AND has no midpoint stereotype (the end-label
|
|
37
|
+
# path).
|
|
38
|
+
def end_label?(connector)
|
|
39
|
+
association?(connector) && !midpoint?(connector)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def association?(connector)
|
|
43
|
+
rel = model_index ? model_index[connector.relationship_ref] : nil
|
|
44
|
+
rel.is_a?(Ea::Model::Association)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
# Per-connector label renderers. Each renderer handles one
|
|
7
|
+
# visual label kind on a connector:
|
|
8
|
+
#
|
|
9
|
+
# EndLabel - role + «property» + multiplicity at
|
|
10
|
+
# LLT/LRT box positions (UML associations)
|
|
11
|
+
# MidpointLabel - «stereotype» at the connector midpoint
|
|
12
|
+
# (Package / Dependency / Realization)
|
|
13
|
+
#
|
|
14
|
+
# Registry dispatches by relationship kind so new label kinds
|
|
15
|
+
# register rather than editing the orchestrator (OCP).
|
|
16
|
+
module Label
|
|
17
|
+
autoload :EndLabel, "ea/svg/ea_emitter/label/end_label"
|
|
18
|
+
autoload :MidpointLabel, "ea/svg/ea_emitter/label/midpoint_label"
|
|
19
|
+
autoload :Registry, "ea/svg/ea_emitter/label/registry"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -3,60 +3,126 @@
|
|
|
3
3
|
module Ea
|
|
4
4
|
module Svg
|
|
5
5
|
module EaEmitter
|
|
6
|
-
# Emits
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
6
|
+
# Emits the labels layer: connector role/multiplicity end
|
|
7
|
+
# labels plus midpoint stereotype labels for non-association
|
|
8
|
+
# connectors.
|
|
9
|
+
#
|
|
10
|
+
# This class is the orchestrator. The actual label-shaping
|
|
11
|
+
# logic lives in dedicated, MECE collaborators under
|
|
12
|
+
# `Ea::Svg::EaEmitter::Label`:
|
|
13
|
+
#
|
|
14
|
+
# Label::EndLabel - role + «property» + mult at LLT/LRT
|
|
15
|
+
# Label::MidpointLabel - «stereotype» at connector midpoint
|
|
16
|
+
# Label::Registry - dispatch by relationship kind (OCP)
|
|
17
|
+
#
|
|
18
|
+
# Theme is read once and threaded to collaborators so the
|
|
19
|
+
# rendering rules (font family/size, fill color) live in one
|
|
20
|
+
# place.
|
|
10
21
|
class Labels
|
|
11
|
-
DEFAULT_FAMILY = "
|
|
12
|
-
DEFAULT_SIZE =
|
|
22
|
+
DEFAULT_FAMILY = "Yu Gothic UI"
|
|
23
|
+
DEFAULT_SIZE = 13
|
|
13
24
|
|
|
14
|
-
attr_reader :diagram
|
|
25
|
+
attr_reader :diagram, :canvas, :model_index, :theme, :document
|
|
15
26
|
|
|
16
|
-
def initialize(diagram
|
|
27
|
+
def initialize(diagram, canvas: nil, model_index: nil, theme: nil,
|
|
28
|
+
document: nil)
|
|
17
29
|
@diagram = diagram
|
|
30
|
+
@canvas = canvas
|
|
31
|
+
@model_index = model_index
|
|
32
|
+
@theme = theme || Ea::Theme::Registry.default
|
|
33
|
+
@document = document
|
|
18
34
|
end
|
|
19
35
|
|
|
20
36
|
def render
|
|
21
|
-
texts = visible_connectors.
|
|
37
|
+
texts = visible_connectors.flat_map { |c| texts_for(c) }
|
|
22
38
|
return "" if texts.empty?
|
|
23
39
|
|
|
24
|
-
|
|
25
|
-
%(<g style="stroke-width:1;stroke-linecap:round;stroke-linejoin:bevel; fill:#000000;fill-opacity:1.00; stroke:#000000; stroke-opacity:0.00">\n#{blocks.join("\n")}\n</g>)
|
|
40
|
+
%(<g style="stroke-width:1;stroke-linecap:round;stroke-linejoin:bevel; fill:#000000;fill-opacity:1.00; stroke:#000000; stroke-opacity:0.00">\n#{texts.join("\n")}\n</g>)
|
|
26
41
|
end
|
|
27
42
|
|
|
28
43
|
private
|
|
29
44
|
|
|
30
45
|
def visible_connectors
|
|
31
|
-
(diagram.connectors || []).
|
|
46
|
+
(diagram.connectors || []).select(&:renderable?)
|
|
32
47
|
end
|
|
33
48
|
|
|
34
49
|
def texts_for(connector)
|
|
35
|
-
points = connector
|
|
36
|
-
return
|
|
50
|
+
points = waypoint_pairs(connector)
|
|
51
|
+
return [] if points.size < 2
|
|
37
52
|
|
|
53
|
+
texts = []
|
|
54
|
+
|
|
55
|
+
# Midpoint label: stereotype «import», or relationship
|
|
56
|
+
# Name ("Association A"). Renders at the path midpoint.
|
|
57
|
+
mid_text = midpoint_renderer.text_for(connector, points)
|
|
58
|
+
texts << mid_text if mid_text
|
|
59
|
+
|
|
60
|
+
# End-labels: role name + «property» + multiplicity at
|
|
61
|
+
# positioned LLT/LRT boxes. Only for associations without
|
|
62
|
+
# a midpoint stereotype (stereotyped associations route
|
|
63
|
+
# to midpoint only).
|
|
64
|
+
if registry.end_label?(connector)
|
|
65
|
+
end_label_texts(connector, points).each { |t| texts << t }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
texts
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def end_label_texts(connector, points)
|
|
38
72
|
source_pt = points.first
|
|
39
73
|
target_pt = points.last
|
|
40
|
-
|
|
41
74
|
boxes = connector.label_boxes || {}
|
|
75
|
+
|
|
42
76
|
texts = []
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
texts
|
|
77
|
+
end_renderer.texts(text_box: boxes[:llt], mult_box: boxes[:llb],
|
|
78
|
+
anchor: source_pt, connector: connector,
|
|
79
|
+
end_kind: :source).each { |t| texts << t }
|
|
80
|
+
end_renderer.texts(text_box: boxes[:lrt], mult_box: boxes[:lrb],
|
|
81
|
+
anchor: target_pt, connector: connector,
|
|
82
|
+
end_kind: :target).each { |t| texts << t }
|
|
83
|
+
texts
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def registry
|
|
87
|
+
@registry ||= Label::Registry.new(model_index: model_index)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def end_renderer
|
|
91
|
+
@end_renderer ||= Label::EndLabel.new(
|
|
92
|
+
canvas: canvas,
|
|
93
|
+
model_index: model_index,
|
|
94
|
+
document: document,
|
|
95
|
+
theme: theme,
|
|
96
|
+
font_family: label_font_family,
|
|
97
|
+
font_size: label_font_size,
|
|
98
|
+
font_unit: theme.font_size_unit
|
|
99
|
+
)
|
|
50
100
|
end
|
|
51
101
|
|
|
52
|
-
def
|
|
53
|
-
|
|
102
|
+
def midpoint_renderer
|
|
103
|
+
@midpoint_renderer ||= Label::MidpointLabel.new(
|
|
104
|
+
canvas: canvas,
|
|
105
|
+
model_index: model_index,
|
|
106
|
+
font_family: label_font_family,
|
|
107
|
+
font_size: label_font_size,
|
|
108
|
+
font_unit: theme.font_size_unit
|
|
109
|
+
)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def waypoint_pairs(connector)
|
|
113
|
+
(connector.waypoints || []).filter_map do |w|
|
|
114
|
+
next unless w.position
|
|
115
|
+
|
|
116
|
+
[w.position.x, w.position.y]
|
|
117
|
+
end
|
|
118
|
+
end
|
|
54
119
|
|
|
55
|
-
|
|
120
|
+
def label_font_family
|
|
121
|
+
theme.font_family || DEFAULT_FAMILY
|
|
56
122
|
end
|
|
57
123
|
|
|
58
|
-
def
|
|
59
|
-
|
|
124
|
+
def label_font_size
|
|
125
|
+
theme.font_size || DEFAULT_SIZE
|
|
60
126
|
end
|
|
61
127
|
end
|
|
62
128
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
# A single `<g>` block ready for inclusion in the SVG output.
|
|
7
|
+
# Carries the style string and the body (one or more child
|
|
8
|
+
# elements: `<path>`, `<polygon>`, `<text>`, `<rect>`).
|
|
9
|
+
#
|
|
10
|
+
# Emitters return Arrays of Layer objects. The Document
|
|
11
|
+
# orchestrator either joins them as-is (per-entity ordering)
|
|
12
|
+
# or buckets them by `style_key` for EA-style grouped output.
|
|
13
|
+
Layer = Struct.new(:style_key, :style, :body, keyword_init: true) do
|
|
14
|
+
def to_svg
|
|
15
|
+
%(<g style="#{style}">\n#{body}\n</g>)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|