ea 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +3 -0
- data/TODO.complete/01-t-xref-parsing.md +91 -0
- data/TODO.complete/02-per-label-geometry.md +50 -0
- data/TODO.complete/03-object-properties-constraints.md +57 -0
- data/TODO.complete/04-operation-params.md +37 -0
- data/TODO.complete/05-pdata-flag-matrix.md +52 -0
- data/TODO.complete/06-emf-image-rendering.md +34 -0
- data/TODO.complete/07-auxiliary-tables.md +47 -0
- data/TODO.complete/10-mdg-stereotype-registry.md +40 -0
- data/TODO.complete/11-gml-xsd-generation.md +42 -0
- data/TODO.complete/13-shapescript-interpreter.md +28 -0
- data/TODO.complete/20-cli-diff.md +46 -0
- data/TODO.complete/21-cli-render.md +26 -0
- data/TODO.complete/22-cli-export.md +29 -0
- data/TODO.complete/23-cli-mdg.md +23 -0
- data/TODO.complete/30-headerlines-ocp-refactor.md +72 -0
- data/TODO.complete/31-public-send-cleanup.md +61 -0
- data/TODO.complete/32-spec-coverage.md +33 -0
- data/TODO.complete/33-blocked-parity-gaps.md +41 -0
- data/TODO.complete/36-stereotype-icon-wiring.md +32 -0
- data/TODO.complete/37-cli-lint.md +32 -0
- data/TODO.complete/38-cli-query.md +26 -0
- data/TODO.complete/39-cli-info.md +23 -0
- data/TODO.complete/40-ocl-evaluator.md +31 -0
- data/TODO.complete/41-round-trip-spec.md +18 -0
- data/TODO.complete/42-html-diff-report.md +18 -0
- data/TODO.complete/43-json-schema-export.md +20 -0
- data/TODO.complete/44-remaining-qea-tables.md +40 -0
- data/TODO.complete/45-shapescript-extensions.md +25 -0
- data/TODO.complete/46-xsd-consolidation.md +20 -0
- data/TODO.complete/47-headerlines-legacy-removal.md +20 -0
- data/TODO.complete/STATUS.md +109 -0
- data/TODO.diagrams/01-svg-measurement-harness.md +31 -0
- data/TODO.diagrams/02-polygon-over-rendering.md +29 -0
- data/TODO.diagrams/03-text-over-rendering.md +30 -0
- data/TODO.diagrams/04-rect-under-rendering.md +33 -0
- data/TODO.diagrams/05-canvas-dimension-parity.md +51 -0
- data/TODO.diagrams/06-group-layering-parity.md +39 -0
- data/TODO.diagrams/07-text-content-fidelity.md +65 -0
- data/TODO.diagrams/08-qea-direct-pipeline.md +46 -0
- data/TODO.diagrams/12-connector-labels.md +40 -0
- data/TODO.diagrams/12-per-entity-svg-layer-ordering.md +24 -0
- data/TODO.diagrams/13-marker-polygon-vs-path-dispatch.md +20 -0
- data/TODO.diagrams/13-per-text-type-font-sizes.md +33 -0
- data/TODO.diagrams/14-center-header-text.md +33 -0
- data/TODO.diagrams/14-filter-diagram-frame-elements.md +18 -0
- data/TODO.diagrams/15-deep-dead-code-audit.md +47 -0
- data/TODO.diagrams/15-per-element-font-from-style.md +24 -0
- data/TODO.diagrams/16-canvas-marker-extent.md +30 -0
- data/TODO.diagrams/16-document-remaining-parity-gaps.md +91 -0
- data/TODO.diagrams/17-italic-abstract-classes.md +28 -0
- data/TODO.diagrams/17-remaining-parity-gaps.md +90 -0
- data/TODO.diagrams/18-canvas-from-image-bounds.md +20 -0
- data/TODO.diagrams/18-property-lookup-index.md +42 -0
- data/TODO.diagrams/19-connector-sx-sy-routing.md +27 -0
- data/TODO.diagrams/19-replace-respond-to-with-is-a.md +31 -0
- data/TODO.diagrams/20-split-labels-emitter.md +56 -0
- data/TODO.diagrams/20-visibility-glyph-font-encoding.md +27 -0
- data/TODO.diagrams/21-aggregation-navigability-arrow.md +21 -0
- data/TODO.diagrams/21-auto-generated-legend-block.md +65 -0
- data/TODO.diagrams/22-ghost-element-discriminator.md +57 -0
- data/TODO.diagrams/22-style-based-path-grouping.md +28 -0
- data/TODO.diagrams/23-marker-polygon-deduplication.md +47 -0
- data/TODO.diagrams/23-multiplicity-label-positioning.md +29 -0
- data/TODO.diagrams/24-marker-anchor-structural.md +35 -0
- data/TODO.diagrams/24-track-marker-anchors-structurally.md +66 -0
- data/TODO.diagrams/24-truncate-properties-to-fit-box.md +24 -0
- data/TODO.diagrams/26-phantom-general-parent-rendering.md +59 -0
- data/TODO.diagrams/27-path-grouping-mode-detection.md +43 -0
- data/TODO.diagrams/28-architecture-cleanup-batch.md +47 -0
- data/TODO.diagrams/30-decompose-elements-groups-for.md +52 -0
- data/TODO.diagrams/32-examples-coverage-batch.md +78 -0
- data/TODO.diagrams/36-instance-specification-model.md +42 -0
- data/TODO.diagrams/37-element-icon-decorations.md +41 -0
- data/TODO.diagrams/38-html-note-and-receptions.md +32 -0
- data/TODO.diagrams/39-property-stereotype-discriminator.md +40 -0
- data/TODO.diagrams/40-standalone-1-discriminator.md +38 -0
- data/TODO.diagrams/41-xmi-to-svg-pipeline.md +113 -0
- data/TODO.diagrams/42-close-all-parity-deltas.md +69 -0
- data/TODO.diagrams/43-marker-deduplication-strategy.md +54 -0
- data/TODO.diagrams/44-element-icon-decorations.md +34 -0
- data/TODO.diagrams/45-phantom-parent-class-boxes.md +40 -0
- data/TODO.diagrams/46-user-drawn-legend-swatches.md +44 -0
- data/TODO.diagrams/47-attribute-line-fidelity.md +72 -0
- data/TODO.diagrams/48-spec-coverage-critical-emitter-components.md +45 -0
- data/TODO.diagrams/49-spec-coverage-orchestrators.md +25 -0
- data/TODO.diagrams/50-examples-parity.md +85 -0
- data/TODO.diagrams/51-property-oy-gap-and-remaining-gaps.md +48 -0
- data/TODO.diagrams/52-marker-registration-architecture.md +89 -0
- data/TODO.diagrams/53-remaining-example-gaps.md +86 -0
- data/TODO.diagrams/54-display-name-nested-classes.md +40 -0
- data/TODO.diagrams/55-visibility-icon-discriminator.md +58 -0
- data/TODO.diagrams/56-package-contents-rendering.md +148 -0
- data/TODO.diagrams/57-instance-specification-model.md +100 -0
- data/TODO.diagrams/58-domain-model-note-text.md +67 -0
- data/TODO.diagrams/59-classes-arrow-discriminator.md +57 -0
- data/TODO.diagrams/60-testschema-association-marker.md +40 -0
- data/TODO.diagrams/61-legend-block-rendering.md +55 -0
- data/TODO.diagrams/61-plateau-parity-gaps.md +59 -0
- data/TODO.diagrams/62-ghost-element-rendering.md +56 -0
- data/TODO.diagrams/63-qualified-name-wrap.md +36 -0
- data/TODO.diagrams/64-property-stereotype-labels.md +22 -0
- data/TODO.diagrams/65-path-coordinate-frame.md +62 -0
- data/TODO.diagrams/67-path-coordinate-frame.md +62 -0
- data/TODO.diagrams/68-inherited-attributes.md +65 -0
- data/TODO.diagrams/69-package-anchor-conditional.md +29 -0
- data/TODO.diagrams/70-mdg-technology-support.md +68 -0
- data/TODO.diagrams/71-off-canvas-parent-ghost.md +45 -0
- data/TODO.diagrams/72-basic-attr-divider-paths.md +39 -0
- data/TODO.diagrams/73-plateau-polygon-gap.md +69 -0
- data/TODO.diagrams/74-plateau-rect-gap.md +22 -0
- data/TODO.diagrams/75-plateau-text-gap.md +39 -0
- data/TODO.diagrams/76-basic-poly-path-gaps.md +41 -0
- data/TODO.diagrams/77-basic-text-overrender.md +40 -0
- data/TODO.diagrams/78-headerlines-ocp-refactor.md +65 -0
- data/TODO.diagrams/79-architecture-backlog.md +65 -0
- data/TODO.diagrams/80-basic-triangle-polygons.md +51 -0
- data/TODO.diagrams/81-plateau-path-overrender.md +53 -0
- data/TODO.diagrams/82-appearance-canvas-offset.md +54 -0
- data/TODO.diagrams/83-3d-diagram-subtitle.md +58 -0
- data/TODO.diagrams/84-canvas-offset-mismatches.md +61 -0
- data/TODO.diagrams/85-phantom-property-overrender.md +95 -0
- data/TODO.diagrams/86-foreign-package-rule.md +62 -0
- data/TODO.diagrams/87-internal-tech-encryption.md +80 -0
- data/TODO.diagrams/EXAMPLES_STATUS.md +88 -0
- data/TODO.diagrams/README.md +69 -0
- data/TODO.diagrams/STATUS.md +73 -0
- data/TODO.fixup/01-default-grouped-paths.md +34 -0
- data/TODO.fixup/02-marker-consolidation-per-connector.md +20 -0
- data/TODO.fixup/03-diagram-font-inference.md +28 -0
- data/TODO.fixup/04-extract-bounds-calculator.md +25 -0
- data/TODO.fixup/05-marker-registry-ocp.md +32 -0
- data/TODO.fixup/06-connector-path-bend-routing.md +28 -0
- data/TODO.fixup/07-edge-attachment-formula.md +22 -0
- data/TODO.fixup/08-visual-diff-harness.md +28 -0
- data/TODO.fixup/09-extract-element-compartments.md +34 -0
- data/TODO.fixup/10-canvas-width-overshoot-fix.md +20 -0
- data/TODO.fixup/11-layer-sequencer-extraction.md +18 -0
- data/TODO.fixup/12-path-style-harmony.md +23 -0
- data/TODO.fixup/13-datatype-stereotype-fallback.md +28 -0
- data/TODO.fixup/14-ea-exe-reverse-engineering-notes.md +56 -0
- data/TODO.fixup/15-enumeration-literal-rendering.md +30 -0
- data/TODO.fixup/16-operation-compartment-rendering.md +42 -0
- data/TODO.fixup/17-path-count-calibration.md +27 -0
- data/TODO.fixup/18-polygon-count-calibration.md +32 -0
- data/TODO.fixup/19-rect-count-undershoot-investigation.md +40 -0
- data/TODO.fixup/20-stereotype-color-from-classifier.md +18 -0
- data/TODO.fixup/21-visibility-filter-for-properties.md +24 -0
- data/TODO.fixup/22-multiplicity-display-rule-calibration.md +25 -0
- data/TODO.fixup/23-diagram-frame-title-element.md +31 -0
- data/TODO.fixup/24-note-element-rendering.md +23 -0
- data/TODO.fixup/25-ea-exe-disassembly-findings.md +177 -0
- data/TODO.fixup/26-svg-renderer-architecture.md +78 -0
- data/TODO.fixup/27-svg-element-emitters-disassembly.md +135 -0
- data/TODO.fixup/28-geometry-parser-identification.md +57 -0
- data/TODO.fixup/29-qea-svg-xmi-triple-comparison.md +185 -0
- data/TODO.fixup/30-theme-system-stub.md +46 -0
- data/TODO.fixup/31-theme-value-object-registry.md +17 -0
- data/TODO.fixup/32-styleex-on-diagram-model.md +22 -0
- data/TODO.fixup/33-text-encoding-decimals-rotation.md +29 -0
- data/TODO.fixup/34-per-type-fill-via-theme.md +18 -0
- data/TODO.fixup/35-stroke-width-per-theme.md +7 -0
- data/TODO.fixup/36-qea-rect-coord-normalization.md +24 -0
- data/TODO.fixup/37-package-shape-rendering.md +22 -0
- data/TODO.fixup/38-note-element-rendering.md +15 -0
- data/TODO.fixup/39-styleex-flag-honoring.md +25 -0
- data/TODO.fixup/40-connector-dashed-lines.md +15 -0
- data/TODO.fixup/41-diagram-frame-showborder-detection.md +23 -0
- data/TODO.fixup/42-textrenderer-centralization.md +10 -0
- data/TODO.fixup/43-colorresolver-unification.md +24 -0
- data/TODO.fixup/44-theme-aware-textrenderer.md +17 -0
- data/TODO.fixup/45-theme-aware-fontresolver.md +22 -0
- data/TODO.fixup/46-style-parameterized-by-theme.md +21 -0
- data/TODO.fixup/47-canvas-frame-inset.md +19 -0
- data/TODO.fixup/48-background-rect-format.md +15 -0
- data/TODO.fixup/49-all-samples-integration-test.md +17 -0
- data/TODO.fixup/50-svg-emitter-architecture-docs.md +33 -0
- data/TODO.fixup/51-promote-theme-to-ea-namespace.md +28 -0
- data/TODO.fixup/52-theme-definition-integrated-fills.md +26 -0
- data/TODO.fixup/53-theme-registry-yaml-loading.md +50 -0
- data/TODO.fixup/54-diagram-theme-api.md +50 -0
- data/TODO.fixup/55-update-eaemitter-to-ea-theme.md +23 -0
- data/TODO.fixup/56-theme-yaml-files.md +65 -0
- data/TODO.fixup/57-theme-specs-and-integration.md +27 -0
- data/TODO.fixup/58-ea-entry-point-theme-autoload.md +20 -0
- data/TODO.fixup/59-thread-theme-through-all-renderers.md +29 -0
- data/TODO.fixup/60-cli-theme-option.md +22 -0
- data/TODO.fixup/61-theme-validation.md +28 -0
- data/TODO.fixup/62-theme-builder-dsl.md +31 -0
- data/TODO.fixup/63-theme-integration-rendering-spec.md +24 -0
- data/TODO.fixup/64-theme-writeback-styleex.md +21 -0
- data/TODO.fixup/65-remove-dead-eaemitter-theme-files.md +30 -0
- data/TODO.fixup/66-styleex-writeback.md +22 -0
- data/TODO.fixup/67-delete-dead-code.md +28 -0
- data/TODO.fixup/68-figure-parity-status.md +82 -0
- data/TODO.fixup/69-package-polygon-rendering.md +54 -0
- data/TODO.fixup/70-tagged-values-compartment.md +39 -0
- data/TODO.fixup/71-auto-detect-theme-from-xmi.md +37 -0
- data/TODO.fixup/72-sample-exports-as-regression-specs.md +37 -0
- data/TODO.fixup/73-delete-dead-transformations.md +14 -0
- data/TODO.fixup/74-delete-dead-diagram-renderers.md +18 -0
- data/TODO.fixup/75-visibility-legend-icons.md +23 -0
- data/TODO.fixup/76-plateau-text-undershoot.md +27 -0
- data/TODO.fixup/77-canvas-frame-margins.md +28 -0
- data/TODO.fixup/78-note-element-rendering.md +30 -0
- data/TODO.fixup/79-operation-compartment-rendering.md +27 -0
- data/TODO.fixup/80-tagged-value-stereotype-visibility.md +37 -0
- data/TODO.fixup/81-figure-parity-current-state.md +63 -0
- data/config/qea_schema.yml +356 -0
- data/config/themes/119.yml +98 -0
- data/config/themes/default.yml +14 -0
- data/examples/exports/20251010_current_plateau_v5/UML_EA.DTD +6856 -0
- data/examples/exports/20251010_current_plateau_v5/model.xml +96709 -0
- data/examples/exports/ArcGISWorkspace_template/UML_EA.DTD +6856 -0
- data/examples/exports/ArcGISWorkspace_template/model.xml +1427 -0
- data/examples/exports/UmlModel_template/UML_EA.DTD +6856 -0
- data/examples/exports/UmlModel_template/model.xml +23 -0
- data/examples/exports/basic/UML_EA.DTD +6856 -0
- data/examples/exports/basic/model.xml +8039 -0
- data/examples/exports/simple/UML_EA.DTD +6856 -0
- data/examples/exports/simple/model.xml +585 -0
- data/examples/exports/simple_example/UML_EA.DTD +6856 -0
- data/examples/exports/simple_example/model.xml +23 -0
- data/examples/exports/test/UML_EA.DTD +6856 -0
- data/examples/exports/test/model.xml +1638 -0
- data/examples/qea/basic.qea +0 -0
- data/frontend/README.md +51 -0
- data/frontend/dist/app.iife.js +24 -0
- data/frontend/dist/style.css +1 -0
- data/frontend/index.html +26 -0
- data/frontend/package-lock.json +2359 -0
- data/frontend/package.json +26 -0
- data/frontend/src/App.vue +59 -0
- data/frontend/src/app.ts +8 -0
- data/frontend/src/components/AppHeader.vue +127 -0
- data/frontend/src/components/AppSidebar.vue +119 -0
- data/frontend/src/components/ClassDetails.vue +247 -0
- data/frontend/src/components/DiagramView.vue +79 -0
- data/frontend/src/components/PackageDetails.vue +92 -0
- data/frontend/src/components/PackageTreeNode.vue +64 -0
- data/frontend/src/components/SearchResults.vue +48 -0
- data/frontend/src/components/WelcomeView.vue +87 -0
- data/frontend/src/shims-vue.d.ts +5 -0
- data/frontend/src/stores/dataStore.ts +79 -0
- data/frontend/src/stores/uiStore.ts +167 -0
- data/frontend/src/styles/main.css +1566 -0
- data/frontend/src/types.ts +221 -0
- data/frontend/tests/e2e/__screenshots__/new-dark-mode.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-package-details.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-search-modal.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-sidebar-collapsed.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-welcome.png +0 -0
- data/frontend/tests/e2e/render.spec.ts +188 -0
- data/frontend/tsconfig.json +20 -0
- data/frontend/vite.config.ts +26 -0
- data/frontend/vitest.config.ts +8 -0
- data/lib/ea/cli/app.rb +57 -2
- data/lib/ea/cli/command/diagrams.rb +37 -28
- data/lib/ea/cli/command/diff.rb +46 -0
- data/lib/ea/cli/command/export.rb +80 -0
- data/lib/ea/cli/command/info.rb +75 -0
- data/lib/ea/cli/command/lint.rb +48 -0
- data/lib/ea/cli/command/mdg.rb +98 -0
- data/lib/ea/cli/command/query.rb +34 -0
- data/lib/ea/cli/command/render.rb +135 -0
- data/lib/ea/cli/command/svg.rb +8 -15
- data/lib/ea/cli/command.rb +7 -0
- data/lib/ea/diagram/display_config.rb +184 -0
- data/lib/ea/diagram.rb +4 -41
- data/lib/ea/diff/change.rb +30 -0
- data/lib/ea/diff/comparator.rb +82 -0
- data/lib/ea/diff/html_reporter.rb +91 -0
- data/lib/ea/diff.rb +15 -0
- data/lib/ea/export/json/generator.rb +42 -0
- data/lib/ea/export/json.rb +12 -0
- data/lib/ea/export/json_schema/generator.rb +77 -0
- data/lib/ea/export/json_schema.rb +12 -0
- data/lib/ea/export/plantuml/generator.rb +73 -0
- data/lib/ea/export/plantuml.rb +12 -0
- data/lib/ea/export/xmi/generator.rb +77 -0
- data/lib/ea/export/xmi.rb +11 -0
- data/lib/ea/export/xsd/class_mapping.rb +65 -0
- data/lib/ea/export/xsd/generator.rb +142 -0
- data/lib/ea/export/xsd/namespace_registry.rb +82 -0
- data/lib/ea/export/xsd.rb +19 -0
- data/lib/ea/export.rb +14 -0
- data/lib/ea/image/emf_renderer.rb +42 -0
- data/lib/ea/image.rb +12 -0
- data/lib/ea/lint/engine.rb +30 -0
- data/lib/ea/lint/lint_rule.rb +38 -0
- data/lib/ea/lint/offense.rb +18 -0
- data/lib/ea/lint/rules/cyclic_generalization.rb +62 -0
- data/lib/ea/lint/rules/duplicate_name.rb +28 -0
- data/lib/ea/lint/rules/missing_stereotype.rb +35 -0
- data/lib/ea/lint/rules/naming_convention.rb +44 -0
- data/lib/ea/lint/rules/orphan_element.rb +28 -0
- data/lib/ea/lint/rules.rb +14 -0
- data/lib/ea/lint.rb +13 -0
- data/lib/ea/mdg/document.rb +120 -0
- data/lib/ea/mdg/loader.rb +118 -0
- data/lib/ea/mdg/registry.rb +98 -0
- data/lib/ea/mdg/stereotype_alias_registry.rb +77 -0
- data/lib/ea/mdg/xml/applies_to.rb +18 -0
- data/lib/ea/mdg/xml/apply.rb +18 -0
- data/lib/ea/mdg/xml/content.rb +18 -0
- data/lib/ea/mdg/xml/documentation.rb +24 -0
- data/lib/ea/mdg/xml/stereotype.rb +28 -0
- data/lib/ea/mdg/xml/stereotypes.rb +18 -0
- data/lib/ea/mdg/xml/tag.rb +28 -0
- data/lib/ea/mdg/xml/tagged_values.rb +18 -0
- data/lib/ea/mdg/xml/technology.rb +28 -0
- data/lib/ea/mdg/xml/uml_profile.rb +22 -0
- data/lib/ea/mdg/xml/uml_profiles.rb +18 -0
- data/lib/ea/mdg/xml.rb +24 -0
- data/lib/ea/mdg.rb +25 -0
- data/lib/ea/model/classifier.rb +4 -0
- data/lib/ea/model/constraint.rb +24 -0
- data/lib/ea/model/diagram.rb +85 -0
- data/lib/ea/model/diagram_connector.rb +34 -0
- data/lib/ea/model/diagram_element.rb +16 -0
- data/lib/ea/model/document.rb +47 -2
- data/lib/ea/model/ghost_label.rb +32 -0
- data/lib/ea/model/instance_specification.rb +34 -0
- data/lib/ea/model/legend.rb +43 -0
- data/lib/ea/model/legend_item.rb +26 -0
- data/lib/ea/model/note.rb +28 -0
- data/lib/ea/model/operation.rb +2 -0
- data/lib/ea/model/property.rb +2 -0
- data/lib/ea/model/relationship.rb +2 -0
- data/lib/ea/model/signal.rb +14 -0
- data/lib/ea/model/slot.rb +19 -0
- data/lib/ea/model.rb +8 -0
- data/lib/ea/ocl/evaluator.rb +94 -0
- data/lib/ea/ocl/nodes.rb +30 -0
- data/lib/ea/ocl/parser.rb +133 -0
- data/lib/ea/ocl/unsupported_error.rb +7 -0
- data/lib/ea/ocl.rb +13 -0
- data/lib/ea/qea/factory/document_builder.rb +24 -10
- data/lib/ea/qea/factory/generalization_builder.rb +9 -11
- data/lib/ea/qea/infrastructure/table_reader.rb +9 -0
- data/lib/ea/qea/models/ea_author.rb +35 -0
- data/lib/ea/qea/models/ea_glossary.rb +34 -0
- data/lib/ea/qea/models/ea_image.rb +53 -0
- data/lib/ea/qea/models/ea_implement.rb +32 -0
- data/lib/ea/qea/models/ea_list.rb +42 -0
- data/lib/ea/qea/models/ea_object_effort.rb +34 -0
- data/lib/ea/qea/models/ea_object_problem.rb +37 -0
- data/lib/ea/qea/models/ea_object_require.rb +34 -0
- data/lib/ea/qea/models/ea_object_resource.rb +34 -0
- data/lib/ea/qea/models/ea_object_risk.rb +34 -0
- data/lib/ea/qea/models/ea_object_scenario.rb +34 -0
- data/lib/ea/qea/models/ea_object_test.rb +35 -0
- data/lib/ea/qea/models/ea_object_trx.rb +34 -0
- data/lib/ea/qea/models/ea_operation.rb +7 -0
- data/lib/ea/qea/models/ea_palette.rb +30 -0
- data/lib/ea/qea/models/ea_palette_item.rb +36 -0
- data/lib/ea/qea/models/ea_phase.rb +40 -0
- data/lib/ea/qea/models/ea_role_constraint.rb +32 -0
- data/lib/ea/qea/models/ea_secrypt.rb +34 -0
- data/lib/ea/qea/models/ea_version.rb +39 -0
- data/lib/ea/qea/models/ea_xref.rb +11 -1
- data/lib/ea/qea/models.rb +19 -0
- data/lib/ea/qea/services/database_loader.rb +22 -1
- data/lib/ea/query/builder.rb +108 -0
- data/lib/ea/query.rb +9 -0
- data/lib/ea/render/image_converter.rb +101 -0
- data/lib/ea/render.rb +10 -0
- data/lib/ea/shapescript/parser.rb +208 -0
- data/lib/ea/shapescript/renderer.rb +74 -0
- data/lib/ea/shapescript/shape.rb +34 -0
- data/lib/ea/shapescript.rb +24 -0
- data/lib/ea/sources/qea/adapter.rb +22 -5
- data/lib/ea/sources/qea/classifier_builder.rb +184 -7
- data/lib/ea/sources/qea/diagram_builder.rb +352 -24
- data/lib/ea/sources/qea/id_normalizer.rb +16 -1
- data/lib/ea/sources/qea/instance_builder.rb +113 -0
- data/lib/ea/sources/qea/legend_builder.rb +137 -0
- data/lib/ea/sources/qea/note_builder.rb +58 -0
- data/lib/ea/sources/qea/object_classifier_map.rb +6 -1
- data/lib/ea/sources/qea/operation_builder.rb +1 -0
- data/lib/ea/sources/qea/package_builder.rb +16 -0
- data/lib/ea/sources/qea/relationship_builder.rb +21 -5
- data/lib/ea/sources/qea/tagged_value_builder.rb +28 -9
- data/lib/ea/sources/qea/xref/legend_definition.rb +35 -0
- data/lib/ea/sources/qea/xref/parser.rb +150 -0
- data/lib/ea/sources/qea/xref/property_definition.rb +29 -0
- data/lib/ea/sources/qea/xref/record.rb +41 -0
- data/lib/ea/sources/qea/xref/stereotype_application.rb +37 -0
- data/lib/ea/sources/qea/xref.rb +27 -0
- data/lib/ea/sources/qea.rb +4 -0
- data/lib/ea/sources/xmi/adapter.rb +80 -2
- data/lib/ea/sources/xmi/classifier_builder.rb +10 -1
- data/lib/ea/sources/xmi/diagram_builder.rb +95 -18
- data/lib/ea/sources/xmi/extension_geometry_parser.rb +19 -2
- data/lib/ea/sources/xmi/extension_style_parser.rb +15 -2
- data/lib/ea/sources/xmi/note_builder.rb +46 -0
- data/lib/ea/sources/xmi/property_builder.rb +9 -0
- data/lib/ea/sources/xmi/stereotype_builder.rb +54 -0
- data/lib/ea/sources/xmi/tag_builder.rb +45 -0
- data/lib/ea/sources/xmi/umldi_keyword_extractor.rb +87 -0
- data/lib/ea/sources/xmi.rb +4 -0
- data/lib/ea/spa/output/sharded_multi_file_strategy.rb +18 -3
- data/lib/ea/spa/output/single_file_strategy.rb +8 -2
- data/lib/ea/spa/output/strategy.rb +34 -0
- data/lib/ea/svg/connector_router.rb +134 -0
- data/lib/ea/svg/ea_emitter/background.rb +2 -3
- data/lib/ea/svg/ea_emitter/bounds_calculator.rb +142 -0
- data/lib/ea/svg/ea_emitter/canvas.rb +56 -33
- data/lib/ea/svg/ea_emitter/color_resolver.rb +56 -0
- data/lib/ea/svg/ea_emitter/compartment/attributes.rb +28 -0
- data/lib/ea/svg/ea_emitter/compartment/constraints.rb +30 -0
- data/lib/ea/svg/ea_emitter/compartment/enum_literals.rb +86 -0
- data/lib/ea/svg/ea_emitter/compartment/header.rb +32 -0
- data/lib/ea/svg/ea_emitter/compartment/header_divider.rb +57 -0
- data/lib/ea/svg/ea_emitter/compartment/instance_slots.rb +96 -0
- data/lib/ea/svg/ea_emitter/compartment/instance_underline.rb +67 -0
- data/lib/ea/svg/ea_emitter/compartment/note_body.rb +52 -0
- data/lib/ea/svg/ea_emitter/compartment/note_text.rb +102 -0
- data/lib/ea/svg/ea_emitter/compartment/operations.rb +38 -0
- data/lib/ea/svg/ea_emitter/compartment/package_contents.rb +72 -0
- data/lib/ea/svg/ea_emitter/compartment/package_from_parent.rb +136 -0
- data/lib/ea/svg/ea_emitter/compartment/shape.rb +82 -0
- data/lib/ea/svg/ea_emitter/compartment/tagged_values.rb +30 -0
- data/lib/ea/svg/ea_emitter/compartment.rb +58 -0
- data/lib/ea/svg/ea_emitter/connectors.rb +46 -12
- data/lib/ea/svg/ea_emitter/diagram_frame.rb +143 -0
- data/lib/ea/svg/ea_emitter/document.rb +15 -24
- data/lib/ea/svg/ea_emitter/element/attribute_line_builder.rb +117 -0
- data/lib/ea/svg/ea_emitter/element/attribute_renderer.rb +119 -0
- data/lib/ea/svg/ea_emitter/element/bcol_decoder.rb +29 -0
- data/lib/ea/svg/ea_emitter/element/compartment_geometry.rb +113 -0
- data/lib/ea/svg/ea_emitter/element/constraint_renderer.rb +53 -0
- data/lib/ea/svg/ea_emitter/element/divider_renderer.rb +17 -0
- data/lib/ea/svg/ea_emitter/element/enumeration_literal_renderer.rb +34 -0
- data/lib/ea/svg/ea_emitter/element/filter.rb +44 -0
- data/lib/ea/svg/ea_emitter/element/hand_draw_shape_renderer.rb +77 -0
- data/lib/ea/svg/ea_emitter/element/header_line_pipeline.rb +43 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/instance_spec.rb +27 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/name.rb +76 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/parent_ghost.rb +25 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/stereotype_label.rb +68 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider.rb +19 -0
- data/lib/ea/svg/ea_emitter/element/header_lines.rb +27 -0
- data/lib/ea/svg/ea_emitter/element/header_renderer.rb +48 -0
- data/lib/ea/svg/ea_emitter/element/icon_renderer.rb +109 -0
- data/lib/ea/svg/ea_emitter/element/legend_renderer.rb +177 -0
- data/lib/ea/svg/ea_emitter/element/note_shape_renderer.rb +112 -0
- data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +100 -0
- data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +181 -0
- data/lib/ea/svg/ea_emitter/element/row_icon_renderer.rb +156 -0
- data/lib/ea/svg/ea_emitter/element/shape_renderer.rb +17 -0
- data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +82 -0
- data/lib/ea/svg/ea_emitter/element/tagged_value_renderer.rb +50 -0
- data/lib/ea/svg/ea_emitter/element/text_escape.rb +20 -0
- data/lib/ea/svg/ea_emitter/element.rb +37 -0
- data/lib/ea/svg/ea_emitter/elements.rb +283 -95
- data/lib/ea/svg/ea_emitter/font_resolver.rb +111 -0
- data/lib/ea/svg/ea_emitter/ghost_labels.rb +66 -0
- data/lib/ea/svg/ea_emitter/label/end_label.rb +266 -0
- data/lib/ea/svg/ea_emitter/label/midpoint_label.rb +101 -0
- data/lib/ea/svg/ea_emitter/label/registry.rb +50 -0
- data/lib/ea/svg/ea_emitter/label.rb +23 -0
- data/lib/ea/svg/ea_emitter/labels.rb +93 -27
- data/lib/ea/svg/ea_emitter/layer.rb +20 -0
- data/lib/ea/svg/ea_emitter/layer_sequencer.rb +93 -0
- data/lib/ea/svg/ea_emitter/marker/arrow_path.rb +52 -0
- data/lib/ea/svg/ea_emitter/marker/diamond.rb +57 -0
- data/lib/ea/svg/ea_emitter/marker/kind.rb +35 -0
- data/lib/ea/svg/ea_emitter/marker/open_triangle.rb +27 -0
- data/lib/ea/svg/ea_emitter/marker/package_import.rb +39 -0
- data/lib/ea/svg/ea_emitter/marker/plus.rb +40 -0
- data/lib/ea/svg/ea_emitter/marker/registry.rb +42 -0
- data/lib/ea/svg/ea_emitter/marker.rb +37 -0
- data/lib/ea/svg/ea_emitter/markers.rb +351 -64
- data/lib/ea/svg/ea_emitter/render_context.rb +52 -0
- data/lib/ea/svg/ea_emitter/style.rb +15 -0
- data/lib/ea/svg/ea_emitter/text_renderer.rb +92 -0
- data/lib/ea/svg/ea_emitter.rb +33 -11
- data/lib/ea/svg/parity/checker.rb +105 -0
- data/lib/ea/svg/parity/source.rb +59 -0
- data/lib/ea/svg/parity/suite.rb +113 -0
- data/lib/ea/svg/parity.rb +14 -0
- data/lib/ea/svg.rb +2 -15
- data/lib/ea/theme/definition.rb +176 -0
- data/lib/ea/theme/loader.rb +125 -0
- data/lib/ea/theme/registry.rb +96 -0
- data/lib/ea/theme.rb +37 -0
- data/lib/ea/transformations.rb +5 -88
- data/lib/ea/version.rb +4 -1
- data/lib/ea.rb +14 -4
- data/lib/tasks/svg_bench.rake +87 -0
- data/lutaml-uml-0.5.1.gem +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes-list.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-2-gradients.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-3-colors.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance-fonts.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-5-behavior.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-6-sequence.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-2-objects.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-3-links.png +0 -0
- metadata +449 -26
- data/lib/ea/diagram/configuration.rb +0 -379
- data/lib/ea/diagram/element_renderers/base_renderer.rb +0 -77
- data/lib/ea/diagram/element_renderers/class_renderer.rb +0 -323
- data/lib/ea/diagram/element_renderers/connector_renderer.rb +0 -41
- data/lib/ea/diagram/element_renderers/package_renderer.rb +0 -61
- data/lib/ea/diagram/element_renderers.rb +0 -43
- data/lib/ea/diagram/extractor.rb +0 -566
- data/lib/ea/diagram/layout_engine.rb +0 -170
- data/lib/ea/diagram/path_builder.rb +0 -202
- data/lib/ea/diagram/style_parser.rb +0 -42
- data/lib/ea/diagram/style_resolver.rb +0 -291
- data/lib/ea/diagram/svg_renderer.rb +0 -274
- data/lib/ea/diagram/util.rb +0 -73
- data/lib/ea/svg/bounds_calculator.rb +0 -74
- data/lib/ea/svg/connector_path.rb +0 -138
- data/lib/ea/svg/element_box.rb +0 -256
- data/lib/ea/svg/renderer.rb +0 -77
- data/lib/ea/svg/style_resolver.rb +0 -72
- data/lib/ea/transformations/configuration.rb +0 -333
- data/lib/ea/transformations/format_registry.rb +0 -366
- data/lib/ea/transformations/parsers/base_parser.rb +0 -482
- data/lib/ea/transformations/parsers/qea_parser.rb +0 -401
- data/lib/ea/transformations/parsers/xmi_parser.rb +0 -243
- data/lib/ea/transformations/transformation_engine.rb +0 -390
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Element
|
|
7
|
+
# Emits the constraints compartment text `<g>`. EA renders
|
|
8
|
+
# constraints as a sub-compartment with an italic "constraints"
|
|
9
|
+
# header followed by `{name}` lines:
|
|
10
|
+
#
|
|
11
|
+
# <text style="...font-style:italic;...">constraints</text>
|
|
12
|
+
# <text style="...font-style:normal;...">{pattern}</text>
|
|
13
|
+
class ConstraintRenderer
|
|
14
|
+
HEADER_TEXT = "constraints"
|
|
15
|
+
HEADER_X_OFFSET = 5
|
|
16
|
+
LINE_X_OFFSET = 5
|
|
17
|
+
|
|
18
|
+
def self.render(constraints, bounds:, first_y:, family:, size:,
|
|
19
|
+
fill: "#000000")
|
|
20
|
+
line_h = size + 4
|
|
21
|
+
text_blocks = []
|
|
22
|
+
text_blocks << build_text(
|
|
23
|
+
bounds.x + centered_x_offset(bounds, size), first_y,
|
|
24
|
+
HEADER_TEXT, family, size, fill, style: "italic"
|
|
25
|
+
)
|
|
26
|
+
constraints.each_with_index do |constraint, idx|
|
|
27
|
+
y = first_y + ((idx + 1) * line_h)
|
|
28
|
+
text_blocks << build_text(
|
|
29
|
+
bounds.x + LINE_X_OFFSET, y,
|
|
30
|
+
"{#{constraint.name}}", family, size, fill
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
%(<g style="#{Style::TEXT_GROUP}">\n#{text_blocks.join("\n")}\n</g>)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.build_text(x, y, content, family, size, fill, style: "normal")
|
|
37
|
+
TextRenderer.new(content: content, x: x, y: y,
|
|
38
|
+
family: family, size: size, fill: fill,
|
|
39
|
+
style: style).to_svg
|
|
40
|
+
end
|
|
41
|
+
private_class_method :build_text
|
|
42
|
+
|
|
43
|
+
def self.centered_x_offset(bounds, size)
|
|
44
|
+
text_width = HEADER_TEXT.length * (size / 2)
|
|
45
|
+
centered = (bounds.width / 2 - text_width / 2).floor
|
|
46
|
+
[HEADER_X_OFFSET, centered].max
|
|
47
|
+
end
|
|
48
|
+
private_class_method :centered_x_offset
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Element
|
|
7
|
+
# Emits the horizontal divider path between header and
|
|
8
|
+
# attribute compartments.
|
|
9
|
+
class DividerRenderer
|
|
10
|
+
def self.render(bounds, y:, stroke:, stroke_width:)
|
|
11
|
+
%(<g style="stroke-width:#{stroke_width};stroke-linecap:round;stroke-linejoin:bevel; fill:#000000;fill-opacity:0.00; stroke:#{stroke}; stroke-opacity:1.00">\n <path d="M #{Canvas.coord(bounds.x)} #{Canvas.coord(y)} L #{Canvas.coord(bounds.x + bounds.width)} #{Canvas.coord(y)}" shape-rendering="auto"/>\n</g>)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Element
|
|
7
|
+
# Emits the enumeration literal compartment text `<g>`. Each
|
|
8
|
+
# literal appears as a separate text element matching EA's
|
|
9
|
+
# encoding:
|
|
10
|
+
#
|
|
11
|
+
# <text>literal_name</text>
|
|
12
|
+
#
|
|
13
|
+
# Rendered as a third compartment below the attribute
|
|
14
|
+
# compartment for Enumeration classifiers.
|
|
15
|
+
class EnumerationLiteralRenderer
|
|
16
|
+
def self.render(literals, bounds:, first_y:, family:, size:)
|
|
17
|
+
line_h = size + 4
|
|
18
|
+
text_blocks = literals.each_with_index.map do |literal, idx|
|
|
19
|
+
y = first_y + (idx * line_h)
|
|
20
|
+
build_text(bounds.x + 5, y, literal.name.to_s, family, size)
|
|
21
|
+
end
|
|
22
|
+
%(<g style="#{Style::TEXT_GROUP}">\n#{text_blocks.join("\n")}\n</g>)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.build_text(x, y, content, family, size)
|
|
26
|
+
TextRenderer.new(content: content, x: x, y: y,
|
|
27
|
+
family: family, size: size).to_svg
|
|
28
|
+
end
|
|
29
|
+
private_class_method :build_text
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Element
|
|
7
|
+
# Predicate: should this element be rendered, or is it a
|
|
8
|
+
# diagram-frame placeholder that EA skips?
|
|
9
|
+
class Filter
|
|
10
|
+
attr_reader :model_index
|
|
11
|
+
|
|
12
|
+
def initialize(model_index:)
|
|
13
|
+
@model_index = model_index
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Returns true if the element should be SKIPPED.
|
|
17
|
+
# EA emits a "diagram frame placeholder" Classifier with
|
|
18
|
+
# no name and no properties on some diagrams — those get
|
|
19
|
+
# dropped. Packages and named Classifiers are always kept.
|
|
20
|
+
def skip?(element)
|
|
21
|
+
return false unless element.background_color == -1
|
|
22
|
+
|
|
23
|
+
classifier = classifier_for(element)
|
|
24
|
+
return false unless classifier
|
|
25
|
+
|
|
26
|
+
classifier.name.to_s.strip.empty? && (classifier.properties.nil? || classifier.properties.empty?)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def classifier_for(element)
|
|
32
|
+
ref = element.model_element_ref
|
|
33
|
+
return nil unless ref
|
|
34
|
+
|
|
35
|
+
candidate = model_index[ref]
|
|
36
|
+
return nil unless candidate.is_a?(Ea::Model::Classifier)
|
|
37
|
+
|
|
38
|
+
candidate
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Element
|
|
7
|
+
# Emits a hand-drawn style shape `<path>` for elements on
|
|
8
|
+
# diagrams that have HandDraw=1 in their StyleEx. EA renders
|
|
9
|
+
# each element as a closed `<path>` with cubic beziers per
|
|
10
|
+
# side (slight wave) instead of a clean `<rect>`. Used by
|
|
11
|
+
# Domain Model diagrams.
|
|
12
|
+
class HandDrawShapeRenderer
|
|
13
|
+
WAVE_AMPLITUDE = 2
|
|
14
|
+
WAVE_LENGTH = 70
|
|
15
|
+
|
|
16
|
+
def self.render(bounds, fill:, stroke:, stroke_width:)
|
|
17
|
+
new(bounds, fill, stroke, stroke_width).to_svg
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize(bounds, fill, stroke, stroke_width)
|
|
21
|
+
@bounds = bounds
|
|
22
|
+
@fill = fill
|
|
23
|
+
@stroke = stroke
|
|
24
|
+
@stroke_width = stroke_width
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def to_svg
|
|
28
|
+
%(<g style="#{group_style}">\n #{shape_path}\n</g>)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
attr_reader :bounds, :fill, :stroke, :stroke_width
|
|
34
|
+
|
|
35
|
+
def group_style
|
|
36
|
+
"stroke-width:#{stroke_width};stroke-linecap:round;" \
|
|
37
|
+
"stroke-linejoin:bevel; fill:#{fill};fill-opacity:1.00;" \
|
|
38
|
+
" stroke:#{stroke}; stroke-opacity:1.00"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Approximation of EA's hand-drawn border. Each side is a
|
|
42
|
+
# cubic bezier from one corner to the next, with control
|
|
43
|
+
# points pulled slightly inward to give a wavy silhouette.
|
|
44
|
+
# The exact ref encoding uses many short S-curves; this
|
|
45
|
+
# emits 4 C commands (one per side) which is shape-count
|
|
46
|
+
# compatible.
|
|
47
|
+
def shape_path
|
|
48
|
+
x1 = bounds.x.to_f
|
|
49
|
+
y1 = bounds.y.to_f
|
|
50
|
+
x2 = x1 + bounds.width.to_f
|
|
51
|
+
y2 = y1 + bounds.height.to_f
|
|
52
|
+
cx1 = x1 + bounds.width.to_f / 2
|
|
53
|
+
cy1 = y1 + bounds.height.to_f / 2
|
|
54
|
+
d = "M #{coord(x1)} #{coord(y1)} " \
|
|
55
|
+
"C #{coord(x1 + WAVE_LENGTH)} #{coord(y1 - WAVE_AMPLITUDE)} " \
|
|
56
|
+
"#{coord(cx1 + WAVE_LENGTH / 2)} #{coord(y1 + WAVE_AMPLITUDE)} " \
|
|
57
|
+
"#{coord(x2)} #{coord(y1)} " \
|
|
58
|
+
"C #{coord(x2 + WAVE_AMPLITUDE)} #{coord(y1 + WAVE_LENGTH)} " \
|
|
59
|
+
"#{coord(x2 - WAVE_AMPLITUDE)} #{coord(cy1 + WAVE_LENGTH / 2)} " \
|
|
60
|
+
"#{coord(x2)} #{coord(y2)} " \
|
|
61
|
+
"C #{coord(x2 - WAVE_LENGTH)} #{coord(y2 + WAVE_AMPLITUDE)} " \
|
|
62
|
+
"#{coord(cx1 - WAVE_LENGTH / 2)} #{coord(y2 - WAVE_AMPLITUDE)} " \
|
|
63
|
+
"#{coord(x1)} #{coord(y2)} " \
|
|
64
|
+
"C #{coord(x1 - WAVE_AMPLITUDE)} #{coord(y2 - WAVE_LENGTH)} " \
|
|
65
|
+
"#{coord(x1 + WAVE_AMPLITUDE)} #{coord(cy1 - WAVE_LENGTH / 2)} " \
|
|
66
|
+
"#{coord(x1)} #{coord(y1)} Z"
|
|
67
|
+
%(<path d="#{d}" shape-rendering="auto"/>)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def coord(value)
|
|
71
|
+
Ea::Svg::EaEmitter::Canvas.coord(value)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Element
|
|
7
|
+
# OCP-friendly header line pipeline. Each contributor is a
|
|
8
|
+
# stateless provider that appends 0+ lines to the output.
|
|
9
|
+
# New behaviors add a new provider to PROVIDERS without
|
|
10
|
+
# modifying existing ones.
|
|
11
|
+
#
|
|
12
|
+
# Context (Struct) carries everything a provider might need:
|
|
13
|
+
# classifier, diagram_package_id, visually_nested,
|
|
14
|
+
# umldi_keyword, bounds_width, font_size,
|
|
15
|
+
# off_canvas_parent_name
|
|
16
|
+
#
|
|
17
|
+
# Each provider implements:
|
|
18
|
+
# def self.call(context) → Array<[[text, style], ...]>
|
|
19
|
+
module HeaderLinePipeline
|
|
20
|
+
Context = Struct.new(:classifier, :diagram_package_id,
|
|
21
|
+
:visually_nested, :umldi_keyword,
|
|
22
|
+
:bounds_width, :font_size,
|
|
23
|
+
:off_canvas_parent_name,
|
|
24
|
+
keyword_init: true)
|
|
25
|
+
|
|
26
|
+
PROVIDERS = [
|
|
27
|
+
HeaderLineProvider::ParentGhost,
|
|
28
|
+
HeaderLineProvider::InstanceSpec,
|
|
29
|
+
HeaderLineProvider::StereotypeLabel,
|
|
30
|
+
HeaderLineProvider::Name
|
|
31
|
+
].freeze
|
|
32
|
+
|
|
33
|
+
# @param classifier [Ea::Model::Classifier, Ea::Model::InstanceSpecification]
|
|
34
|
+
# @return [Array<Array(String, Symbol)>] list of [text, style] pairs
|
|
35
|
+
def self.for(classifier, **opts)
|
|
36
|
+
context = Context.new(classifier: classifier, **opts)
|
|
37
|
+
PROVIDERS.flat_map { |provider| provider.call(context) }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Element
|
|
7
|
+
module HeaderLineProvider
|
|
8
|
+
# InstanceSpecification: short-circuits the pipeline. The
|
|
9
|
+
# entire header for an instance is "name: Classifier" on
|
|
10
|
+
# one bold line. No stereotype, no wrap, no parent ghost.
|
|
11
|
+
class InstanceSpec
|
|
12
|
+
def self.call(context)
|
|
13
|
+
inst = context.classifier
|
|
14
|
+
return [] unless inst.is_a?(Ea::Model::InstanceSpecification)
|
|
15
|
+
|
|
16
|
+
label = inst.name.to_s
|
|
17
|
+
if inst.classifier_name && !inst.classifier_name.empty?
|
|
18
|
+
label = "#{label}: #{inst.classifier_name}"
|
|
19
|
+
end
|
|
20
|
+
[[label, :bold]]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Element
|
|
7
|
+
module HeaderLineProvider
|
|
8
|
+
# Emits the classifier's display name, with these behaviors:
|
|
9
|
+
#
|
|
10
|
+
# - Bold-italic for abstract Klass; bold otherwise.
|
|
11
|
+
# - Qualified name wrap when the rendered width exceeds the
|
|
12
|
+
# element bounds. EA splits "pkg::Class" across two lines
|
|
13
|
+
# when the combined width is too wide.
|
|
14
|
+
#
|
|
15
|
+
# InstanceSpecification returns [] (InstanceSpec provider
|
|
16
|
+
# already emitted the full label).
|
|
17
|
+
class Name
|
|
18
|
+
QUALIFIED_WRAP_WIDTH_FACTOR = 0.55
|
|
19
|
+
QUALIFIED_WRAP_PADDING = 8
|
|
20
|
+
|
|
21
|
+
def self.call(context)
|
|
22
|
+
classifier = context.classifier
|
|
23
|
+
return [] if classifier.is_a?(Ea::Model::InstanceSpecification)
|
|
24
|
+
|
|
25
|
+
weight = weight_for(classifier)
|
|
26
|
+
wrapped_name_lines(classifier, context, weight)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @param classifier [Ea::Model::Classifier]
|
|
30
|
+
# @return [Symbol] :bold_italic for abstract, :bold otherwise
|
|
31
|
+
def self.weight_for(classifier)
|
|
32
|
+
classifier.is_a?(Ea::Model::Klass) && classifier.is_abstract ? :bold_italic : :bold
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.wrapped_name_lines(classifier, context, weight)
|
|
36
|
+
name = display_name(classifier, context.diagram_package_id,
|
|
37
|
+
context.visually_nested)
|
|
38
|
+
unless context.bounds_width && name.include?("::") &&
|
|
39
|
+
name_exceeds_bounds?(name, context.bounds_width,
|
|
40
|
+
context.font_size || 9)
|
|
41
|
+
return [[name, weight]]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
qualifier, base = name.split("::", 2)
|
|
45
|
+
[["#{qualifier}::", weight], [base, weight]]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.name_exceeds_bounds?(name, bounds_width, font_size)
|
|
49
|
+
TextRenderer.estimate_width(name, font_size,
|
|
50
|
+
QUALIFIED_WRAP_WIDTH_FACTOR) >
|
|
51
|
+
(bounds_width - QUALIFIED_WRAP_PADDING)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.display_name(classifier, diagram_package_id = nil,
|
|
55
|
+
_visually_nested = false)
|
|
56
|
+
name = if classifier.is_a?(Ea::Model::Classifier)
|
|
57
|
+
(classifier.qualified_name || classifier.name).to_s
|
|
58
|
+
else
|
|
59
|
+
classifier.name.to_s
|
|
60
|
+
end
|
|
61
|
+
return name if diagram_package_id.nil?
|
|
62
|
+
return name unless name.include?("::")
|
|
63
|
+
|
|
64
|
+
if classifier.is_a?(Ea::Model::Classifier) &&
|
|
65
|
+
classifier.package_id == diagram_package_id
|
|
66
|
+
name.split("::").last
|
|
67
|
+
else
|
|
68
|
+
name
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Element
|
|
7
|
+
module HeaderLineProvider
|
|
8
|
+
# Emits the off-canvas parent classifier name as the first
|
|
9
|
+
# italic line. EA renders this when HideParents=0 and the
|
|
10
|
+
# classifier's generalization parent is not placed on the
|
|
11
|
+
# diagram.
|
|
12
|
+
class ParentGhost
|
|
13
|
+
# @param context [HeaderLinePipeline::Context]
|
|
14
|
+
def self.call(context)
|
|
15
|
+
name = context.off_canvas_parent_name
|
|
16
|
+
return [] unless name
|
|
17
|
+
|
|
18
|
+
[[name, :italic]]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Element
|
|
7
|
+
module HeaderLineProvider
|
|
8
|
+
# Emits the stereotype label `«name»` for classifiers with
|
|
9
|
+
# an explicit stereotype, OR a type-derived fallback
|
|
10
|
+
# («enumeration», «dataType», etc.) for non-Klass classifiers.
|
|
11
|
+
#
|
|
12
|
+
# Plain Klass with no stereotype gets no label — EA's
|
|
13
|
+
# behavior.
|
|
14
|
+
#
|
|
15
|
+
# InstanceSpecification is excluded (it owns its own header).
|
|
16
|
+
class StereotypeLabel
|
|
17
|
+
def self.call(context)
|
|
18
|
+
classifier = context.classifier
|
|
19
|
+
return [] if classifier.is_a?(Ea::Model::InstanceSpecification)
|
|
20
|
+
|
|
21
|
+
label = label_for(classifier, context.umldi_keyword)
|
|
22
|
+
return [] unless label
|
|
23
|
+
|
|
24
|
+
[[label, :normal]]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @return [String, nil] «name» label or nil
|
|
28
|
+
def self.label_for(classifier, umldi_keyword)
|
|
29
|
+
return "«#{umldi_keyword}»" if umldi_keyword && !umldi_keyword.empty?
|
|
30
|
+
|
|
31
|
+
explicit = explicit_stereotype(classifier)
|
|
32
|
+
return explicit if explicit
|
|
33
|
+
|
|
34
|
+
case classifier
|
|
35
|
+
when Ea::Model::Klass, Ea::Model::Package, Ea::Model::Note
|
|
36
|
+
nil
|
|
37
|
+
else
|
|
38
|
+
fallback = fallback_name(classifier)
|
|
39
|
+
fallback ? "«#{fallback}»" : nil
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.explicit_stereotype(classifier)
|
|
44
|
+
return nil unless classifier.is_a?(Ea::Model::Classifier) ||
|
|
45
|
+
classifier.is_a?(Ea::Model::Package)
|
|
46
|
+
|
|
47
|
+
refs = classifier.stereotype_refs
|
|
48
|
+
return nil unless refs&.any?
|
|
49
|
+
|
|
50
|
+
"«#{refs.first}»"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.fallback_name(classifier)
|
|
54
|
+
case classifier
|
|
55
|
+
when Ea::Model::Enumeration then "enumeration"
|
|
56
|
+
when Ea::Model::DataType then "dataType"
|
|
57
|
+
when Ea::Model::PrimitiveType then "primitive"
|
|
58
|
+
when Ea::Model::Interface then "interface"
|
|
59
|
+
when Ea::Model::Signal then "signal"
|
|
60
|
+
else "DataType"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Element
|
|
7
|
+
# Namespace for HeaderLinePipeline providers. Each provider
|
|
8
|
+
# owns ONE contribution to the header. Adding a new line type
|
|
9
|
+
# (e.g. legend symbol) = adding a new provider here.
|
|
10
|
+
module HeaderLineProvider
|
|
11
|
+
autoload :ParentGhost, "ea/svg/ea_emitter/element/header_line_provider/parent_ghost"
|
|
12
|
+
autoload :InstanceSpec, "ea/svg/ea_emitter/element/header_line_provider/instance_spec"
|
|
13
|
+
autoload :StereotypeLabel, "ea/svg/ea_emitter/element/header_line_provider/stereotype_label"
|
|
14
|
+
autoload :Name, "ea/svg/ea_emitter/element/header_line_provider/name"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Element
|
|
7
|
+
# Legacy entry point preserved for backward compatibility.
|
|
8
|
+
# Delegates to HeaderLinePipeline — the OCP-friendly provider
|
|
9
|
+
# chain that replaces this module. New code should call
|
|
10
|
+
# HeaderLinePipeline directly.
|
|
11
|
+
module HeaderLines
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
def for(classifier, **opts)
|
|
15
|
+
HeaderLinePipeline.for(classifier, **opts)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Delegate to the Name provider so historical callers and
|
|
19
|
+
# specs that test display_name keep working.
|
|
20
|
+
def display_name(classifier, diagram_package_id = nil, *_)
|
|
21
|
+
HeaderLineProvider::Name.display_name(classifier, diagram_package_id)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Element
|
|
7
|
+
# Emits the header text `<g>` containing stereotype + class
|
|
8
|
+
# name lines. Lines + styling computed by HeaderLines.
|
|
9
|
+
#
|
|
10
|
+
# EA centers header text within the element bounds (computing
|
|
11
|
+
# the left edge from text-width approximation since SVG text
|
|
12
|
+
# x= is the start position, not the center).
|
|
13
|
+
class HeaderRenderer
|
|
14
|
+
def self.render(lines, bounds:, first_y:, family:,
|
|
15
|
+
size:, size_unit: "pt",
|
|
16
|
+
fill: "#000000", weight_normal: 400, weight_bold: 700,
|
|
17
|
+
stroke_in_text: "#000000", width_factor: 0.65,
|
|
18
|
+
line_offset: 6)
|
|
19
|
+
line_h = size + line_offset
|
|
20
|
+
text_blocks = lines.each_with_index.map do |(text, style), idx|
|
|
21
|
+
weight = case style
|
|
22
|
+
when :bold, :bold_italic then weight_bold
|
|
23
|
+
else weight_normal
|
|
24
|
+
end
|
|
25
|
+
font_style = (style == :italic || style == :bold_italic) ? "italic" : "normal"
|
|
26
|
+
y = first_y + (idx * line_h)
|
|
27
|
+
centered_x = center_x_for(text, bounds, size, width_factor)
|
|
28
|
+
TextRenderer.new(
|
|
29
|
+
content: text,
|
|
30
|
+
x: centered_x, y: y,
|
|
31
|
+
family: family, size: size, size_unit: size_unit,
|
|
32
|
+
weight: weight, style: font_style, fill: fill,
|
|
33
|
+
stroke_in_text: stroke_in_text, width_factor: width_factor
|
|
34
|
+
).to_svg
|
|
35
|
+
end
|
|
36
|
+
%(<g style="#{Style::TEXT_GROUP}">\n#{text_blocks.join("\n")}\n</g>)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.center_x_for(text, bounds, size, width_factor)
|
|
40
|
+
text_width = Ea::Svg::EaEmitter::TextRenderer.estimate_width(text, size, width_factor)
|
|
41
|
+
bounds.x + (bounds.width - text_width) / 2.0
|
|
42
|
+
end
|
|
43
|
+
private_class_method :center_x_for
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Element
|
|
7
|
+
# Emits the small "folded paper" classifier-type icon at the
|
|
8
|
+
# top-right corner of an element box. EA renders this when
|
|
9
|
+
# the element's style does NOT set HideIcon=1.
|
|
10
|
+
#
|
|
11
|
+
# The icon is two `<path>` elements:
|
|
12
|
+
#
|
|
13
|
+
# 1. Outer outline (closed): a 9×11 rectangle with the
|
|
14
|
+
# top-right corner "folded" (a 3×4 triangular cut).
|
|
15
|
+
# 2. Folded corner detail (open): the L-shape indicating
|
|
16
|
+
# the fold.
|
|
17
|
+
#
|
|
18
|
+
# Style:
|
|
19
|
+
# - Outer: fill=#DCF8F0, stroke=#577AC1
|
|
20
|
+
# - Corner: fill=transparent, stroke=#577AC1
|
|
21
|
+
# - stroke-width=1, stroke-linecap=square, linejoin=bevel
|
|
22
|
+
class IconRenderer
|
|
23
|
+
OUTER_FILL = "#DCF8F0"
|
|
24
|
+
OUTER_STROKE = "#577AC1"
|
|
25
|
+
CORNER_FILL = "##{0.to_s.rjust(6, '0')}" # transparent black
|
|
26
|
+
WIDTH = 9
|
|
27
|
+
HEIGHT = 11
|
|
28
|
+
FOLD_WIDTH = 3
|
|
29
|
+
FOLD_HEIGHT = 4
|
|
30
|
+
X_INSET = 13
|
|
31
|
+
Y_OFFSET = 3
|
|
32
|
+
|
|
33
|
+
attr_reader :bounds, :canvas
|
|
34
|
+
|
|
35
|
+
def initialize(bounds:, canvas: nil)
|
|
36
|
+
@bounds = bounds
|
|
37
|
+
@canvas = canvas
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.render(bounds:, canvas: nil, family: nil, size: nil)
|
|
41
|
+
new(bounds: bounds, canvas: canvas).to_svg
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Returns the two `<path>` elements joined as a single
|
|
45
|
+
# String. Returns "" if bounds is nil.
|
|
46
|
+
def to_svg
|
|
47
|
+
return "" unless bounds
|
|
48
|
+
|
|
49
|
+
"#{outer_path}\n#{corner_path}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def outer_path
|
|
55
|
+
x, y = top_left
|
|
56
|
+
right = x + WIDTH
|
|
57
|
+
bottom = y + HEIGHT
|
|
58
|
+
fold_x = right - FOLD_WIDTH
|
|
59
|
+
fold_y = y + FOLD_HEIGHT
|
|
60
|
+
d = "M #{coord(x)} #{coord(y)} " \
|
|
61
|
+
"L #{coord(fold_x)} #{coord(y)} " \
|
|
62
|
+
"L #{coord(right)} #{coord(fold_y)} " \
|
|
63
|
+
"L #{coord(right)} #{coord(bottom)} " \
|
|
64
|
+
"L #{coord(x)} #{coord(bottom)} " \
|
|
65
|
+
"L #{coord(x)} #{coord(y)} Z"
|
|
66
|
+
%(<g style="#{outer_style}">\n <path d="#{d}" shape-rendering="auto"/>\n</g>)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def corner_path
|
|
70
|
+
x, y = top_left
|
|
71
|
+
right = x + WIDTH
|
|
72
|
+
fold_x = right - FOLD_WIDTH
|
|
73
|
+
fold_y = y + FOLD_HEIGHT
|
|
74
|
+
d = "M #{coord(fold_x)} #{coord(y)} " \
|
|
75
|
+
"L #{coord(fold_x)} #{coord(fold_y)} " \
|
|
76
|
+
"L #{coord(right)} #{coord(fold_y)}"
|
|
77
|
+
%(<g style="#{corner_style}">\n <path d="#{d}" shape-rendering="auto"/>\n</g>)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def outer_style
|
|
81
|
+
"stroke-width:1;stroke-linecap:square;stroke-linejoin:bevel; " \
|
|
82
|
+
"fill:#{OUTER_FILL};fill-opacity:1.00; stroke:#{OUTER_STROKE}; stroke-opacity:1.00"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def corner_style
|
|
86
|
+
"stroke-width:1;stroke-linecap:square;stroke-linejoin:bevel; " \
|
|
87
|
+
"fill:#000000;fill-opacity:0.00; stroke:#{OUTER_STROKE}; stroke-opacity:1.00"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Top-left of the icon: x = bounds.right - X_INSET,
|
|
91
|
+
# y = bounds.top + Y_OFFSET.
|
|
92
|
+
def top_left
|
|
93
|
+
tx = bounds.x + bounds.width - X_INSET
|
|
94
|
+
ty = bounds.y + Y_OFFSET
|
|
95
|
+
if canvas
|
|
96
|
+
[canvas.translate_x(tx), canvas.translate_y(ty)]
|
|
97
|
+
else
|
|
98
|
+
[tx, ty]
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def coord(value)
|
|
103
|
+
Ea::Svg::EaEmitter::Canvas.coord(value)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|