ea 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +128 -1
- data/Rakefile +3 -0
- data/TODO.complete/01-t-xref-parsing.md +91 -0
- data/TODO.complete/02-per-label-geometry.md +50 -0
- data/TODO.complete/03-object-properties-constraints.md +57 -0
- data/TODO.complete/04-operation-params.md +37 -0
- data/TODO.complete/05-pdata-flag-matrix.md +52 -0
- data/TODO.complete/06-emf-image-rendering.md +34 -0
- data/TODO.complete/07-auxiliary-tables.md +47 -0
- data/TODO.complete/10-mdg-stereotype-registry.md +40 -0
- data/TODO.complete/11-gml-xsd-generation.md +42 -0
- data/TODO.complete/13-shapescript-interpreter.md +28 -0
- data/TODO.complete/20-cli-diff.md +46 -0
- data/TODO.complete/21-cli-render.md +26 -0
- data/TODO.complete/22-cli-export.md +29 -0
- data/TODO.complete/23-cli-mdg.md +23 -0
- data/TODO.complete/30-headerlines-ocp-refactor.md +72 -0
- data/TODO.complete/31-public-send-cleanup.md +61 -0
- data/TODO.complete/32-spec-coverage.md +33 -0
- data/TODO.complete/33-blocked-parity-gaps.md +41 -0
- data/TODO.complete/36-stereotype-icon-wiring.md +32 -0
- data/TODO.complete/37-cli-lint.md +32 -0
- data/TODO.complete/38-cli-query.md +26 -0
- data/TODO.complete/39-cli-info.md +23 -0
- data/TODO.complete/40-ocl-evaluator.md +31 -0
- data/TODO.complete/41-round-trip-spec.md +18 -0
- data/TODO.complete/42-html-diff-report.md +18 -0
- data/TODO.complete/43-json-schema-export.md +20 -0
- data/TODO.complete/44-remaining-qea-tables.md +40 -0
- data/TODO.complete/45-shapescript-extensions.md +25 -0
- data/TODO.complete/46-xsd-consolidation.md +20 -0
- data/TODO.complete/47-headerlines-legacy-removal.md +20 -0
- data/TODO.complete/51-remove-respond-to.md +25 -0
- data/TODO.complete/52-xsd-decouple-fixtures.md +23 -0
- data/TODO.complete/53-xmi-public-send-cleanup.md +26 -0
- data/TODO.complete/54-wire-stereotype-icon-renderer.md +20 -0
- data/TODO.complete/55-wire-shapescript.md +20 -0
- data/TODO.complete/56-wire-emf-renderer.md +27 -0
- data/TODO.complete/57-wire-ocl-evaluator.md +23 -0
- data/TODO.complete/58-diff-modifications.md +21 -0
- data/TODO.complete/59-delete-toy-xmi-export.md +30 -0
- data/TODO.complete/60-curate-json-export.md +20 -0
- data/TODO.complete/61-plantuml-relationships.md +22 -0
- data/TODO.complete/62-changelog.md +17 -0
- data/TODO.complete/63-dependabot-fix.md +18 -0
- data/TODO.complete/STATUS.md +85 -0
- data/TODO.diagrams/01-svg-measurement-harness.md +31 -0
- data/TODO.diagrams/02-polygon-over-rendering.md +29 -0
- data/TODO.diagrams/03-text-over-rendering.md +30 -0
- data/TODO.diagrams/04-rect-under-rendering.md +33 -0
- data/TODO.diagrams/05-canvas-dimension-parity.md +51 -0
- data/TODO.diagrams/06-group-layering-parity.md +39 -0
- data/TODO.diagrams/07-text-content-fidelity.md +65 -0
- data/TODO.diagrams/08-qea-direct-pipeline.md +46 -0
- data/TODO.diagrams/12-connector-labels.md +40 -0
- data/TODO.diagrams/12-per-entity-svg-layer-ordering.md +24 -0
- data/TODO.diagrams/13-marker-polygon-vs-path-dispatch.md +20 -0
- data/TODO.diagrams/13-per-text-type-font-sizes.md +33 -0
- data/TODO.diagrams/14-center-header-text.md +33 -0
- data/TODO.diagrams/14-filter-diagram-frame-elements.md +18 -0
- data/TODO.diagrams/15-deep-dead-code-audit.md +47 -0
- data/TODO.diagrams/15-per-element-font-from-style.md +24 -0
- data/TODO.diagrams/16-canvas-marker-extent.md +30 -0
- data/TODO.diagrams/16-document-remaining-parity-gaps.md +91 -0
- data/TODO.diagrams/17-italic-abstract-classes.md +28 -0
- data/TODO.diagrams/17-remaining-parity-gaps.md +90 -0
- data/TODO.diagrams/18-canvas-from-image-bounds.md +20 -0
- data/TODO.diagrams/18-property-lookup-index.md +42 -0
- data/TODO.diagrams/19-connector-sx-sy-routing.md +27 -0
- data/TODO.diagrams/19-replace-respond-to-with-is-a.md +31 -0
- data/TODO.diagrams/20-split-labels-emitter.md +56 -0
- data/TODO.diagrams/20-visibility-glyph-font-encoding.md +27 -0
- data/TODO.diagrams/21-aggregation-navigability-arrow.md +21 -0
- data/TODO.diagrams/21-auto-generated-legend-block.md +65 -0
- data/TODO.diagrams/22-ghost-element-discriminator.md +57 -0
- data/TODO.diagrams/22-style-based-path-grouping.md +28 -0
- data/TODO.diagrams/23-marker-polygon-deduplication.md +47 -0
- data/TODO.diagrams/23-multiplicity-label-positioning.md +29 -0
- data/TODO.diagrams/24-marker-anchor-structural.md +35 -0
- data/TODO.diagrams/24-track-marker-anchors-structurally.md +66 -0
- data/TODO.diagrams/24-truncate-properties-to-fit-box.md +24 -0
- data/TODO.diagrams/26-phantom-general-parent-rendering.md +59 -0
- data/TODO.diagrams/27-path-grouping-mode-detection.md +43 -0
- data/TODO.diagrams/28-architecture-cleanup-batch.md +47 -0
- data/TODO.diagrams/30-decompose-elements-groups-for.md +52 -0
- data/TODO.diagrams/32-examples-coverage-batch.md +78 -0
- data/TODO.diagrams/36-instance-specification-model.md +42 -0
- data/TODO.diagrams/37-element-icon-decorations.md +41 -0
- data/TODO.diagrams/38-html-note-and-receptions.md +32 -0
- data/TODO.diagrams/39-property-stereotype-discriminator.md +40 -0
- data/TODO.diagrams/40-standalone-1-discriminator.md +38 -0
- data/TODO.diagrams/41-xmi-to-svg-pipeline.md +113 -0
- data/TODO.diagrams/42-close-all-parity-deltas.md +69 -0
- data/TODO.diagrams/43-marker-deduplication-strategy.md +54 -0
- data/TODO.diagrams/44-element-icon-decorations.md +34 -0
- data/TODO.diagrams/45-phantom-parent-class-boxes.md +40 -0
- data/TODO.diagrams/46-user-drawn-legend-swatches.md +44 -0
- data/TODO.diagrams/47-attribute-line-fidelity.md +72 -0
- data/TODO.diagrams/48-spec-coverage-critical-emitter-components.md +45 -0
- data/TODO.diagrams/49-spec-coverage-orchestrators.md +25 -0
- data/TODO.diagrams/50-examples-parity.md +85 -0
- data/TODO.diagrams/51-property-oy-gap-and-remaining-gaps.md +48 -0
- data/TODO.diagrams/52-marker-registration-architecture.md +89 -0
- data/TODO.diagrams/53-remaining-example-gaps.md +86 -0
- data/TODO.diagrams/54-display-name-nested-classes.md +40 -0
- data/TODO.diagrams/55-visibility-icon-discriminator.md +58 -0
- data/TODO.diagrams/56-package-contents-rendering.md +148 -0
- data/TODO.diagrams/57-instance-specification-model.md +100 -0
- data/TODO.diagrams/58-domain-model-note-text.md +67 -0
- data/TODO.diagrams/59-classes-arrow-discriminator.md +57 -0
- data/TODO.diagrams/60-testschema-association-marker.md +40 -0
- data/TODO.diagrams/61-legend-block-rendering.md +55 -0
- data/TODO.diagrams/61-plateau-parity-gaps.md +59 -0
- data/TODO.diagrams/62-ghost-element-rendering.md +56 -0
- data/TODO.diagrams/63-qualified-name-wrap.md +36 -0
- data/TODO.diagrams/64-property-stereotype-labels.md +22 -0
- data/TODO.diagrams/65-path-coordinate-frame.md +62 -0
- data/TODO.diagrams/67-path-coordinate-frame.md +62 -0
- data/TODO.diagrams/68-inherited-attributes.md +65 -0
- data/TODO.diagrams/69-package-anchor-conditional.md +29 -0
- data/TODO.diagrams/70-mdg-technology-support.md +68 -0
- data/TODO.diagrams/71-off-canvas-parent-ghost.md +45 -0
- data/TODO.diagrams/72-basic-attr-divider-paths.md +39 -0
- data/TODO.diagrams/73-plateau-polygon-gap.md +69 -0
- data/TODO.diagrams/74-plateau-rect-gap.md +22 -0
- data/TODO.diagrams/75-plateau-text-gap.md +39 -0
- data/TODO.diagrams/76-basic-poly-path-gaps.md +41 -0
- data/TODO.diagrams/77-basic-text-overrender.md +40 -0
- data/TODO.diagrams/78-headerlines-ocp-refactor.md +65 -0
- data/TODO.diagrams/79-architecture-backlog.md +65 -0
- data/TODO.diagrams/80-basic-triangle-polygons.md +51 -0
- data/TODO.diagrams/81-plateau-path-overrender.md +53 -0
- data/TODO.diagrams/82-appearance-canvas-offset.md +54 -0
- data/TODO.diagrams/83-3d-diagram-subtitle.md +58 -0
- data/TODO.diagrams/84-canvas-offset-mismatches.md +61 -0
- data/TODO.diagrams/85-phantom-property-overrender.md +95 -0
- data/TODO.diagrams/86-foreign-package-rule.md +62 -0
- data/TODO.diagrams/87-internal-tech-encryption.md +80 -0
- data/TODO.diagrams/EXAMPLES_STATUS.md +88 -0
- data/TODO.diagrams/README.md +69 -0
- data/TODO.diagrams/STATUS.md +73 -0
- data/TODO.fixup/01-default-grouped-paths.md +34 -0
- data/TODO.fixup/02-marker-consolidation-per-connector.md +20 -0
- data/TODO.fixup/03-diagram-font-inference.md +28 -0
- data/TODO.fixup/04-extract-bounds-calculator.md +25 -0
- data/TODO.fixup/05-marker-registry-ocp.md +32 -0
- data/TODO.fixup/06-connector-path-bend-routing.md +28 -0
- data/TODO.fixup/07-edge-attachment-formula.md +22 -0
- data/TODO.fixup/08-visual-diff-harness.md +28 -0
- data/TODO.fixup/09-extract-element-compartments.md +34 -0
- data/TODO.fixup/10-canvas-width-overshoot-fix.md +20 -0
- data/TODO.fixup/11-layer-sequencer-extraction.md +18 -0
- data/TODO.fixup/12-path-style-harmony.md +23 -0
- data/TODO.fixup/13-datatype-stereotype-fallback.md +28 -0
- data/TODO.fixup/14-ea-exe-reverse-engineering-notes.md +56 -0
- data/TODO.fixup/15-enumeration-literal-rendering.md +30 -0
- data/TODO.fixup/16-operation-compartment-rendering.md +42 -0
- data/TODO.fixup/17-path-count-calibration.md +27 -0
- data/TODO.fixup/18-polygon-count-calibration.md +32 -0
- data/TODO.fixup/19-rect-count-undershoot-investigation.md +40 -0
- data/TODO.fixup/20-stereotype-color-from-classifier.md +18 -0
- data/TODO.fixup/21-visibility-filter-for-properties.md +24 -0
- data/TODO.fixup/22-multiplicity-display-rule-calibration.md +25 -0
- data/TODO.fixup/23-diagram-frame-title-element.md +31 -0
- data/TODO.fixup/24-note-element-rendering.md +23 -0
- data/TODO.fixup/25-ea-exe-disassembly-findings.md +177 -0
- data/TODO.fixup/26-svg-renderer-architecture.md +78 -0
- data/TODO.fixup/27-svg-element-emitters-disassembly.md +135 -0
- data/TODO.fixup/28-geometry-parser-identification.md +57 -0
- data/TODO.fixup/29-qea-svg-xmi-triple-comparison.md +185 -0
- data/TODO.fixup/30-theme-system-stub.md +46 -0
- data/TODO.fixup/31-theme-value-object-registry.md +17 -0
- data/TODO.fixup/32-styleex-on-diagram-model.md +22 -0
- data/TODO.fixup/33-text-encoding-decimals-rotation.md +29 -0
- data/TODO.fixup/34-per-type-fill-via-theme.md +18 -0
- data/TODO.fixup/35-stroke-width-per-theme.md +7 -0
- data/TODO.fixup/36-qea-rect-coord-normalization.md +24 -0
- data/TODO.fixup/37-package-shape-rendering.md +22 -0
- data/TODO.fixup/38-note-element-rendering.md +15 -0
- data/TODO.fixup/39-styleex-flag-honoring.md +25 -0
- data/TODO.fixup/40-connector-dashed-lines.md +15 -0
- data/TODO.fixup/41-diagram-frame-showborder-detection.md +23 -0
- data/TODO.fixup/42-textrenderer-centralization.md +10 -0
- data/TODO.fixup/43-colorresolver-unification.md +24 -0
- data/TODO.fixup/44-theme-aware-textrenderer.md +17 -0
- data/TODO.fixup/45-theme-aware-fontresolver.md +22 -0
- data/TODO.fixup/46-style-parameterized-by-theme.md +21 -0
- data/TODO.fixup/47-canvas-frame-inset.md +19 -0
- data/TODO.fixup/48-background-rect-format.md +15 -0
- data/TODO.fixup/49-all-samples-integration-test.md +17 -0
- data/TODO.fixup/50-svg-emitter-architecture-docs.md +33 -0
- data/TODO.fixup/51-promote-theme-to-ea-namespace.md +28 -0
- data/TODO.fixup/52-theme-definition-integrated-fills.md +26 -0
- data/TODO.fixup/53-theme-registry-yaml-loading.md +50 -0
- data/TODO.fixup/54-diagram-theme-api.md +50 -0
- data/TODO.fixup/55-update-eaemitter-to-ea-theme.md +23 -0
- data/TODO.fixup/56-theme-yaml-files.md +65 -0
- data/TODO.fixup/57-theme-specs-and-integration.md +27 -0
- data/TODO.fixup/58-ea-entry-point-theme-autoload.md +20 -0
- data/TODO.fixup/59-thread-theme-through-all-renderers.md +29 -0
- data/TODO.fixup/60-cli-theme-option.md +22 -0
- data/TODO.fixup/61-theme-validation.md +28 -0
- data/TODO.fixup/62-theme-builder-dsl.md +31 -0
- data/TODO.fixup/63-theme-integration-rendering-spec.md +24 -0
- data/TODO.fixup/64-theme-writeback-styleex.md +21 -0
- data/TODO.fixup/65-remove-dead-eaemitter-theme-files.md +30 -0
- data/TODO.fixup/66-styleex-writeback.md +22 -0
- data/TODO.fixup/67-delete-dead-code.md +28 -0
- data/TODO.fixup/68-figure-parity-status.md +82 -0
- data/TODO.fixup/69-package-polygon-rendering.md +54 -0
- data/TODO.fixup/70-tagged-values-compartment.md +39 -0
- data/TODO.fixup/71-auto-detect-theme-from-xmi.md +37 -0
- data/TODO.fixup/72-sample-exports-as-regression-specs.md +37 -0
- data/TODO.fixup/73-delete-dead-transformations.md +14 -0
- data/TODO.fixup/74-delete-dead-diagram-renderers.md +18 -0
- data/TODO.fixup/75-visibility-legend-icons.md +23 -0
- data/TODO.fixup/76-plateau-text-undershoot.md +27 -0
- data/TODO.fixup/77-canvas-frame-margins.md +28 -0
- data/TODO.fixup/78-note-element-rendering.md +30 -0
- data/TODO.fixup/79-operation-compartment-rendering.md +27 -0
- data/TODO.fixup/80-tagged-value-stereotype-visibility.md +37 -0
- data/TODO.fixup/81-figure-parity-current-state.md +63 -0
- data/config/qea_schema.yml +356 -0
- data/config/themes/119.yml +98 -0
- data/config/themes/default.yml +14 -0
- data/examples/exports/20251010_current_plateau_v5/UML_EA.DTD +6856 -0
- data/examples/exports/20251010_current_plateau_v5/model.xml +96709 -0
- data/examples/exports/ArcGISWorkspace_template/UML_EA.DTD +6856 -0
- data/examples/exports/ArcGISWorkspace_template/model.xml +1427 -0
- data/examples/exports/UmlModel_template/UML_EA.DTD +6856 -0
- data/examples/exports/UmlModel_template/model.xml +23 -0
- data/examples/exports/basic/UML_EA.DTD +6856 -0
- data/examples/exports/basic/model.xml +8039 -0
- data/examples/exports/simple/UML_EA.DTD +6856 -0
- data/examples/exports/simple/model.xml +585 -0
- data/examples/exports/simple_example/UML_EA.DTD +6856 -0
- data/examples/exports/simple_example/model.xml +23 -0
- data/examples/exports/test/UML_EA.DTD +6856 -0
- data/examples/exports/test/model.xml +1638 -0
- data/examples/qea/basic.qea +0 -0
- data/frontend/README.md +51 -0
- data/frontend/dist/app.iife.js +24 -0
- data/frontend/dist/style.css +1 -0
- data/frontend/index.html +26 -0
- data/frontend/package-lock.json +2359 -0
- data/frontend/package.json +26 -0
- data/frontend/src/App.vue +59 -0
- data/frontend/src/app.ts +8 -0
- data/frontend/src/components/AppHeader.vue +127 -0
- data/frontend/src/components/AppSidebar.vue +119 -0
- data/frontend/src/components/ClassDetails.vue +247 -0
- data/frontend/src/components/DiagramView.vue +79 -0
- data/frontend/src/components/PackageDetails.vue +92 -0
- data/frontend/src/components/PackageTreeNode.vue +64 -0
- data/frontend/src/components/SearchResults.vue +48 -0
- data/frontend/src/components/WelcomeView.vue +87 -0
- data/frontend/src/shims-vue.d.ts +5 -0
- data/frontend/src/stores/dataStore.ts +79 -0
- data/frontend/src/stores/uiStore.ts +167 -0
- data/frontend/src/styles/main.css +1566 -0
- data/frontend/src/types.ts +221 -0
- data/frontend/tests/e2e/__screenshots__/new-dark-mode.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-package-details.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-search-modal.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-sidebar-collapsed.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-welcome.png +0 -0
- data/frontend/tests/e2e/render.spec.ts +188 -0
- data/frontend/tsconfig.json +20 -0
- data/frontend/vite.config.ts +26 -0
- data/frontend/vitest.config.ts +8 -0
- data/lib/ea/cli/app.rb +57 -2
- data/lib/ea/cli/command/diagrams.rb +37 -28
- data/lib/ea/cli/command/diff.rb +46 -0
- data/lib/ea/cli/command/export.rb +101 -0
- data/lib/ea/cli/command/info.rb +75 -0
- data/lib/ea/cli/command/lint.rb +48 -0
- data/lib/ea/cli/command/mdg.rb +98 -0
- data/lib/ea/cli/command/query.rb +34 -0
- data/lib/ea/cli/command/render.rb +135 -0
- data/lib/ea/cli/command/svg.rb +8 -15
- data/lib/ea/cli/command.rb +7 -0
- data/lib/ea/diagram/display_config.rb +184 -0
- data/lib/ea/diagram.rb +4 -41
- data/lib/ea/diff/change.rb +30 -0
- data/lib/ea/diff/comparator.rb +109 -0
- data/lib/ea/diff/html_reporter.rb +91 -0
- data/lib/ea/diff.rb +15 -0
- data/lib/ea/export/json/generator.rb +118 -0
- data/lib/ea/export/json.rb +12 -0
- data/lib/ea/export/json_schema/generator.rb +77 -0
- data/lib/ea/export/json_schema.rb +12 -0
- data/lib/ea/export/plantuml/generator.rb +140 -0
- data/lib/ea/export/plantuml.rb +12 -0
- data/lib/ea/export/xsd/class_mapping.rb +65 -0
- data/lib/ea/export/xsd/generator.rb +137 -0
- data/lib/ea/export/xsd/namespace_registry.rb +82 -0
- data/lib/ea/export/xsd.rb +19 -0
- data/lib/ea/export.rb +13 -0
- data/lib/ea/image/emf_renderer.rb +42 -0
- data/lib/ea/image.rb +12 -0
- data/lib/ea/lint/engine.rb +30 -0
- data/lib/ea/lint/lint_rule.rb +38 -0
- data/lib/ea/lint/offense.rb +18 -0
- data/lib/ea/lint/rules/cyclic_generalization.rb +62 -0
- data/lib/ea/lint/rules/duplicate_name.rb +28 -0
- data/lib/ea/lint/rules/missing_stereotype.rb +40 -0
- data/lib/ea/lint/rules/naming_convention.rb +44 -0
- data/lib/ea/lint/rules/orphan_element.rb +28 -0
- data/lib/ea/lint/rules.rb +14 -0
- data/lib/ea/lint.rb +13 -0
- data/lib/ea/mdg/document.rb +120 -0
- data/lib/ea/mdg/loader.rb +118 -0
- data/lib/ea/mdg/registry.rb +98 -0
- data/lib/ea/mdg/stereotype_alias_registry.rb +77 -0
- data/lib/ea/mdg/xml/applies_to.rb +18 -0
- data/lib/ea/mdg/xml/apply.rb +18 -0
- data/lib/ea/mdg/xml/content.rb +18 -0
- data/lib/ea/mdg/xml/documentation.rb +24 -0
- data/lib/ea/mdg/xml/stereotype.rb +28 -0
- data/lib/ea/mdg/xml/stereotypes.rb +18 -0
- data/lib/ea/mdg/xml/tag.rb +28 -0
- data/lib/ea/mdg/xml/tagged_values.rb +18 -0
- data/lib/ea/mdg/xml/technology.rb +28 -0
- data/lib/ea/mdg/xml/uml_profile.rb +22 -0
- data/lib/ea/mdg/xml/uml_profiles.rb +18 -0
- data/lib/ea/mdg/xml.rb +24 -0
- data/lib/ea/mdg.rb +25 -0
- data/lib/ea/model/classifier.rb +4 -0
- data/lib/ea/model/constraint.rb +24 -0
- data/lib/ea/model/diagram.rb +85 -0
- data/lib/ea/model/diagram_connector.rb +34 -0
- data/lib/ea/model/diagram_element.rb +16 -0
- data/lib/ea/model/document.rb +47 -2
- data/lib/ea/model/ghost_label.rb +32 -0
- data/lib/ea/model/instance_specification.rb +34 -0
- data/lib/ea/model/legend.rb +43 -0
- data/lib/ea/model/legend_item.rb +26 -0
- data/lib/ea/model/note.rb +28 -0
- data/lib/ea/model/operation.rb +2 -0
- data/lib/ea/model/property.rb +2 -0
- data/lib/ea/model/relationship.rb +2 -0
- data/lib/ea/model/signal.rb +14 -0
- data/lib/ea/model/slot.rb +19 -0
- data/lib/ea/model.rb +8 -0
- data/lib/ea/ocl/evaluator.rb +94 -0
- data/lib/ea/ocl/nodes.rb +30 -0
- data/lib/ea/ocl/parser.rb +133 -0
- data/lib/ea/ocl/unsupported_error.rb +7 -0
- data/lib/ea/ocl.rb +13 -0
- data/lib/ea/qea/factory/document_builder.rb +24 -10
- data/lib/ea/qea/factory/generalization_builder.rb +9 -11
- data/lib/ea/qea/infrastructure/table_reader.rb +9 -0
- data/lib/ea/qea/models/ea_author.rb +35 -0
- data/lib/ea/qea/models/ea_glossary.rb +34 -0
- data/lib/ea/qea/models/ea_image.rb +53 -0
- data/lib/ea/qea/models/ea_implement.rb +32 -0
- data/lib/ea/qea/models/ea_list.rb +42 -0
- data/lib/ea/qea/models/ea_object_effort.rb +34 -0
- data/lib/ea/qea/models/ea_object_problem.rb +37 -0
- data/lib/ea/qea/models/ea_object_require.rb +34 -0
- data/lib/ea/qea/models/ea_object_resource.rb +34 -0
- data/lib/ea/qea/models/ea_object_risk.rb +34 -0
- data/lib/ea/qea/models/ea_object_scenario.rb +34 -0
- data/lib/ea/qea/models/ea_object_test.rb +35 -0
- data/lib/ea/qea/models/ea_object_trx.rb +34 -0
- data/lib/ea/qea/models/ea_operation.rb +7 -0
- data/lib/ea/qea/models/ea_palette.rb +30 -0
- data/lib/ea/qea/models/ea_palette_item.rb +36 -0
- data/lib/ea/qea/models/ea_phase.rb +40 -0
- data/lib/ea/qea/models/ea_role_constraint.rb +32 -0
- data/lib/ea/qea/models/ea_secrypt.rb +34 -0
- data/lib/ea/qea/models/ea_version.rb +39 -0
- data/lib/ea/qea/models/ea_xref.rb +11 -1
- data/lib/ea/qea/models.rb +19 -0
- data/lib/ea/qea/services/database_loader.rb +22 -1
- data/lib/ea/qea/validation/database/ocl_constraint_validator.rb +65 -0
- data/lib/ea/qea/validation/database.rb +2 -0
- data/lib/ea/qea/validation/validation_engine.rb +2 -0
- data/lib/ea/qea/validation.rb +2 -0
- data/lib/ea/query/builder.rb +127 -0
- data/lib/ea/query.rb +9 -0
- data/lib/ea/render/image_converter.rb +101 -0
- data/lib/ea/render.rb +10 -0
- data/lib/ea/shapescript/parser.rb +208 -0
- data/lib/ea/shapescript/renderer.rb +74 -0
- data/lib/ea/shapescript/shape.rb +34 -0
- data/lib/ea/shapescript.rb +24 -0
- data/lib/ea/sources/qea/adapter.rb +22 -5
- data/lib/ea/sources/qea/classifier_builder.rb +184 -7
- data/lib/ea/sources/qea/diagram_builder.rb +352 -24
- data/lib/ea/sources/qea/id_normalizer.rb +16 -1
- data/lib/ea/sources/qea/instance_builder.rb +113 -0
- data/lib/ea/sources/qea/legend_builder.rb +137 -0
- data/lib/ea/sources/qea/note_builder.rb +58 -0
- data/lib/ea/sources/qea/object_classifier_map.rb +6 -1
- data/lib/ea/sources/qea/operation_builder.rb +1 -0
- data/lib/ea/sources/qea/package_builder.rb +16 -0
- data/lib/ea/sources/qea/relationship_builder.rb +21 -5
- data/lib/ea/sources/qea/tagged_value_builder.rb +28 -9
- data/lib/ea/sources/qea/xref/legend_definition.rb +35 -0
- data/lib/ea/sources/qea/xref/parser.rb +150 -0
- data/lib/ea/sources/qea/xref/property_definition.rb +29 -0
- data/lib/ea/sources/qea/xref/record.rb +41 -0
- data/lib/ea/sources/qea/xref/stereotype_application.rb +37 -0
- data/lib/ea/sources/qea/xref.rb +27 -0
- data/lib/ea/sources/qea.rb +4 -0
- data/lib/ea/sources/xmi/adapter.rb +80 -2
- data/lib/ea/sources/xmi/classifier_builder.rb +10 -1
- data/lib/ea/sources/xmi/diagram_builder.rb +95 -18
- data/lib/ea/sources/xmi/extension_geometry_parser.rb +19 -2
- data/lib/ea/sources/xmi/extension_style_parser.rb +15 -2
- data/lib/ea/sources/xmi/note_builder.rb +46 -0
- data/lib/ea/sources/xmi/property_builder.rb +9 -0
- data/lib/ea/sources/xmi/stereotype_builder.rb +54 -0
- data/lib/ea/sources/xmi/tag_builder.rb +45 -0
- data/lib/ea/sources/xmi/umldi_keyword_extractor.rb +87 -0
- data/lib/ea/sources/xmi.rb +4 -0
- data/lib/ea/spa/output/sharded_multi_file_strategy.rb +18 -3
- data/lib/ea/spa/output/single_file_strategy.rb +8 -2
- data/lib/ea/spa/output/strategy.rb +34 -0
- data/lib/ea/svg/connector_router.rb +134 -0
- data/lib/ea/svg/ea_emitter/background.rb +2 -3
- data/lib/ea/svg/ea_emitter/bounds_calculator.rb +142 -0
- data/lib/ea/svg/ea_emitter/canvas.rb +56 -33
- data/lib/ea/svg/ea_emitter/color_resolver.rb +56 -0
- data/lib/ea/svg/ea_emitter/compartment/attributes.rb +28 -0
- data/lib/ea/svg/ea_emitter/compartment/constraints.rb +30 -0
- data/lib/ea/svg/ea_emitter/compartment/enum_literals.rb +86 -0
- data/lib/ea/svg/ea_emitter/compartment/header.rb +32 -0
- data/lib/ea/svg/ea_emitter/compartment/header_divider.rb +57 -0
- data/lib/ea/svg/ea_emitter/compartment/instance_slots.rb +96 -0
- data/lib/ea/svg/ea_emitter/compartment/instance_underline.rb +67 -0
- data/lib/ea/svg/ea_emitter/compartment/note_body.rb +52 -0
- data/lib/ea/svg/ea_emitter/compartment/note_text.rb +102 -0
- data/lib/ea/svg/ea_emitter/compartment/operations.rb +38 -0
- data/lib/ea/svg/ea_emitter/compartment/package_contents.rb +72 -0
- data/lib/ea/svg/ea_emitter/compartment/package_from_parent.rb +136 -0
- data/lib/ea/svg/ea_emitter/compartment/shape.rb +82 -0
- data/lib/ea/svg/ea_emitter/compartment/stereotype_icon.rb +32 -0
- data/lib/ea/svg/ea_emitter/compartment/tagged_values.rb +30 -0
- data/lib/ea/svg/ea_emitter/compartment.rb +60 -0
- data/lib/ea/svg/ea_emitter/connectors.rb +46 -12
- data/lib/ea/svg/ea_emitter/diagram_frame.rb +143 -0
- data/lib/ea/svg/ea_emitter/document.rb +49 -25
- data/lib/ea/svg/ea_emitter/element/attribute_line_builder.rb +117 -0
- data/lib/ea/svg/ea_emitter/element/attribute_renderer.rb +119 -0
- data/lib/ea/svg/ea_emitter/element/bcol_decoder.rb +29 -0
- data/lib/ea/svg/ea_emitter/element/compartment_geometry.rb +113 -0
- data/lib/ea/svg/ea_emitter/element/constraint_renderer.rb +53 -0
- data/lib/ea/svg/ea_emitter/element/divider_renderer.rb +17 -0
- data/lib/ea/svg/ea_emitter/element/enumeration_literal_renderer.rb +34 -0
- data/lib/ea/svg/ea_emitter/element/filter.rb +44 -0
- data/lib/ea/svg/ea_emitter/element/hand_draw_shape_renderer.rb +77 -0
- data/lib/ea/svg/ea_emitter/element/header_line_pipeline.rb +43 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/instance_spec.rb +27 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/name.rb +76 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/parent_ghost.rb +25 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/stereotype_label.rb +68 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider.rb +19 -0
- data/lib/ea/svg/ea_emitter/element/header_lines.rb +27 -0
- data/lib/ea/svg/ea_emitter/element/header_renderer.rb +48 -0
- data/lib/ea/svg/ea_emitter/element/icon_renderer.rb +109 -0
- data/lib/ea/svg/ea_emitter/element/legend_renderer.rb +177 -0
- data/lib/ea/svg/ea_emitter/element/note_shape_renderer.rb +112 -0
- data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +100 -0
- data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +181 -0
- data/lib/ea/svg/ea_emitter/element/row_icon_renderer.rb +156 -0
- data/lib/ea/svg/ea_emitter/element/shape_renderer.rb +17 -0
- data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +119 -0
- data/lib/ea/svg/ea_emitter/element/tagged_value_renderer.rb +50 -0
- data/lib/ea/svg/ea_emitter/element/text_escape.rb +20 -0
- data/lib/ea/svg/ea_emitter/element.rb +37 -0
- data/lib/ea/svg/ea_emitter/elements.rb +283 -95
- data/lib/ea/svg/ea_emitter/font_resolver.rb +111 -0
- data/lib/ea/svg/ea_emitter/ghost_labels.rb +66 -0
- data/lib/ea/svg/ea_emitter/label/end_label.rb +266 -0
- data/lib/ea/svg/ea_emitter/label/midpoint_label.rb +101 -0
- data/lib/ea/svg/ea_emitter/label/registry.rb +50 -0
- data/lib/ea/svg/ea_emitter/label.rb +23 -0
- data/lib/ea/svg/ea_emitter/labels.rb +93 -27
- data/lib/ea/svg/ea_emitter/layer.rb +20 -0
- data/lib/ea/svg/ea_emitter/layer_sequencer.rb +93 -0
- data/lib/ea/svg/ea_emitter/marker/arrow_path.rb +52 -0
- data/lib/ea/svg/ea_emitter/marker/diamond.rb +57 -0
- data/lib/ea/svg/ea_emitter/marker/kind.rb +35 -0
- data/lib/ea/svg/ea_emitter/marker/open_triangle.rb +27 -0
- data/lib/ea/svg/ea_emitter/marker/package_import.rb +39 -0
- data/lib/ea/svg/ea_emitter/marker/plus.rb +40 -0
- data/lib/ea/svg/ea_emitter/marker/registry.rb +42 -0
- data/lib/ea/svg/ea_emitter/marker.rb +37 -0
- data/lib/ea/svg/ea_emitter/markers.rb +351 -64
- data/lib/ea/svg/ea_emitter/render_context.rb +52 -0
- data/lib/ea/svg/ea_emitter/style.rb +15 -0
- data/lib/ea/svg/ea_emitter/text_renderer.rb +92 -0
- data/lib/ea/svg/ea_emitter.rb +33 -11
- data/lib/ea/svg/parity/checker.rb +105 -0
- data/lib/ea/svg/parity/source.rb +59 -0
- data/lib/ea/svg/parity/suite.rb +113 -0
- data/lib/ea/svg/parity.rb +14 -0
- data/lib/ea/svg.rb +2 -15
- data/lib/ea/theme/definition.rb +176 -0
- data/lib/ea/theme/loader.rb +125 -0
- data/lib/ea/theme/registry.rb +96 -0
- data/lib/ea/theme.rb +37 -0
- data/lib/ea/transformations.rb +5 -88
- data/lib/ea/validation/xmi_parity.rb +80 -0
- data/lib/ea/validation.rb +12 -0
- data/lib/ea/version.rb +4 -1
- data/lib/ea.rb +15 -4
- data/lib/tasks/svg_bench.rake +87 -0
- data/lutaml-uml-0.5.1.gem +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes-list.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-2-gradients.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-3-colors.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance-fonts.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-5-behavior.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-6-sequence.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-2-objects.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-3-links.png +0 -0
- metadata +464 -26
- data/lib/ea/diagram/configuration.rb +0 -379
- data/lib/ea/diagram/element_renderers/base_renderer.rb +0 -77
- data/lib/ea/diagram/element_renderers/class_renderer.rb +0 -323
- data/lib/ea/diagram/element_renderers/connector_renderer.rb +0 -41
- data/lib/ea/diagram/element_renderers/package_renderer.rb +0 -61
- data/lib/ea/diagram/element_renderers.rb +0 -43
- data/lib/ea/diagram/extractor.rb +0 -566
- data/lib/ea/diagram/layout_engine.rb +0 -170
- data/lib/ea/diagram/path_builder.rb +0 -202
- data/lib/ea/diagram/style_parser.rb +0 -42
- data/lib/ea/diagram/style_resolver.rb +0 -291
- data/lib/ea/diagram/svg_renderer.rb +0 -274
- data/lib/ea/diagram/util.rb +0 -73
- data/lib/ea/svg/bounds_calculator.rb +0 -74
- data/lib/ea/svg/connector_path.rb +0 -138
- data/lib/ea/svg/element_box.rb +0 -256
- data/lib/ea/svg/renderer.rb +0 -77
- data/lib/ea/svg/style_resolver.rb +0 -72
- data/lib/ea/transformations/configuration.rb +0 -333
- data/lib/ea/transformations/format_registry.rb +0 -366
- data/lib/ea/transformations/parsers/base_parser.rb +0 -482
- data/lib/ea/transformations/parsers/qea_parser.rb +0 -401
- data/lib/ea/transformations/parsers/xmi_parser.rb +0 -243
- data/lib/ea/transformations/transformation_engine.rb +0 -390
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "lutaml/model"
|
|
4
|
-
require "yaml"
|
|
5
|
-
|
|
6
|
-
module Ea
|
|
7
|
-
module Transformations
|
|
8
|
-
# Configuration service for model transformations using external YAML
|
|
9
|
-
# configuration.
|
|
10
|
-
#
|
|
11
|
-
# This class follows the Dependency Inversion Principle by allowing external
|
|
12
|
-
# configuration instead of hardcoded behavior. It uses lutaml-model for
|
|
13
|
-
# structured YAML parsing and validation.
|
|
14
|
-
#
|
|
15
|
-
# @example Load default configuration
|
|
16
|
-
# config = Configuration.load
|
|
17
|
-
# puts config.enabled_formats
|
|
18
|
-
#
|
|
19
|
-
# @example Load custom configuration
|
|
20
|
-
# config = Configuration.load("my_config.yml")
|
|
21
|
-
# parser_config = config.parser_config_for("xmi")
|
|
22
|
-
class Configuration < Lutaml::Model::Serializable
|
|
23
|
-
# Parser configuration model
|
|
24
|
-
class ParserConfig < Lutaml::Model::Serializable
|
|
25
|
-
attribute :format, :string
|
|
26
|
-
attribute :extension, :string
|
|
27
|
-
attribute :parser_class, :string
|
|
28
|
-
attribute :enabled, :boolean, default: -> { true }
|
|
29
|
-
attribute :priority, :integer, default: -> { 100 }
|
|
30
|
-
attribute :description, :string
|
|
31
|
-
attribute :options, :string, collection: true
|
|
32
|
-
|
|
33
|
-
yaml do
|
|
34
|
-
map "format", to: :format
|
|
35
|
-
map "extension", to: :extension
|
|
36
|
-
map "parser_class", to: :parser_class
|
|
37
|
-
map "enabled", to: :enabled
|
|
38
|
-
map "priority", to: :priority
|
|
39
|
-
map "description", to: :description
|
|
40
|
-
map "options", to: :options
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Check if this parser handles the given extension
|
|
44
|
-
#
|
|
45
|
-
# @param ext [String] File extension (e.g., ".xmi")
|
|
46
|
-
# @return [Boolean] true if this parser handles the extension
|
|
47
|
-
def handles_extension?(ext)
|
|
48
|
-
extension == ext.downcase
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Transformation options model
|
|
53
|
-
class TransformationOptions < Lutaml::Model::Serializable
|
|
54
|
-
attribute :validate_output, :boolean, default: -> { false }
|
|
55
|
-
attribute :include_diagrams, :boolean, default: -> { true }
|
|
56
|
-
attribute :preserve_ids, :boolean, default: -> { true }
|
|
57
|
-
attribute :resolve_references, :boolean, default: -> { true }
|
|
58
|
-
attribute :strict_mode, :boolean, default: -> { false }
|
|
59
|
-
|
|
60
|
-
yaml do
|
|
61
|
-
map "validate_output", to: :validate_output
|
|
62
|
-
map "include_diagrams", to: :include_diagrams
|
|
63
|
-
map "preserve_ids", to: :preserve_ids
|
|
64
|
-
map "resolve_references", to: :resolve_references
|
|
65
|
-
map "strict_mode", to: :strict_mode
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# Format detection rules model
|
|
70
|
-
class FormatDetection < Lutaml::Model::Serializable
|
|
71
|
-
attribute :use_file_extension, :boolean, default: -> { true }
|
|
72
|
-
attribute :use_content_sniffing, :boolean, default: -> { true }
|
|
73
|
-
attribute :fallback_parser, :string
|
|
74
|
-
attribute :magic_bytes, :string, collection: true
|
|
75
|
-
|
|
76
|
-
yaml do
|
|
77
|
-
map "use_file_extension", to: :use_file_extension
|
|
78
|
-
map "use_content_sniffing", to: :use_content_sniffing
|
|
79
|
-
map "fallback_parser", to: :fallback_parser
|
|
80
|
-
map "magic_bytes", to: :magic_bytes
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Error handling configuration
|
|
85
|
-
class ErrorHandling < Lutaml::Model::Serializable
|
|
86
|
-
attribute :strategy, :string, default: -> { "continue" }
|
|
87
|
-
attribute :log_errors, :boolean, default: -> { true }
|
|
88
|
-
attribute :max_errors, :integer, default: -> { 10 }
|
|
89
|
-
attribute :fail_fast, :boolean, default: -> { false }
|
|
90
|
-
|
|
91
|
-
yaml do
|
|
92
|
-
map "strategy", to: :strategy
|
|
93
|
-
map "log_errors", to: :log_errors
|
|
94
|
-
map "max_errors", to: :max_errors
|
|
95
|
-
map "fail_fast", to: :fail_fast
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
attribute :version, :string
|
|
100
|
-
attribute :description, :string
|
|
101
|
-
attribute :parsers, ParserConfig, collection: true
|
|
102
|
-
attribute :transformation_options, TransformationOptions
|
|
103
|
-
attribute :format_detection, FormatDetection
|
|
104
|
-
attribute :error_handling, ErrorHandling
|
|
105
|
-
|
|
106
|
-
yaml do
|
|
107
|
-
map "version", to: :version
|
|
108
|
-
map "description", to: :description
|
|
109
|
-
map "parsers", to: :parsers
|
|
110
|
-
map "transformation_options", to: :transformation_options
|
|
111
|
-
map "format_detection", to: :format_detection
|
|
112
|
-
map "error_handling", to: :error_handling
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
class << self
|
|
116
|
-
# Load configuration from YAML file
|
|
117
|
-
#
|
|
118
|
-
# @param config_path [String, nil] Path to configuration file
|
|
119
|
-
# Defaults to config/model_transformations.yml
|
|
120
|
-
# @return [Configuration] The loaded configuration
|
|
121
|
-
# @raise [Errno::ENOENT] if config file not found
|
|
122
|
-
# @raise [Lutaml::Model::Error] if YAML is invalid
|
|
123
|
-
def load(config_path = nil)
|
|
124
|
-
config_path ||= default_config_path
|
|
125
|
-
|
|
126
|
-
unless File.exist?(config_path)
|
|
127
|
-
# Create default configuration if none exists
|
|
128
|
-
return create_default_configuration
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
yaml_content = File.read(config_path)
|
|
132
|
-
from_yaml(yaml_content)
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# Get default configuration file path
|
|
136
|
-
#
|
|
137
|
-
# @return [String] Path to default config file
|
|
138
|
-
def default_config_path
|
|
139
|
-
File.expand_path("../../../config/model_transformations.yml", __dir__)
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# Create default configuration when no config file exists
|
|
143
|
-
#
|
|
144
|
-
# @return [Configuration] Default configuration instance
|
|
145
|
-
def create_default_configuration
|
|
146
|
-
new.tap do |config|
|
|
147
|
-
config.version = "1.0"
|
|
148
|
-
config.description = "Default Model Transformations Configuration"
|
|
149
|
-
|
|
150
|
-
# Default parsers
|
|
151
|
-
config.parsers = [
|
|
152
|
-
create_xmi_parser_config,
|
|
153
|
-
create_qea_parser_config,
|
|
154
|
-
]
|
|
155
|
-
|
|
156
|
-
# Default options
|
|
157
|
-
config.transformation_options = TransformationOptions.new
|
|
158
|
-
config.format_detection = FormatDetection.new
|
|
159
|
-
config.error_handling = ErrorHandling.new
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
private
|
|
164
|
-
|
|
165
|
-
# Create default XMI parser configuration
|
|
166
|
-
#
|
|
167
|
-
# @return [ParserConfig] XMI parser configuration
|
|
168
|
-
def create_xmi_parser_config
|
|
169
|
-
ParserConfig.new.tap do |parser|
|
|
170
|
-
parser.format = "xmi"
|
|
171
|
-
parser.extension = ".xmi"
|
|
172
|
-
parser.parser_class = "Ea::Transformations::Parsers::XmiParser"
|
|
173
|
-
parser.enabled = true
|
|
174
|
-
parser.priority = 100
|
|
175
|
-
parser.description = "XML Metadata Interchange parser"
|
|
176
|
-
parser.options = ["validate_xml", "resolve_references"]
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
# Create default QEA parser configuration
|
|
181
|
-
#
|
|
182
|
-
# @return [ParserConfig] QEA parser configuration
|
|
183
|
-
def create_qea_parser_config
|
|
184
|
-
ParserConfig.new.tap do |parser|
|
|
185
|
-
parser.format = "qea"
|
|
186
|
-
parser.extension = ".qea"
|
|
187
|
-
parser.parser_class = "Ea::Transformations::Parsers::QeaParser"
|
|
188
|
-
parser.enabled = true
|
|
189
|
-
parser.priority = 90
|
|
190
|
-
parser.description = "Enterprise Architect database parser"
|
|
191
|
-
parser.options = ["include_diagrams", "resolve_references"]
|
|
192
|
-
end
|
|
193
|
-
end
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
# Get list of enabled parsers, sorted by priority
|
|
197
|
-
#
|
|
198
|
-
# @return [Array<ParserConfig>] Array of enabled parser configurations
|
|
199
|
-
def enabled_parsers
|
|
200
|
-
parsers&.select(&:enabled)&.sort_by { |p| -p.priority } || []
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
# Get parser configuration by format name
|
|
204
|
-
#
|
|
205
|
-
# @param format [String] The format name (e.g., "xmi", "qea")
|
|
206
|
-
# @return [ParserConfig, nil] The parser configuration or nil if not found
|
|
207
|
-
def parser_config_for(format)
|
|
208
|
-
parsers&.find { |p| p.format == format.downcase }
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
# Get parser configuration by file extension
|
|
212
|
-
#
|
|
213
|
-
# @param extension [String] The file extension (e.g., ".xmi", ".qea")
|
|
214
|
-
# @return [ParserConfig, nil] The parser configuration or nil if not found
|
|
215
|
-
def parser_config_for_extension(extension)
|
|
216
|
-
normalized_ext = extension.downcase
|
|
217
|
-
unless normalized_ext.start_with?(".")
|
|
218
|
-
normalized_ext = ".#{normalized_ext}"
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
enabled_parsers.find { |p| p.handles_extension?(normalized_ext) }
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
# Check if a format is enabled
|
|
225
|
-
#
|
|
226
|
-
# @param format [String] The format name
|
|
227
|
-
# @return [Boolean] true if format is enabled
|
|
228
|
-
def format_enabled?(format)
|
|
229
|
-
parser = parser_config_for(format)
|
|
230
|
-
parser&.enabled == true
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
# Get all enabled format names
|
|
234
|
-
#
|
|
235
|
-
# @return [Array<String>] Array of enabled format names
|
|
236
|
-
def enabled_formats
|
|
237
|
-
enabled_parsers.map(&:format)
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
# Get all supported file extensions
|
|
241
|
-
#
|
|
242
|
-
# @return [Array<String>] Array of supported extensions
|
|
243
|
-
def supported_extensions
|
|
244
|
-
enabled_parsers.filter_map(&:extension)
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
# Check if content sniffing is enabled
|
|
248
|
-
#
|
|
249
|
-
# @return [Boolean] true if content sniffing should be used
|
|
250
|
-
def content_sniffing_enabled?
|
|
251
|
-
format_detection&.use_content_sniffing == true
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
# Check if file extension detection is enabled
|
|
255
|
-
#
|
|
256
|
-
# @return [Boolean] true if file extension should be used for detection
|
|
257
|
-
def file_extension_detection_enabled?
|
|
258
|
-
format_detection&.use_file_extension == true
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
# Get fallback parser when format detection fails
|
|
262
|
-
#
|
|
263
|
-
# @return [String, nil] The fallback parser class name
|
|
264
|
-
def fallback_parser
|
|
265
|
-
format_detection&.fallback_parser
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
# Get transformation options with defaults
|
|
269
|
-
#
|
|
270
|
-
# @return [TransformationOptions] Transformation options
|
|
271
|
-
def transformation_options
|
|
272
|
-
@transformation_options ||= TransformationOptions.new
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
# Get error handling configuration with defaults
|
|
276
|
-
#
|
|
277
|
-
# @return [ErrorHandling] Error handling configuration
|
|
278
|
-
def error_handling
|
|
279
|
-
@error_handling ||= ErrorHandling.new
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
# Merge with another configuration (this takes precedence)
|
|
283
|
-
#
|
|
284
|
-
# @param other [Configuration] Configuration to merge with
|
|
285
|
-
# @return [Configuration] New merged configuration
|
|
286
|
-
def merge(other) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
287
|
-
merged = self.class.new
|
|
288
|
-
|
|
289
|
-
# Basic attributes
|
|
290
|
-
merged.version = version || other.version
|
|
291
|
-
merged.description = description || other.description
|
|
292
|
-
|
|
293
|
-
# Merge parsers (this config takes precedence)
|
|
294
|
-
merged.parsers = merge_parsers(other.parsers)
|
|
295
|
-
|
|
296
|
-
# Use this config's options, fallback to other
|
|
297
|
-
merged.transformation_options = transformation_options ||
|
|
298
|
-
other.transformation_options
|
|
299
|
-
merged.format_detection = format_detection || other.format_detection
|
|
300
|
-
merged.error_handling = error_handling || other.error_handling
|
|
301
|
-
|
|
302
|
-
merged
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
private
|
|
306
|
-
|
|
307
|
-
# Merge parser configurations
|
|
308
|
-
#
|
|
309
|
-
# @param other_parsers [Array<ParserConfig>] Other parsers to merge
|
|
310
|
-
# @return [Array<ParserConfig>] Merged parser list
|
|
311
|
-
def merge_parsers(other_parsers) # rubocop:disable Metrics/MethodLength
|
|
312
|
-
return parsers unless other_parsers
|
|
313
|
-
return other_parsers unless parsers
|
|
314
|
-
|
|
315
|
-
# Create a hash of this config's parsers by format
|
|
316
|
-
our_parsers = {}
|
|
317
|
-
parsers.each do |parser|
|
|
318
|
-
our_parsers[parser.format] = parser
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
# Add other parsers that we don't have
|
|
322
|
-
merged = parsers.dup
|
|
323
|
-
other_parsers.each do |other_parser|
|
|
324
|
-
unless our_parsers.key?(other_parser.format)
|
|
325
|
-
merged << other_parser
|
|
326
|
-
end
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
merged
|
|
330
|
-
end
|
|
331
|
-
end
|
|
332
|
-
end
|
|
333
|
-
end
|
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Ea
|
|
4
|
-
module Transformations
|
|
5
|
-
# Format Registry manages parser registration and discovery.
|
|
6
|
-
#
|
|
7
|
-
# This class implements the Registry pattern to provide a centralized
|
|
8
|
-
# location for managing format parsers. It follows the Open/Closed Principle
|
|
9
|
-
# by allowing new parsers to be registered without modifying existing code.
|
|
10
|
-
#
|
|
11
|
-
# @example Register a parser
|
|
12
|
-
# registry = FormatRegistry.new
|
|
13
|
-
# registry.register(".custom", MyCustomParser)
|
|
14
|
-
#
|
|
15
|
-
# @example Get parser for extension
|
|
16
|
-
# parser_class = registry.parser_for_extension(".xmi")
|
|
17
|
-
# parser = parser_class.new
|
|
18
|
-
class FormatRegistry
|
|
19
|
-
# @return [Hash<String, Class>] Map of extensions to parser classes
|
|
20
|
-
attr_reader :parsers
|
|
21
|
-
|
|
22
|
-
def initialize
|
|
23
|
-
@parsers = {}
|
|
24
|
-
@extensions = []
|
|
25
|
-
@default_parsers_loaded = false
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Register a parser for a file extension
|
|
29
|
-
#
|
|
30
|
-
# @param extension [String] File extension (e.g., ".xmi", ".qea")
|
|
31
|
-
# @param parser_class [Class] Parser class implementing BaseParser
|
|
32
|
-
# interface
|
|
33
|
-
# @raise [ArgumentError] if extension or parser_class is invalid
|
|
34
|
-
def register(extension, parser_class) # rubocop:disable Metrics/MethodLength
|
|
35
|
-
if extension.is_a?(Array)
|
|
36
|
-
extension.each { |ext| register(ext, parser_class) }
|
|
37
|
-
return
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
validate_extension!(extension)
|
|
41
|
-
validate_parser_class!(parser_class)
|
|
42
|
-
|
|
43
|
-
normalized_ext = normalize_extension(extension)
|
|
44
|
-
@parsers[normalized_ext] = parser_class
|
|
45
|
-
unless @extensions.include?(normalized_ext)
|
|
46
|
-
@extensions << normalized_ext
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Unregister a parser for a file extension
|
|
51
|
-
#
|
|
52
|
-
# @param extension [String] File extension to unregister
|
|
53
|
-
# @return [Class, nil] The unregistered parser class, or nil if not found
|
|
54
|
-
def unregister(extension)
|
|
55
|
-
normalized_ext = normalize_extension(extension)
|
|
56
|
-
@extensions.delete(normalized_ext)
|
|
57
|
-
@parsers.delete(normalized_ext)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Auto-register a parser class based on its supported extensions
|
|
61
|
-
#
|
|
62
|
-
# @param parser_class [Class] Parser class inherited from BaseParser
|
|
63
|
-
# @return [void]
|
|
64
|
-
def auto_register_from_parser(parser_class)
|
|
65
|
-
unless parser_class.is_a?(Class) &&
|
|
66
|
-
parser_class <= Ea::Transformations::Parsers::BaseParser
|
|
67
|
-
raise ArgumentError,
|
|
68
|
-
"#{parser_class} must inherit from " \
|
|
69
|
-
"Ea::Transformations::Parsers::BaseParser"
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
extensions = parser_class.new.supported_extensions
|
|
73
|
-
raise ArgumentError, "Extension cannot be nil or empty" if extensions.nil? || extensions.empty?
|
|
74
|
-
|
|
75
|
-
register(extensions, parser_class)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Get parser class for a file extension
|
|
79
|
-
#
|
|
80
|
-
# @param extension [String] File extension (e.g., ".xmi", ".qea")
|
|
81
|
-
# @return [Class, nil] Parser class, or nil if not found
|
|
82
|
-
def parser_for_extension(extension)
|
|
83
|
-
# ensure_default_parsers_loaded!
|
|
84
|
-
normalized_ext = normalize_extension(extension)
|
|
85
|
-
@parsers[normalized_ext]
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
# Get parser class for a file path
|
|
89
|
-
#
|
|
90
|
-
# @param file_path [String] Path to the file
|
|
91
|
-
# @return [Class, nil] Parser class, or nil if not found
|
|
92
|
-
def parser_for_file(file_path)
|
|
93
|
-
extension = File.extname(file_path)
|
|
94
|
-
parser_for_extension(extension)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Check if an extension is supported
|
|
98
|
-
#
|
|
99
|
-
# @param extension [String] File extension to check
|
|
100
|
-
# @return [Boolean] true if extension is supported
|
|
101
|
-
def supports_extension?(extension)
|
|
102
|
-
parser_for_extension(extension) != nil
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
# Check if a file is supported
|
|
106
|
-
#
|
|
107
|
-
# @param file_path [String] Path to the file
|
|
108
|
-
# @return [Boolean] true if file format is supported
|
|
109
|
-
def supports_file?(file_path)
|
|
110
|
-
parser_for_file(file_path) != nil
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
# Get all supported extensions
|
|
114
|
-
#
|
|
115
|
-
# @return [Array<String>] List of supported file extensions
|
|
116
|
-
def supported_extensions
|
|
117
|
-
# ensure_default_parsers_loaded!
|
|
118
|
-
@parsers.keys.sort
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# Get all registered parsers
|
|
122
|
-
#
|
|
123
|
-
# @return [Hash<String, Class>] Map of extensions to parser classes
|
|
124
|
-
def all_parsers
|
|
125
|
-
# ensure_default_parsers_loaded!
|
|
126
|
-
@parsers.dup
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
# Get parsers sorted by priority (highest first)
|
|
130
|
-
#
|
|
131
|
-
# @return [Array<Array(String, Class)>] List of [extension, parser_class]
|
|
132
|
-
def parsers_by_priority
|
|
133
|
-
@parsers.sort_by do |_ext, parser_class|
|
|
134
|
-
parser_class.new.priority
|
|
135
|
-
end.reverse
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Get all registered extensions
|
|
139
|
-
#
|
|
140
|
-
# @return [Array<String>] List of registered extensions
|
|
141
|
-
def all_extensions
|
|
142
|
-
@extensions.dup
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
# Get statistics about registered parsers
|
|
146
|
-
#
|
|
147
|
-
# @return [Hash] Statistics hash
|
|
148
|
-
def statistics # rubocop:disable Metrics/MethodLength
|
|
149
|
-
parsers = @parsers.values.uniq
|
|
150
|
-
total_parsers = parsers.size
|
|
151
|
-
ext_size = @extensions.size
|
|
152
|
-
|
|
153
|
-
{
|
|
154
|
-
total_parsers: total_parsers,
|
|
155
|
-
total_extensions: ext_size,
|
|
156
|
-
extensions_per_parser: (ext_size.to_f / total_parsers).round(2),
|
|
157
|
-
parser_details: parsers.map do |parser_class|
|
|
158
|
-
{
|
|
159
|
-
parser: parser_class,
|
|
160
|
-
extensions: parser_class.new.supported_extensions,
|
|
161
|
-
priority: parser_class.new.priority,
|
|
162
|
-
format_name: parser_class.new.format_name,
|
|
163
|
-
}
|
|
164
|
-
end,
|
|
165
|
-
}
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def export_configuration # rubocop:disable Metrics/MethodLength
|
|
169
|
-
{
|
|
170
|
-
exported_at: Time.now,
|
|
171
|
-
parsers: @parsers.map do |parser|
|
|
172
|
-
_ext, parser_class = parser
|
|
173
|
-
|
|
174
|
-
{
|
|
175
|
-
parser_class: parser_class,
|
|
176
|
-
extensions: parser_class.new.supported_extensions,
|
|
177
|
-
priority: parser_class.new.priority,
|
|
178
|
-
format: parser_class.new.format_name,
|
|
179
|
-
}
|
|
180
|
-
end,
|
|
181
|
-
}
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
# Clear all registered parsers
|
|
185
|
-
#
|
|
186
|
-
# @return [void]
|
|
187
|
-
def clear
|
|
188
|
-
@extensions.clear
|
|
189
|
-
@parsers.clear
|
|
190
|
-
@default_parsers_loaded = false
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
# Load parsers from configuration
|
|
194
|
-
#
|
|
195
|
-
# @param configuration [Configuration] Configuration with parser
|
|
196
|
-
# definitions
|
|
197
|
-
# @return [void]
|
|
198
|
-
def load_from_configuration(configuration)
|
|
199
|
-
configuration.enabled_parsers.each do |parser_config|
|
|
200
|
-
parser_class = constantize_parser_class(parser_config.parser_class)
|
|
201
|
-
register(parser_config.extension, parser_class)
|
|
202
|
-
rescue NameError => e
|
|
203
|
-
warn "Warning: Could not load parser " \
|
|
204
|
-
"#{parser_config.parser_class}: #{e.message}"
|
|
205
|
-
end
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
# Create a new instance with default parsers loaded
|
|
209
|
-
#
|
|
210
|
-
# @return [FormatRegistry] New registry instance
|
|
211
|
-
def self.with_defaults
|
|
212
|
-
registry = new
|
|
213
|
-
registry.load_default_parsers
|
|
214
|
-
registry
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
# Load default parsers (called automatically when needed)
|
|
218
|
-
#
|
|
219
|
-
# @return [void]
|
|
220
|
-
def load_default_parsers # rubocop:disable Metrics/MethodLength
|
|
221
|
-
return if @default_parsers_loaded
|
|
222
|
-
|
|
223
|
-
# Load XMI parser if available
|
|
224
|
-
begin
|
|
225
|
-
register(".xmi", Parsers::XmiParser)
|
|
226
|
-
rescue LoadError
|
|
227
|
-
# XMI parser not available, skip
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
# Load QEA parser if available
|
|
231
|
-
begin
|
|
232
|
-
register(".qea", Parsers::QeaParser)
|
|
233
|
-
rescue LoadError
|
|
234
|
-
# QEA parser not available, skip
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
@default_parsers_loaded = true
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
# Detect format by file content (magic bytes/signatures)
|
|
241
|
-
#
|
|
242
|
-
# @param file_path [String] Path to the file
|
|
243
|
-
# @return [Class, nil] Parser class based on content detection
|
|
244
|
-
def detect_by_content(file_path) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
245
|
-
# ensure_default_parsers_loaded!
|
|
246
|
-
unless File.exist?(file_path)
|
|
247
|
-
raise ArgumentError, "#{file_path} does not exist!"
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
# Read first few bytes to detect format
|
|
251
|
-
File.open(file_path, "rb") do |file|
|
|
252
|
-
header = file.read(1024) # Read first 1KB
|
|
253
|
-
|
|
254
|
-
return nil if header.nil? || header.empty?
|
|
255
|
-
|
|
256
|
-
# Check header match for content patterns
|
|
257
|
-
@parsers.each do |ext, parser_class|
|
|
258
|
-
parser_klass = parser_class.new
|
|
259
|
-
parser_klass.content_patterns.each do |pattern|
|
|
260
|
-
if header.match?(pattern)
|
|
261
|
-
return parser_for_extension(ext)
|
|
262
|
-
end
|
|
263
|
-
end
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
# Check for XML/XMI signatures
|
|
267
|
-
if header.include?("<?xml") && header.include?("xmi:")
|
|
268
|
-
ensure_default_parsers_loaded!
|
|
269
|
-
return parser_for_extension(".xmi")
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
# Check for SQLite database signature (QEA files)
|
|
273
|
-
if header[0..15] == "SQLite format 3\0"
|
|
274
|
-
ensure_default_parsers_loaded!
|
|
275
|
-
return parser_for_extension(".qea")
|
|
276
|
-
end
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
nil
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
# Get the best parser for a file using multiple detection methods
|
|
283
|
-
#
|
|
284
|
-
# @param file_path [String] Path to the file
|
|
285
|
-
# @param use_content_detection [Boolean] Whether to use content detection
|
|
286
|
-
# @return [Class, nil] Best parser class for the file
|
|
287
|
-
def best_parser_for_file(file_path, use_content_detection: true)
|
|
288
|
-
# First try extension-based detection
|
|
289
|
-
parser = parser_for_file(file_path)
|
|
290
|
-
return parser if parser
|
|
291
|
-
|
|
292
|
-
# Fall back to content detection if enabled
|
|
293
|
-
if use_content_detection
|
|
294
|
-
parser = detect_by_content(file_path)
|
|
295
|
-
return parser if parser
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
nil
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
private
|
|
302
|
-
|
|
303
|
-
# Ensure default parsers are loaded
|
|
304
|
-
#
|
|
305
|
-
# @return [void]
|
|
306
|
-
def ensure_default_parsers_loaded!
|
|
307
|
-
load_default_parsers unless @default_parsers_loaded
|
|
308
|
-
end
|
|
309
|
-
|
|
310
|
-
# Normalize file extension to lowercase with leading dot
|
|
311
|
-
#
|
|
312
|
-
# @param extension [String] File extension
|
|
313
|
-
# @return [String] Normalized extension
|
|
314
|
-
def normalize_extension(extension)
|
|
315
|
-
ext = extension.to_s.downcase
|
|
316
|
-
ext = ".#{ext}" unless ext.start_with?(".")
|
|
317
|
-
ext
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
# Validate file extension format
|
|
321
|
-
#
|
|
322
|
-
# @param extension [String] Extension to validate
|
|
323
|
-
# @raise [ArgumentError] if extension is invalid
|
|
324
|
-
def validate_extension!(extension)
|
|
325
|
-
if extension.nil? || extension.empty?
|
|
326
|
-
raise ArgumentError, "Extension cannot be nil or empty"
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
normalized = normalize_extension(extension)
|
|
330
|
-
unless normalized.match?(/^\.[a-z0-9]+(.[a-z0-9]+)?/)
|
|
331
|
-
raise ArgumentError, "Invalid extension format: #{extension}"
|
|
332
|
-
end
|
|
333
|
-
end
|
|
334
|
-
|
|
335
|
-
# Validate parser class implements required interface
|
|
336
|
-
#
|
|
337
|
-
# @param parser_class [Class] Parser class to validate
|
|
338
|
-
# @raise [ArgumentError] if parser class is invalid
|
|
339
|
-
def validate_parser_class!(parser_class) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
|
|
340
|
-
# Check if nil
|
|
341
|
-
if parser_class.nil?
|
|
342
|
-
raise ArgumentError, "Parser class cannot be nil"
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
# Check if it's a class
|
|
346
|
-
unless parser_class.is_a?(Class)
|
|
347
|
-
raise ArgumentError, "Parser must be a class"
|
|
348
|
-
end
|
|
349
|
-
|
|
350
|
-
unless parser_class < Parsers::BaseParser
|
|
351
|
-
raise ArgumentError,
|
|
352
|
-
"Parser class must inherit from BaseParser"
|
|
353
|
-
end
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
# Convert string class name to actual class constant
|
|
357
|
-
#
|
|
358
|
-
# @param class_name [String] Fully qualified class name
|
|
359
|
-
# @return [Class] The class constant
|
|
360
|
-
# @raise [NameError] if class cannot be found
|
|
361
|
-
def constantize_parser_class(class_name)
|
|
362
|
-
Transformations.constantize(class_name) ||
|
|
363
|
-
raise(NameError, "Cannot find class: #{class_name}")
|
|
364
|
-
end end
|
|
365
|
-
end
|
|
366
|
-
end
|