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
|
@@ -23,7 +23,8 @@ module Ea
|
|
|
23
23
|
italic: truthy?(kv["italic"]),
|
|
24
24
|
underline: truthy?(kv["ul"]),
|
|
25
25
|
black: truthy?(kv["black"]),
|
|
26
|
-
hide_icon:
|
|
26
|
+
hide_icon: tri_state_icon(kv["HideIcon"]),
|
|
27
|
+
show_tagged_values: truthy?(kv["Tag"]),
|
|
27
28
|
duid: kv["DUID"],
|
|
28
29
|
soid: kv["SOID"],
|
|
29
30
|
eoid: kv["EOID"],
|
|
@@ -34,6 +35,17 @@ module Ea
|
|
|
34
35
|
)
|
|
35
36
|
end
|
|
36
37
|
|
|
38
|
+
# HideIcon tri-state: "1" → :hidden, "0" → :shown,
|
|
39
|
+
# missing → nil (use the diagram default, which EA treats
|
|
40
|
+
# as hidden in practice).
|
|
41
|
+
def tri_state_icon(value)
|
|
42
|
+
return nil if value.nil? || value.empty?
|
|
43
|
+
return :hidden if value == "1" || value == "true" || value == "-1"
|
|
44
|
+
return :shown if value == "0"
|
|
45
|
+
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
|
|
37
49
|
def split_pairs(str)
|
|
38
50
|
str.split(";").filter_map do |pair|
|
|
39
51
|
next nil unless pair.include?("=")
|
|
@@ -59,7 +71,7 @@ module Ea
|
|
|
59
71
|
return 13 if value.nil? || value.empty?
|
|
60
72
|
|
|
61
73
|
pct = int(value)
|
|
62
|
-
return 13 if pct.nil?
|
|
74
|
+
return 13 if pct.nil? || pct.zero?
|
|
63
75
|
|
|
64
76
|
(pct * 13 / 100).round
|
|
65
77
|
end
|
|
@@ -68,6 +80,7 @@ module Ea
|
|
|
68
80
|
:background_color, :line_color, :line_width,
|
|
69
81
|
:font_family, :font_size,
|
|
70
82
|
:bold, :italic, :underline, :black, :hide_icon,
|
|
83
|
+
:show_tagged_values,
|
|
71
84
|
:duid, :soid, :eoid, :mode, :color, :hidden,
|
|
72
85
|
:raw,
|
|
73
86
|
keyword_init: true
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "nokogiri"
|
|
4
|
+
|
|
5
|
+
module Ea
|
|
6
|
+
module Sources
|
|
7
|
+
module Xmi
|
|
8
|
+
# Extracts Note elements from EA's XMI extension block.
|
|
9
|
+
# Notes are documentation entries (`<element xmi:type="uml:Note">`)
|
|
10
|
+
# inside `<xmi:Extension>/<elements>`. Each carries a
|
|
11
|
+
# `<properties documentation="..."/>` child with the body text.
|
|
12
|
+
#
|
|
13
|
+
# The xmi gem parses Note elements as part of the extension
|
|
14
|
+
# block; we walk them and project to Ea::Model::Note.
|
|
15
|
+
class NoteBuilder
|
|
16
|
+
attr_reader :root
|
|
17
|
+
|
|
18
|
+
def initialize(root)
|
|
19
|
+
@root = root
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def build_all
|
|
23
|
+
elements = root.extension&.elements
|
|
24
|
+
return [] unless elements
|
|
25
|
+
|
|
26
|
+
elements.element.to_a.filter_map { |e| build_one(e) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def build_one(ext_element)
|
|
32
|
+
type = ext_element.type
|
|
33
|
+
return nil unless type == "uml:Note"
|
|
34
|
+
|
|
35
|
+
props = ext_element.properties
|
|
36
|
+
Ea::Model::Note.new(
|
|
37
|
+
id: ext_element.idref,
|
|
38
|
+
name: ext_element.name,
|
|
39
|
+
body: props&.documentation,
|
|
40
|
+
note_type: "Note"
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -19,6 +19,7 @@ module Ea
|
|
|
19
19
|
id: id,
|
|
20
20
|
name: attr.name,
|
|
21
21
|
owner_id: owner.id,
|
|
22
|
+
type_ref: type_ref_for(attr),
|
|
22
23
|
type_name: type_name_for(attr),
|
|
23
24
|
qualified_name: "#{owner.name}::#{attr.name}",
|
|
24
25
|
multiplicity_lower: parse_lower(attr),
|
|
@@ -29,6 +30,7 @@ module Ea
|
|
|
29
30
|
is_ordered: boolean(attr.is_ordered),
|
|
30
31
|
is_unique: boolean(attr.is_unique),
|
|
31
32
|
aggregation: attr.aggregation || "none",
|
|
33
|
+
association_id: IdNormalizer.from_xmi_id(attr.association),
|
|
32
34
|
visibility: attr.visibility,
|
|
33
35
|
annotations: AnnotationBuilder.from_element(attr, id)
|
|
34
36
|
)
|
|
@@ -44,6 +46,13 @@ module Ea
|
|
|
44
46
|
attr.uml_type&.idref
|
|
45
47
|
end
|
|
46
48
|
|
|
49
|
+
def type_ref_for(attr)
|
|
50
|
+
# XMI <type xmi:idref=...> is the type classifier's GUID.
|
|
51
|
+
# The adapter's resolve_type_names walks this against the
|
|
52
|
+
# model to attach the in-model qualified name.
|
|
53
|
+
attr.uml_type&.idref
|
|
54
|
+
end
|
|
55
|
+
|
|
47
56
|
def parse_lower(attr)
|
|
48
57
|
value = attr.lower_value
|
|
49
58
|
return 1 unless value
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Sources
|
|
5
|
+
module Xmi
|
|
6
|
+
# Extracts per-element stereotype applications from EA's
|
|
7
|
+
# `<xmi:Extension>/<elements>` block. EA stores stereotype
|
|
8
|
+
# info in `<element xmi:idref="EAID_...">/<properties
|
|
9
|
+
# stereotype="X"/>`. The uml:Model packaged elements carry
|
|
10
|
+
# type info but not stereotypes.
|
|
11
|
+
#
|
|
12
|
+
# Returns a Hash{element_id => Array<String>} of stereotype
|
|
13
|
+
# names applied to that element.
|
|
14
|
+
class StereotypeBuilder
|
|
15
|
+
attr_reader :root
|
|
16
|
+
|
|
17
|
+
def initialize(root)
|
|
18
|
+
@root = root
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def grouped_by_element
|
|
22
|
+
@grouped ||= begin
|
|
23
|
+
elements = root.extension&.elements
|
|
24
|
+
return {} unless elements
|
|
25
|
+
|
|
26
|
+
elements.element.each_with_object({}) do |ext_element, acc|
|
|
27
|
+
id = ext_element.idref
|
|
28
|
+
next unless id
|
|
29
|
+
|
|
30
|
+
stereotypes = stereotypes_for(ext_element)
|
|
31
|
+
next if stereotypes.empty?
|
|
32
|
+
|
|
33
|
+
acc[id] = stereotypes
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# The `<properties stereotype="X"/>` attribute holds the
|
|
41
|
+
# primary stereotype. Additional stereotypes from `<xrefs>`
|
|
42
|
+
# are not captured here — that would require parsing the
|
|
43
|
+
# packed XREFPROP string format.
|
|
44
|
+
def stereotypes_for(ext_element)
|
|
45
|
+
props = ext_element.properties
|
|
46
|
+
value = props&.stereotype
|
|
47
|
+
return [] if value.nil? || value.to_s.empty?
|
|
48
|
+
|
|
49
|
+
[value.to_s]
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "nokogiri"
|
|
4
|
+
|
|
5
|
+
module Ea
|
|
6
|
+
module Sources
|
|
7
|
+
module Xmi
|
|
8
|
+
# Extracts tagged values from the `<xmi:Extension><tags><tag/>`
|
|
9
|
+
# block. The xmi gem does not expose Sparx tags directly, so we
|
|
10
|
+
# parse the raw XML once and group tags by their
|
|
11
|
+
# `modelElement` attribute. Each tag's value is truncated at
|
|
12
|
+
# the first `#` (EA encodes notes after that marker).
|
|
13
|
+
class TagBuilder
|
|
14
|
+
attr_reader :xmi_path
|
|
15
|
+
|
|
16
|
+
def initialize(xmi_path)
|
|
17
|
+
@xmi_path = xmi_path
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Returns Hash{model_element_id => Array<Ea::Model::TaggedValue>}
|
|
21
|
+
def grouped_by_model_element
|
|
22
|
+
return {} if xmi_path.nil?
|
|
23
|
+
|
|
24
|
+
@grouped ||= begin
|
|
25
|
+
doc = Nokogiri::XML(File.read(xmi_path))
|
|
26
|
+
# <tags><tag/></tags> lives inside per-element <element>
|
|
27
|
+
# blocks within <xmi:Extension>. The element itself is in
|
|
28
|
+
# no XML namespace; match by local-name to be safe.
|
|
29
|
+
doc.xpath(%(//*[local-name()="tags"]/*[local-name()="tag"]))
|
|
30
|
+
.each_with_object({}) do |node, acc|
|
|
31
|
+
me = node["modelElement"]
|
|
32
|
+
next unless me
|
|
33
|
+
|
|
34
|
+
key = node["name"].to_s
|
|
35
|
+
value = node["value"].to_s.split("#", 2).first
|
|
36
|
+
next if key.empty?
|
|
37
|
+
|
|
38
|
+
(acc[me] ||= []) << Ea::Model::TaggedValue.new(key: key, value: value)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "nokogiri"
|
|
4
|
+
|
|
5
|
+
module Ea
|
|
6
|
+
module Sources
|
|
7
|
+
module Xmi
|
|
8
|
+
# Reads UMLDI `<ownedElement>` blocks from the XMI source and
|
|
9
|
+
# returns the keyword text EA emitted for each modeled element.
|
|
10
|
+
#
|
|
11
|
+
# The xmi gem's typed parser does not surface umldi: keyword
|
|
12
|
+
# labels, but EA encodes them as
|
|
13
|
+
# `<ownedElement xmi:type="umldi:UMLKeywordLabel" text="Type"/>`.
|
|
14
|
+
# This class pre-parses the raw XML once and indexes by the
|
|
15
|
+
# referenced classifier ID.
|
|
16
|
+
#
|
|
17
|
+
# Returned text is the raw keyword ("Type", "FeatureType",
|
|
18
|
+
# "dataType"). The emitter wraps it in «...» when rendering.
|
|
19
|
+
class UmldiKeywordExtractor
|
|
20
|
+
# Sparx EA 6.5 declares umldi: as the 20161101 namespace on
|
|
21
|
+
# the XMI root. Older EAs use the 20131001 version. Try both.
|
|
22
|
+
UMLDI_NAMESPACES = [
|
|
23
|
+
"http://www.omg.org/spec/UML/20161101/UMLDI",
|
|
24
|
+
"http://www.omg.org/spec/UML/20131001/UMLDI"
|
|
25
|
+
].freeze
|
|
26
|
+
|
|
27
|
+
attr_reader :xmi_path
|
|
28
|
+
|
|
29
|
+
def initialize(xmi_path)
|
|
30
|
+
@xmi_path = xmi_path
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Returns { classifier_id => keyword_text } for every
|
|
34
|
+
# umldi:UMLKeywordLabel found, scoped to the given diagram.
|
|
35
|
+
def keywords_for_diagram(diagram_id)
|
|
36
|
+
return {} unless File.exist?(xmi_path)
|
|
37
|
+
|
|
38
|
+
keywords_by_diagram[diagram_id] || {}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def doc
|
|
44
|
+
@doc ||= Nokogiri::XML(File.read(xmi_path))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def keywords_by_diagram
|
|
48
|
+
@keywords_by_diagram ||= begin
|
|
49
|
+
result = {}
|
|
50
|
+
# The Sparx XMI export declares the umldi: namespace
|
|
51
|
+
# but the elements inside Diagram have lost their
|
|
52
|
+
# prefix binding once parsed by Nokogiri — the
|
|
53
|
+
# element name resolves to local-name "ownedElement"
|
|
54
|
+
# and the umldi:UMLClassifierShape type information
|
|
55
|
+
# lives in the literal `type` attribute. We therefore
|
|
56
|
+
# match shape children by the presence of `modelElement`
|
|
57
|
+
# and the keyword label by its `text` attribute.
|
|
58
|
+
doc.xpath("//*[local-name()=\"Diagram\"]").each do |diagram|
|
|
59
|
+
diagram_id = diagram["id"] || diagram["xmi:id"]
|
|
60
|
+
next unless diagram_id
|
|
61
|
+
|
|
62
|
+
keywords = {}
|
|
63
|
+
diagram.xpath("./ownedElement[@modelElement]").each do |shape|
|
|
64
|
+
model_id = shape["modelElement"]
|
|
65
|
+
next unless model_id
|
|
66
|
+
|
|
67
|
+
# Keyword label is the first ownedElement child
|
|
68
|
+
# with a non-empty `text` attribute. The name
|
|
69
|
+
# label follows it.
|
|
70
|
+
shape.xpath("./ownedElement[@text]").each do |child|
|
|
71
|
+
text = child["text"]
|
|
72
|
+
next if text.nil? || text.empty?
|
|
73
|
+
next if text.start_with?("«") # safety
|
|
74
|
+
|
|
75
|
+
keywords[model_id] = text
|
|
76
|
+
break
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
result[diagram_id] = keywords unless keywords.empty?
|
|
80
|
+
end
|
|
81
|
+
result
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
data/lib/ea/sources/xmi.rb
CHANGED
|
@@ -22,10 +22,14 @@ module Ea
|
|
|
22
22
|
autoload :OperationBuilder, "ea/sources/xmi/operation_builder"
|
|
23
23
|
autoload :RelationshipBuilder, "ea/sources/xmi/relationship_builder"
|
|
24
24
|
autoload :AnnotationBuilder, "ea/sources/xmi/annotation_builder"
|
|
25
|
+
autoload :TagBuilder, "ea/sources/xmi/tag_builder"
|
|
26
|
+
autoload :NoteBuilder, "ea/sources/xmi/note_builder"
|
|
27
|
+
autoload :StereotypeBuilder, "ea/sources/xmi/stereotype_builder"
|
|
25
28
|
autoload :DiagramBuilder, "ea/sources/xmi/diagram_builder"
|
|
26
29
|
autoload :ExtensionElements, "ea/sources/xmi/extension_elements"
|
|
27
30
|
autoload :ExtensionGeometryParser, "ea/sources/xmi/extension_geometry_parser"
|
|
28
31
|
autoload :ExtensionStyleParser, "ea/sources/xmi/extension_style_parser"
|
|
32
|
+
autoload :UmldiKeywordExtractor, "ea/sources/xmi/umldi_keyword_extractor"
|
|
29
33
|
autoload :Adapter, "ea/sources/xmi/adapter"
|
|
30
34
|
end
|
|
31
35
|
end
|
|
@@ -6,11 +6,14 @@ module Ea
|
|
|
6
6
|
module Spa
|
|
7
7
|
module Output
|
|
8
8
|
# Sharded layout: directory with skeleton.json, search.json,
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
9
|
+
# one JSON file per entity under data/, the pre-built SPA
|
|
10
|
+
# bundle (app.js + style.css), and an index.html shell that
|
|
11
|
+
# ties them together. The frontend loads skeleton + search
|
|
12
|
+
# upfront and fetches shards on demand. Suitable for large
|
|
13
|
+
# models that won't fit comfortably in a single HTML file.
|
|
12
14
|
class ShardedMultiFileStrategy < Strategy
|
|
13
15
|
def render(projector)
|
|
16
|
+
assert_frontend_bundle!
|
|
14
17
|
FileUtils.mkdir_p(output_path)
|
|
15
18
|
FileUtils.mkdir_p(File.join(output_path, "data"))
|
|
16
19
|
|
|
@@ -19,6 +22,7 @@ module Ea
|
|
|
19
22
|
write_json(File.join(output_path, "search.json"),
|
|
20
23
|
projector.search_index)
|
|
21
24
|
write_shards(projector)
|
|
25
|
+
write_assets
|
|
22
26
|
write_index_html(skeleton)
|
|
23
27
|
|
|
24
28
|
output_path
|
|
@@ -42,6 +46,15 @@ module Ea
|
|
|
42
46
|
end
|
|
43
47
|
end
|
|
44
48
|
|
|
49
|
+
# Copy the pre-built JS + CSS bundles next to index.html so
|
|
50
|
+
# the shell can <script src="app.js"> them. Assets are always
|
|
51
|
+
# the same bytes for every generation, so a plain file copy
|
|
52
|
+
# is both simplest and fastest.
|
|
53
|
+
def write_assets
|
|
54
|
+
FileUtils.cp(FRONTEND_APP_JS, File.join(output_path, "app.js"))
|
|
55
|
+
FileUtils.cp(FRONTEND_STYLE_CSS, File.join(output_path, "style.css"))
|
|
56
|
+
end
|
|
57
|
+
|
|
45
58
|
def write_index_html(skeleton)
|
|
46
59
|
title = skeleton.view_extras&.dig("ui", "title") ||
|
|
47
60
|
skeleton.metadata&.fetch("title", nil) ||
|
|
@@ -53,6 +66,7 @@ module Ea
|
|
|
53
66
|
<meta charset="UTF-8">
|
|
54
67
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
55
68
|
<title>#{title}</title>
|
|
69
|
+
<link rel="stylesheet" href="style.css">
|
|
56
70
|
</head>
|
|
57
71
|
<body>
|
|
58
72
|
<div id="app"></div>
|
|
@@ -61,6 +75,7 @@ module Ea
|
|
|
61
75
|
window.__SPA_SEARCH_URL__ = 'search.json';
|
|
62
76
|
window.__SPA_SHARD_BASE__ = 'data/';
|
|
63
77
|
</script>
|
|
78
|
+
<script src="app.js" defer></script>
|
|
64
79
|
</body>
|
|
65
80
|
</html>
|
|
66
81
|
HTML
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "fileutils"
|
|
4
|
+
require "json"
|
|
4
5
|
|
|
5
6
|
module Ea
|
|
6
7
|
module Spa
|
|
7
8
|
module Output
|
|
8
|
-
# Embeds skeleton + search + every shard
|
|
9
|
-
# (
|
|
9
|
+
# Embeds skeleton + search + every shard AND the pre-built SPA
|
|
10
|
+
# bundle (JS + CSS) in one HTML file. Suitable for small models
|
|
10
11
|
# only; not appropriate when shard count or total payload is
|
|
11
12
|
# large.
|
|
12
13
|
class SingleFileStrategy < Strategy
|
|
13
14
|
def render(projector)
|
|
15
|
+
assert_frontend_bundle!
|
|
14
16
|
FileUtils.mkdir_p(File.dirname(output_path))
|
|
15
17
|
|
|
16
18
|
skeleton = projector.skeleton
|
|
@@ -33,6 +35,8 @@ module Ea
|
|
|
33
35
|
title = payload.dig(:viewExtras, "ui", "title") ||
|
|
34
36
|
payload[:metadata]&.fetch("title", nil) ||
|
|
35
37
|
"Ea::Spa"
|
|
38
|
+
css = frontend_style_css || ""
|
|
39
|
+
js = frontend_app_js || ""
|
|
36
40
|
<<~HTML
|
|
37
41
|
<!DOCTYPE html>
|
|
38
42
|
<html lang="en">
|
|
@@ -40,12 +44,14 @@ module Ea
|
|
|
40
44
|
<meta charset="UTF-8">
|
|
41
45
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
42
46
|
<title>#{title}</title>
|
|
47
|
+
<style>#{css}</style>
|
|
43
48
|
</head>
|
|
44
49
|
<body>
|
|
45
50
|
<div id="app"></div>
|
|
46
51
|
<script>
|
|
47
52
|
window.__SPA_DATA__ = #{payload.to_json};
|
|
48
53
|
</script>
|
|
54
|
+
<script>#{js}</script>
|
|
49
55
|
</body>
|
|
50
56
|
</html>
|
|
51
57
|
HTML
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
3
5
|
module Ea
|
|
4
6
|
module Spa
|
|
5
7
|
module Output
|
|
@@ -13,6 +15,14 @@ module Ea
|
|
|
13
15
|
class Strategy
|
|
14
16
|
SIZE_THRESHOLD_BYTES = 2 * 1024 * 1024 # 2 MB
|
|
15
17
|
|
|
18
|
+
# Path to the pre-built Vue IIFE bundle shipped with the gem.
|
|
19
|
+
# Built from frontend/ via `npm run build`; committed so the
|
|
20
|
+
# gem never needs npm at install time. Resolved lazily so a
|
|
21
|
+
# missing bundle raises a clear error pointing at frontend/.
|
|
22
|
+
FRONTEND_DIST_DIR = File.expand_path("../../../../frontend/dist", __dir__)
|
|
23
|
+
FRONTEND_APP_JS = File.join(FRONTEND_DIST_DIR, "app.iife.js")
|
|
24
|
+
FRONTEND_STYLE_CSS = File.join(FRONTEND_DIST_DIR, "style.css")
|
|
25
|
+
|
|
16
26
|
attr_reader :output_path
|
|
17
27
|
|
|
18
28
|
def initialize(output_path)
|
|
@@ -24,6 +34,30 @@ module Ea
|
|
|
24
34
|
"#{self.class} must implement #render"
|
|
25
35
|
end
|
|
26
36
|
|
|
37
|
+
protected
|
|
38
|
+
|
|
39
|
+
# The pre-built SPA application source as a String, for
|
|
40
|
+
# embedding directly into a single-file output. Returns nil
|
|
41
|
+
# if the bundle has not been built (e.g. running from a
|
|
42
|
+
# source checkout before `npm run build`).
|
|
43
|
+
def frontend_app_js
|
|
44
|
+
return nil unless File.exist?(FRONTEND_APP_JS)
|
|
45
|
+
File.read(FRONTEND_APP_JS)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def frontend_style_css
|
|
49
|
+
return nil unless File.exist?(FRONTEND_STYLE_CSS)
|
|
50
|
+
File.read(FRONTEND_STYLE_CSS)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def assert_frontend_bundle!
|
|
54
|
+
unless File.exist?(FRONTEND_APP_JS) && File.exist?(FRONTEND_STYLE_CSS)
|
|
55
|
+
raise Ea::Error,
|
|
56
|
+
"SPA frontend bundle not found under #{FRONTEND_DIST_DIR}. " \
|
|
57
|
+
"Run `cd frontend && npm install && npm run build`."
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
27
61
|
private
|
|
28
62
|
|
|
29
63
|
def write_json(path, data)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
class ConnectorRouter
|
|
6
|
+
# Empirically determined offset EA adds to top-edge
|
|
7
|
+
# attachment points: connector starts ~9 px below the
|
|
8
|
+
# element's logical top edge (matches header height).
|
|
9
|
+
EDGE_TOP_OFFSET = 9
|
|
10
|
+
|
|
11
|
+
attr_reader :source_bounds, :target_bounds, :edge_code,
|
|
12
|
+
:source_delta, :target_delta, :bend_path
|
|
13
|
+
|
|
14
|
+
def initialize(source_bounds:, target_bounds:,
|
|
15
|
+
edge_code: nil,
|
|
16
|
+
source_delta: [0, 0], target_delta: [0, 0],
|
|
17
|
+
bend_path: nil)
|
|
18
|
+
@source_bounds = source_bounds
|
|
19
|
+
@target_bounds = target_bounds
|
|
20
|
+
@edge_code = edge_code
|
|
21
|
+
@source_delta = source_delta
|
|
22
|
+
@target_delta = target_delta
|
|
23
|
+
@bend_path = bend_path || []
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def waypoints
|
|
27
|
+
src_pt = source_point
|
|
28
|
+
tgt_pt = target_point
|
|
29
|
+
return [] unless src_pt && tgt_pt
|
|
30
|
+
|
|
31
|
+
# Path= values from EA are ABSOLUTE pixel positions on the
|
|
32
|
+
# diagram canvas (verified against reference SVGs). Use them
|
|
33
|
+
# as-is — do not offset by src_pt.
|
|
34
|
+
return [src_pt, *bend_path, tgt_pt] if bend_path.any?
|
|
35
|
+
|
|
36
|
+
points = [src_pt]
|
|
37
|
+
bend = bend_point(src_pt, tgt_pt)
|
|
38
|
+
points << bend if bend
|
|
39
|
+
points << tgt_pt
|
|
40
|
+
points
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def source_point
|
|
46
|
+
return nil unless source_bounds
|
|
47
|
+
pt = if @edge_code && @edge_code != 0
|
|
48
|
+
edge_point(source_bounds, @edge_code)
|
|
49
|
+
else
|
|
50
|
+
primary_edge(source_bounds, target_bounds)
|
|
51
|
+
end
|
|
52
|
+
return nil unless pt
|
|
53
|
+
|
|
54
|
+
offset_by_delta(pt, source_delta)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def target_point
|
|
58
|
+
return nil unless target_bounds
|
|
59
|
+
pt = if @edge_code && @edge_code != 0
|
|
60
|
+
opposite_edge_point(source_bounds, target_bounds, @edge_code)
|
|
61
|
+
else
|
|
62
|
+
primary_edge(target_bounds, source_bounds)
|
|
63
|
+
end
|
|
64
|
+
return nil unless pt
|
|
65
|
+
|
|
66
|
+
offset_by_delta(pt, target_delta)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def offset_by_delta(point, delta)
|
|
70
|
+
return point if delta.nil?
|
|
71
|
+
|
|
72
|
+
[point[0] + (delta[0] || 0), point[1] + (delta[1] || 0)]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def edge_point(bounds, code)
|
|
76
|
+
case code
|
|
77
|
+
when 1 then [center_x(bounds), bounds.y + EDGE_TOP_OFFSET]
|
|
78
|
+
when 2 then [bounds.x + bounds.width, center_y(bounds)]
|
|
79
|
+
when 3 then [center_x(bounds), bounds.y + bounds.height]
|
|
80
|
+
when 4 then [bounds.x, center_y(bounds)]
|
|
81
|
+
else [center_x(bounds), center_y(bounds)]
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def opposite_edge_point(source, target, code)
|
|
86
|
+
case code
|
|
87
|
+
when 1 then [center_x(target), target.y + target.height]
|
|
88
|
+
when 2 then [target.x, center_y(target)]
|
|
89
|
+
when 3 then [center_x(target), target.y]
|
|
90
|
+
when 4 then [target.x + target.width, center_y(target)]
|
|
91
|
+
else [center_x(target), center_y(target)]
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def primary_edge(bounds, other)
|
|
96
|
+
return nil unless bounds && other
|
|
97
|
+
|
|
98
|
+
dx = center_x(bounds) - center_x(other)
|
|
99
|
+
dy = center_y(bounds) - center_y(other)
|
|
100
|
+
|
|
101
|
+
if dx.abs > dy.abs
|
|
102
|
+
dx.positive? ? [bounds.x, center_y(bounds)] : [bounds.x + bounds.width, center_y(bounds)]
|
|
103
|
+
else
|
|
104
|
+
dy.positive? ? [center_x(bounds), bounds.y] : [center_x(bounds), bounds.y + bounds.height]
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def bend_point(src, tgt)
|
|
109
|
+
return nil if src == tgt
|
|
110
|
+
return nil if src[0] == tgt[0] || src[1] == tgt[1]
|
|
111
|
+
|
|
112
|
+
if horizontal_exit?
|
|
113
|
+
[tgt[0], src[1]]
|
|
114
|
+
else
|
|
115
|
+
[src[0], tgt[1]]
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def horizontal_exit?
|
|
120
|
+
return false unless source_bounds && @edge_code
|
|
121
|
+
|
|
122
|
+
@edge_code == 2 || @edge_code == 4
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def center_x(bounds)
|
|
126
|
+
bounds.x + (bounds.width / 2.0)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def center_y(bounds)
|
|
130
|
+
bounds.y + (bounds.height / 2.0)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_string: true
|
|
2
2
|
|
|
3
3
|
module Ea
|
|
4
4
|
module Svg
|
|
5
5
|
module EaEmitter
|
|
6
|
-
# Emits the background layer: white rect over the full canvas.
|
|
7
6
|
module Background
|
|
8
7
|
module_function
|
|
9
8
|
|
|
10
9
|
def render(canvas)
|
|
11
|
-
%(<g style="fill:#FFFFFF;fill-opacity:1.00;">\n <rect x="
|
|
10
|
+
%(<g style="fill:#FFFFFF;fill-opacity:1.00;">\n <rect x="0" y="0" width="#{canvas.width}" height="#{canvas.height}" shape-rendering="auto"/>\n</g>)
|
|
12
11
|
end
|
|
13
12
|
end
|
|
14
13
|
end
|