From 99b45b0c065d091c7ba686b8832c51694729c771 Mon Sep 17 00:00:00 2001 From: Jean Patrick Mathes <49759783+jeanpmathes@users.noreply.github.com> Date: Thu, 21 Dec 2023 21:19:02 +0100 Subject: [PATCH] Add informal diagram consistency pipeline steps (#308) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adapt code model to support nested classes * Extend code model to store references of types to other types * Move code from thesis repo into ArDoCo core * Fix some test * Merge the two existing diagram classes * Fix the large graph test * Fix version name * Update stages/diagram-consistency/pom.xml Co-authored-by: Dominik Fuchß * Adapt pom.xml files as reviewed and adapt to changes related to package structure * Update framework/common/pom.xml * Apply spotless * Reset benchmark * Squashed 'tests/tests-base/src/main/resources/benchmark/' changes from 7675243a..daeaee7e daeaee7e Update code models to contain parent class and reference info (#12) 4fae195e Format Code Model. Fix #13 814aa945 Remove duplicate in GS git-subtree-dir: tests/tests-base/src/main/resources/benchmark git-subtree-split: daeaee7e6273722b44a1a82b5d7bdd204edd743c * Let evaluation code extract code models if no already extracted model available * Apply spotless * Use assertion messages to gain more information about the occurring failures * When extracting a code model, clone first * Delete directory before cloning * Squashed 'tests/tests-base/src/main/resources/benchmark/' changes from daeaee7e..9bbc3a89 9bbc3a89 Update code models to contain parent class and reference info, using new formatting (#16) fe5a8549 Update the codemodel to be more (human) readable git-subtree-dir: tests/tests-base/src/main/resources/benchmark git-subtree-split: 9bbc3a89677ffba98bb861f0c0206a93c61ec5d1 * Work on code smells * Remove annotation that leads to compile error * Register diagram-consistency to report * Fix loading of ACM * Remove commented out import --------- Co-authored-by: Dominik Fuchß --- framework/common/pom.xml | 5 + .../DiagramMatchingModelSelectionState.java | 123 +++ .../DiagramModelInconsistencyState.java | 56 ++ .../DiagramModelLinkState.java | 38 + .../api/diagramconsistency/DiagramState.java | 30 + .../common/DiagramUtility.java | 127 +++ .../api/diagramconsistency/common/Edge.java | 37 + .../common/ElementRole.java | 30 + .../common/Extractions.java | 165 ++++ .../common/JsonMapping.java | 36 + .../api/diagramconsistency/common/Label.java | 18 + .../common/TextSimilarity.java | 96 +++ .../common/Transformations.java | 343 +++++++++ .../api/diagramconsistency/common/Vertex.java | 64 ++ .../common/WeightedTextSimilarity.java | 115 +++ .../HierarchyInconsistency.java | 80 ++ .../common/inconsistencies/Inconsistency.java | 104 +++ .../inconsistencies/InconsistencyType.java | 34 + .../MissingBoxInconsistency.java | 39 + .../MissingLineInconsistency.java | 76 ++ .../inconsistencies/NameInconsistency.java | 82 ++ .../UnexpectedBoxInconsistency.java | 39 + .../UnexpectedLineInconsistency.java | 76 ++ .../inconsistencies/refinement/Casing.java | 114 +++ .../inconsistencies/refinement/Group.java | 70 ++ .../refinement/LineInversion.java | 117 +++ .../refinement/NameExtension.java | 129 ++++ .../inconsistencies/refinement/Swap.java | 117 +++ .../rules/AllBoxesMustBeLinked.java | 23 + .../AllModelEntitiesMustBeRepresented.java | 24 + .../rules/BoxesMustBeInParent.java | 80 ++ ...sMustBeConnectedExactlyToDependencies.java | 70 ++ ...MustContainAllSubpackagesIfOneIsEmpty.java | 92 +++ .../common/inconsistencies/rules/Rule.java | 84 ++ .../rules/SameNameForLinkedElements.java | 28 + .../similarityflooding/FixpointFormula.java | 120 +++ .../similarityflooding/LabeledEdge.java | 17 + .../similarityflooding/MatchingFilter.java | 25 + .../OrderedMatchingFilter.java | 108 +++ .../PropagationCoefficientFormula.java | 63 ++ .../SimilarityFloodingAlgorithm.java | 281 +++++++ .../similarityflooding/SimilarityMapping.java | 148 ++++ .../core/api/diagramrecognition/Box.java | 24 +- .../core/api/models/arcotl/code/Datatype.java | 42 +- pom.xml | 1 + report/pom.xml | 6 + stages/diagram-consistency/pom.xml | 71 ++ .../DiagramConsistency.java | 83 ++ .../DiagramInconsistencyStateImpl.java | 39 + ...iagramMatchingModelSelectionStateImpl.java | 91 +++ .../DiagramModelLinkStateImpl.java | 40 + .../diagramconsistency/DiagramStateImpl.java | 23 + .../DiagramModelInconsistencyAgent.java | 51 ++ .../agents/DiagramModelMatchingAgent.java | 48 ++ .../agents/DiagramModelSelectionAgent.java | 66 ++ .../agents/DiagramProviderAgent.java | 39 + ...agramElementOccurrenceFinderInformant.java | 161 ++++ .../DiagramModelInconsistencyInformant.java | 170 +++++ .../informants/DiagramModelLinkInformant.java | 156 ++++ .../informants/DiagramProviderInformant.java | 91 +++ .../InconsistencyGroupingInformant.java | 76 ++ .../InconsistencyRefinementInformant.java | 101 +++ .../OccurrenceToDecisionInformant.java | 126 +++ .../WeightedSimilarityInformant.java | 108 +++ .../common/TextSimilarityTest.java | 20 + .../SimilarityFloodingAlgorithmTest.java | 136 ++++ .../evaluation/DiagramLoadingTest.java | 125 +++ .../evaluation/EvaluationTestBase.java | 159 ++++ .../evaluation/IntegerMetrics.java | 30 + .../evaluation/MapMetrics.java | 73 ++ .../evaluation/Metrics.java | 57 ++ .../evaluation/MetricsStats.java | 84 ++ ...yntheticDiagramMatchingEvaluationTest.java | 166 ++++ .../evaluation/SyntheticDiagramsTest.java | 81 ++ .../evaluation/data/AnnotatedDiagram.java | 157 ++++ .../evaluation/data/AnnotatedGraph.java | 158 ++++ .../evaluation/data/DiagramProject.java | 148 ++++ .../evaluation/data/stage1/Element.java | 15 + .../data/stage1/ElementIdentification.java | 15 + .../evaluation/data/stage1/Occurrence.java | 17 + .../evaluation/data/stage2/ElementLinks.java | 30 + .../evaluation/data/stage2/Link.java | 19 + .../data/stage3/DiagramInconsistencies.java | 40 + .../data/stage3/DiagramInconsistency.java | 110 +++ .../evaluation/refactoring/AppendSuffix.java | 37 + .../evaluation/refactoring/Connect.java | 63 ++ .../evaluation/refactoring/Create.java | 70 ++ .../evaluation/refactoring/Delete.java | 51 ++ .../evaluation/refactoring/Disconnect.java | 62 ++ .../evaluation/refactoring/Mixed.java | 59 ++ .../evaluation/refactoring/Move.java | 86 +++ .../refactoring/PackageSelection.java | 35 + .../refactoring/PartialSelection.java | 84 ++ .../evaluation/refactoring/Refactoring.java | 100 +++ .../refactoring/RefactoringBundle.java | 52 ++ .../evaluation/refactoring/Rename.java | 51 ++ .../informants/DiagramConsistencyTest.java | 721 ++++++++++++++++++ .../informants/ExaminationDescription.java | 17 + .../ExaminationDescriptionBuilder.java | 64 ++ .../informants/GeneralModelType.java | 16 + .../Stage1SyntheticDiagramTest.java | 193 +++++ .../Stage2SyntheticDiagramTest.java | 336 ++++++++ .../Stage3SyntheticDiagramTest.java | 92 +++ .../informants/SyntheticTestBase.java | 183 +++++ .../src/test/resources/.gitignore | 1 + .../resources/gs/bigbluebutton/diagram.json | 1 + .../resources/gs/bigbluebutton/gs_stage1.json | 213 ++++++ .../resources/gs/bigbluebutton/gs_stage2.json | 65 ++ .../resources/gs/bigbluebutton/gs_stage3.json | 168 ++++ .../test/resources/gs/mediastore/diagram.json | 1 + .../resources/gs/mediastore/gs_stage1.json | 194 +++++ .../resources/gs/mediastore/gs_stage2.json | 45 ++ .../resources/gs/mediastore/gs_stage3.json | 98 +++ .../gs/teammates_architecture/diagram.json | 1 + .../gs/teammates_architecture/gs_stage1.json | 137 ++++ .../gs/teammates_architecture/gs_stage2.json | 45 ++ .../gs/teammates_architecture/gs_stage3.json | 125 +++ .../gs/teammates_packages/diagram.json | 1 + .../gs/teammates_packages/gs_stage1.json | 259 +++++++ .../gs/teammates_packages/gs_stage2.json | 117 +++ .../gs/teammates_packages/gs_stage3.json | 516 +++++++++++++ .../gs/teammates_packages/gs_stage3_old.json | 306 ++++++++ .../resources/gs/teammates_ui/diagram.json | 1 + .../resources/gs/teammates_ui/gs_stage1.json | 106 +++ .../resources/gs/teammates_ui/gs_stage2.json | 56 ++ .../resources/gs/teammates_ui/gs_stage3.json | 117 +++ .../gs/teammates_ui/gs_stage3_old.json | 131 ++++ .../test/resources/gs/teastore/diagram.json | 1 + .../test/resources/gs/teastore/gs_stage1.json | 103 +++ .../test/resources/gs/teastore/gs_stage2.json | 29 + .../test/resources/gs/teastore/gs_stage3.json | 123 +++ .../mcse/ardoco/lissa/DiagramRecognition.kt | 6 +- .../informants/DockerInformant.kt | 14 +- .../diagramrecognition/model/DiagramImpl.kt | 4 + .../generators/code/java/JavaModel.java | 30 +- .../test/resources/interface/edu/AClass.java | 17 +- stages/pom.xml | 4 +- .../core/tests/eval/EvaluationMetrics.java | 2 +- 138 files changed, 12167 insertions(+), 18 deletions(-) create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramMatchingModelSelectionState.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramModelInconsistencyState.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramModelLinkState.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramState.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/DiagramUtility.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/Edge.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/ElementRole.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/Extractions.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/JsonMapping.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/Label.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/TextSimilarity.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/Transformations.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/Vertex.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/WeightedTextSimilarity.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/HierarchyInconsistency.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/Inconsistency.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/InconsistencyType.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/MissingBoxInconsistency.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/MissingLineInconsistency.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/NameInconsistency.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/UnexpectedBoxInconsistency.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/UnexpectedLineInconsistency.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/refinement/Casing.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/refinement/Group.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/refinement/LineInversion.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/refinement/NameExtension.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/refinement/Swap.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/rules/AllBoxesMustBeLinked.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/rules/AllModelEntitiesMustBeRepresented.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/rules/BoxesMustBeInParent.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/rules/EntitiesMustBeConnectedExactlyToDependencies.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/rules/PackagesMustContainAllSubpackagesIfOneIsEmpty.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/rules/Rule.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/inconsistencies/rules/SameNameForLinkedElements.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/similarityflooding/FixpointFormula.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/similarityflooding/LabeledEdge.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/similarityflooding/MatchingFilter.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/similarityflooding/OrderedMatchingFilter.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/similarityflooding/PropagationCoefficientFormula.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/similarityflooding/SimilarityFloodingAlgorithm.java create mode 100644 framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/similarityflooding/SimilarityMapping.java create mode 100644 stages/diagram-consistency/pom.xml create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramConsistency.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramInconsistencyStateImpl.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramMatchingModelSelectionStateImpl.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramModelLinkStateImpl.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramStateImpl.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramModelInconsistencyAgent.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramModelMatchingAgent.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramModelSelectionAgent.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramProviderAgent.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramElementOccurrenceFinderInformant.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramModelInconsistencyInformant.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramModelLinkInformant.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramProviderInformant.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/InconsistencyGroupingInformant.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/InconsistencyRefinementInformant.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/OccurrenceToDecisionInformant.java create mode 100644 stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/WeightedSimilarityInformant.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/TextSimilarityTest.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/similarityflooding/SimilarityFloodingAlgorithmTest.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/DiagramLoadingTest.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/EvaluationTestBase.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/IntegerMetrics.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/MapMetrics.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/Metrics.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/MetricsStats.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/SyntheticDiagramMatchingEvaluationTest.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/SyntheticDiagramsTest.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/data/AnnotatedDiagram.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/data/AnnotatedGraph.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/data/DiagramProject.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/data/stage1/Element.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/data/stage1/ElementIdentification.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/data/stage1/Occurrence.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/data/stage2/ElementLinks.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/data/stage2/Link.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/data/stage3/DiagramInconsistencies.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/data/stage3/DiagramInconsistency.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/refactoring/AppendSuffix.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/refactoring/Connect.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/refactoring/Create.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/refactoring/Delete.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/refactoring/Disconnect.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/refactoring/Mixed.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/refactoring/Move.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/refactoring/PackageSelection.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/refactoring/PartialSelection.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/refactoring/Refactoring.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/refactoring/RefactoringBundle.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/evaluation/refactoring/Rename.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramConsistencyTest.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/ExaminationDescription.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/ExaminationDescriptionBuilder.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/GeneralModelType.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/Stage1SyntheticDiagramTest.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/Stage2SyntheticDiagramTest.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/Stage3SyntheticDiagramTest.java create mode 100644 stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/SyntheticTestBase.java create mode 100644 stages/diagram-consistency/src/test/resources/.gitignore create mode 100644 stages/diagram-consistency/src/test/resources/gs/bigbluebutton/diagram.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/bigbluebutton/gs_stage1.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/bigbluebutton/gs_stage2.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/bigbluebutton/gs_stage3.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/mediastore/diagram.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/mediastore/gs_stage1.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/mediastore/gs_stage2.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/mediastore/gs_stage3.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teammates_architecture/diagram.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teammates_architecture/gs_stage1.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teammates_architecture/gs_stage2.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teammates_architecture/gs_stage3.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teammates_packages/diagram.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teammates_packages/gs_stage1.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teammates_packages/gs_stage2.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teammates_packages/gs_stage3.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teammates_packages/gs_stage3_old.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teammates_ui/diagram.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teammates_ui/gs_stage1.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teammates_ui/gs_stage2.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teammates_ui/gs_stage3.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teammates_ui/gs_stage3_old.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teastore/diagram.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teastore/gs_stage1.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teastore/gs_stage2.json create mode 100644 stages/diagram-consistency/src/test/resources/gs/teastore/gs_stage3.json diff --git a/framework/common/pom.xml b/framework/common/pom.xml index 4c2947fbe..d7951af7a 100644 --- a/framework/common/pom.xml +++ b/framework/common/pom.xml @@ -45,6 +45,11 @@ org.eclipse.jgit org.eclipse.jgit + + org.jgrapht + jgrapht-core + ${jgrapht.version} + org.junit.jupiter junit-jupiter-engine diff --git a/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramMatchingModelSelectionState.java b/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramMatchingModelSelectionState.java new file mode 100644 index 000000000..d3f26c23e --- /dev/null +++ b/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramMatchingModelSelectionState.java @@ -0,0 +1,123 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.ElementRole; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.WeightedTextSimilarity; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelType; +import edu.kit.kastel.mcse.ardoco.core.architecture.Deterministic; +import edu.kit.kastel.mcse.ardoco.core.data.PipelineStepData; + +/** + * Stores data created during the model selection process, as well as the selection itself. The data includes a created + * text similarity function and all occurrences of diagram elements in models. + */ +@Deterministic +public interface DiagramMatchingModelSelectionState extends PipelineStepData { + /** + * The ID of this state. + */ + String ID = "DiagramMatchingModelSelectionState"; + + /** + * Gets the model types that are in principle available, meaning their loading is attempted. + * + * @return The model types. + */ + Set getAvailableModelTypes(); + + /** + * Sets the model types that are in principle available, meaning their loading is attempted. + * + * @param availableModelTypes + * The model types. + */ + void setAvailableModelTypes(Set availableModelTypes); + + /** + * Gets the similarity function. + * + * @return The similarity function. + */ + WeightedTextSimilarity getSimilarityFunction(); + + /** + * Sets the similarity function. + * + * @param similarity + * The similarity function. + */ + void setSimilarityFunction(WeightedTextSimilarity similarity); + + /** + * Gets the model type that is selected to be matched with the diagram. + * + * @return The model type. + */ + Set getSelection(); + + /** + * Sets the models that are selected to be matched with the diagram. + * + * @param modelTypes + * The model types. + */ + void setSelection(Set modelTypes); + + /** + * Adds an occurrence of a diagram element in a model. + * + * @param diagramID The ID of the diagram element. + * @param modelType The model the model element is in. + * @param modelID The ID of the model element. + * @param role The role of the model element. + */ + void addOccurrence(String diagramID, ModelType modelType, String modelID, ElementRole role); + + /** + * Get all occurrences of a diagram element in a model. + * + * @param diagramID The ID of the diagram element. + * @param modelType The model to get the occurrences in. + * @return The occurrences. + */ + List getOccurrences(String diagramID, ModelType modelType); + + /** + * Get all occurrences of a diagram element in all models. + * + * @param diagramID + * The ID of the diagram element. + * @return The occurrences. + */ + List getOccurrences(String diagramID); + + /** + * Gets the explanation why the model type was selected. + * + * @return The explanation, which is a match value for each model type. + */ + Map getSelectionExplanation(); + + /** + * Sets the explanation why the model type was selected. + * + * @param explanation + * The explanation, which is a match value for each model type. + */ + void setSelectionExplanation(Map explanation); + + /** + * Describes an occurrence of a diagram element in a model. + * + * @param modelID + * The ID of the model element. + * @param role + * The role of the model element. + */ + public record Occurrence(String modelID, ElementRole role) { + } +} diff --git a/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramModelInconsistencyState.java b/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramModelInconsistencyState.java new file mode 100644 index 000000000..229005521 --- /dev/null +++ b/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramModelInconsistencyState.java @@ -0,0 +1,56 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency; + +import java.util.List; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.Inconsistency; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelType; +import edu.kit.kastel.mcse.ardoco.core.data.PipelineStepData; + +/** + * Contains information about found inconsistencies between the diagram and the given models. + */ +public interface DiagramModelInconsistencyState extends PipelineStepData { + /** + * The ID of this state. + */ + String ID = "DiagramModelInconsistencyState"; + + /** + * Adds an inconsistency. + * + * @param modelType The model type to add the inconsistency for. + * @param inconsistency + * The inconsistency to add. + */ + void addInconsistency(ModelType modelType, Inconsistency inconsistency); + + /** + * Returns all found inconsistencies. + * + * @param modelType The model type to get inconsistencies for. + * @return All inconsistencies. + */ + List> getInconsistencies(ModelType modelType); + + /** + * Set the extended inconsistencies. The extended inconsistency list is based on the basic inconsistency list but a + * larger selection of more concrete inconsistency types can be used. + * + * @param modelType + * The model type to set the inconsistencies for. + * @param inconsistencies + * The inconsistencies to set. + */ + void setExtendedInconsistencies(ModelType modelType, List> inconsistencies); + + /** + * Returns the extended inconsistencies. If no extended inconsistencies are set, the basic inconsistencies are + * returned. + * + * @param modelType + * The model type to get the inconsistencies for. + * @return The extended inconsistencies. + */ + List> getExtendedInconsistencies(ModelType modelType); +} diff --git a/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramModelLinkState.java b/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramModelLinkState.java new file mode 100644 index 000000000..e4f4ced87 --- /dev/null +++ b/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramModelLinkState.java @@ -0,0 +1,38 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency; + +import org.eclipse.collections.api.bimap.MutableBiMap; + +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelType; +import edu.kit.kastel.mcse.ardoco.core.data.PipelineStepData; + +/** + * Stores all results of the matching process. + */ +public interface DiagramModelLinkState extends PipelineStepData { + /** + * The ID of this state. + */ + String ID = "DiagramModelLinkState"; + + /** + * Adds a link between a diagram element and a model element. + * + * @param modelType + * The model type of the model in which the model element is located. + * @param diagramID + * The ID of the diagram element. + * @param modelID + * The ID of the model element. + */ + void addLink(ModelType modelType, String diagramID, String modelID); + + /** + * Get all currently stored links between the diagram and a model. + * + * @param modelType + * The type of the model. + * @return The links. + */ + MutableBiMap getLinks(ModelType modelType); +} diff --git a/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramState.java b/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramState.java new file mode 100644 index 000000000..41eafae42 --- /dev/null +++ b/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/DiagramState.java @@ -0,0 +1,30 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramrecognition.Diagram; +import edu.kit.kastel.mcse.ardoco.core.data.PipelineStepData; + +/** + * Contains the loaded diagram. + */ +public interface DiagramState extends PipelineStepData { + /** + * The ID in the data repository. + */ + String ID = "DiagramStateData"; + + /** + * Returns the diagram. + * + * @return The diagram. May be null. + */ + Diagram getDiagram(); + + /** + * Sets the diagram. Overwrites the old diagram. + * + * @param diagram + * The diagram. + */ + void setDiagram(Diagram diagram); +} diff --git a/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/DiagramUtility.java b/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/DiagramUtility.java new file mode 100644 index 000000000..3aea758ab --- /dev/null +++ b/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/DiagramUtility.java @@ -0,0 +1,127 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common; + +import java.util.List; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.UUID; +import java.util.stream.Collectors; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramrecognition.Box; +import edu.kit.kastel.mcse.ardoco.core.api.diagramrecognition.Connector; +import edu.kit.kastel.mcse.ardoco.core.api.diagramrecognition.Diagram; +import edu.kit.kastel.mcse.ardoco.core.api.diagramrecognition.TextBox; +import edu.kit.kastel.mcse.ardoco.core.architecture.Deterministic; + +/** + * This class contains utility methods to use with the diagram interface. + */ +@Deterministic +public class DiagramUtility { + private DiagramUtility() { + } + + /** + * Checks if there is a connection between the two boxes. + * + * @param diagram The diagram in which the boxes are located. + * @param source The source box. + * @param target The target box. + * @return True if there is a connection between the two boxes, false otherwise. + */ + public static boolean hasConnectionBetween(Diagram diagram, Box source, Box target) { + return diagram.getConnectors().stream().anyMatch(connector -> isConnectionBetween(connector, source, target)); + } + + /** + * Checks if the connector connects the two boxes. + * + * @param connector The connector to check. + * @param source The source box. + * @param target The target box. + * @return True if the connector connects the two boxes, false otherwise. + */ + public static boolean isConnectionBetween(Connector connector, Box source, Box target) { + List connectedBoxes = connector.getConnectedBoxes(); + return connectedBoxes.get(0).equals(source.getUUID()) && connectedBoxes.contains(target.getUUID()); + } + + /** + * Returns all connectors that are outgoing from the box. + * + * @param diagram The diagram in which the box is located. + * @param box The box. + * @return All connectors that are outgoing from the box. + */ + public static List getOutgoingConnectors(Diagram diagram, Box box) { + return diagram.getConnectors().stream().filter(connector -> connector.getConnectedBoxes().get(0).equals(box.getUUID())).toList(); + } + + /** + * Get a map of all boxes in the diagram. + * + * @param diagram The diagram. + * @return A map from the UUID of the box to the box. + */ + public static SortedMap getBoxes(Diagram diagram) { + return diagram.getBoxes().stream().collect(Collectors.toMap(Box::getUUID, box -> box, (a, b) -> b, TreeMap::new)); + } + + /** + * Get the targets of the connector. + * + * @param connector The connector. + * @param boxes A UUID-box map. + * @return The targets of the connector. + */ + public static List getTargets(Connector connector, SortedMap boxes) { + return connector.getConnectedBoxes().stream().skip(1).map(boxes::get).toList(); + } + + /** + * Get the text of the box. + * + * @param box The box. + * @return The text of the box. + */ + public static String getBoxText(Box box) { + return box.getTexts().stream().map(TextBox::getText).collect(Collectors.joining(" ")); + } + + /** + * Get the contained boxes of the box. + * + * @param box The box. + * @param boxes A UUID-box map. + * @return The contained boxes of the box. + */ + public static List getContainedBoxes(Box box, SortedMap boxes) { + return box.getContainedBoxes().stream().map(boxes::get).toList(); + } + + /** + * Add a box to the diagram. + * + * @param diagram The diagram. + * @param text The text of the box. + * @return The added box. + */ + public static Box addBox(Diagram diagram, String text) { + TextBox textBox = new TextBox(0, 0, 0, 0, 1.0, text, null); + Box box = new Box(String.valueOf(diagram.getBoxes().size()), new int[] { 0, 0, 0, 0 }, 1.0, null, List.of(textBox), null); + + diagram.addBox(box); + return box; + } + + /** + * Add a connector between the two boxes. + * + * @param diagram The diagram in which the boxes are located. + * @param source The source box. + * @param target The target box. + */ + public static void addConnector(Diagram diagram, Box source, Box target) { + diagram.addConnector(new Connector(UUID.randomUUID().toString(), List.of(source.getUUID(), target.getUUID()), List.of())); + } +} diff --git a/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/Edge.java b/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/Edge.java new file mode 100644 index 000000000..d6b7545a3 --- /dev/null +++ b/framework/common/src/main/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/Edge.java @@ -0,0 +1,37 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common; + +import org.jgrapht.graph.DefaultEdge; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.similarityflooding.LabeledEdge; + +/** + * The edge type used in the graphs that are matched. + */ +public class Edge extends DefaultEdge implements LabeledEdge + + io.github.ardoco.core + diagram-consistency + ${revision} + compile + io.github.ardoco.core diagram-recognition diff --git a/stages/diagram-consistency/pom.xml b/stages/diagram-consistency/pom.xml new file mode 100644 index 000000000..44b0174fa --- /dev/null +++ b/stages/diagram-consistency/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + io.github.ardoco.core + stages + ${revision} + + + diagram-consistency + + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + io.github.ardoco.core + common + ${revision} + compile + + + io.github.ardoco.core + diagram-recognition + ${revision} + + + io.github.ardoco.core + model-provider + ${revision} + compile + + + io.github.ardoco.core + pipeline-core + ${revision} + compile + + + io.github.ardoco.core + tests-base + ${revision} + test + + + org.jgrapht + jgrapht-core + ${jgrapht.version} + + + org.jgrapht + jgrapht-ext + ${jgrapht.version} + + + org.junit.jupiter + junit-jupiter-params + test + + + org.slf4j + slf4j-simple + test + + + diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramConsistency.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramConsistency.java new file mode 100644 index 000000000..784a1bfa6 --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramConsistency.java @@ -0,0 +1,83 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency; + +import java.io.File; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramrecognition.Diagram; +import edu.kit.kastel.mcse.ardoco.core.api.models.ArchitectureModelType; +import edu.kit.kastel.mcse.ardoco.core.api.models.CodeModelType; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelType; +import edu.kit.kastel.mcse.ardoco.core.data.DataRepository; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.agents.DiagramModelInconsistencyAgent; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.agents.DiagramModelMatchingAgent; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.agents.DiagramModelSelectionAgent; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.agents.DiagramProviderAgent; +import edu.kit.kastel.mcse.ardoco.core.execution.ArDoCo; +import edu.kit.kastel.mcse.ardoco.core.execution.runner.ArDoCoRunner; +import edu.kit.kastel.mcse.ardoco.core.models.ArCoTLModelProviderAgent; + +/** + * A runner that checks the consistency of a diagram and the represented architecture/code model. + */ +public class DiagramConsistency extends ArDoCoRunner { + private static final ArchitectureModelType ARCHITECTURE_MODEL_TYPE = ArchitectureModelType.UML; + private static final Set AVAILABLE_MODEL_TYPES = new LinkedHashSet<>(List.of(ARCHITECTURE_MODEL_TYPE, CodeModelType.CODE_MODEL)); + + /** + * Creates a new runner. + * + * @param projectName + * The name of the project that is analysed. + */ + public DiagramConsistency(String projectName) { + super(projectName); + } + + /** + * Accesses the data repository. + * + * @return The data repository. + */ + public DataRepository getDataRepository() { + return this.getArDoCo().getDataRepository(); + } + + /** + * Sets up the pipeline. + * + * @param inputArchitectureModel + * The architecture model file, as defined by ArCoTL. + * @param inputCodeModel + * The code model file, as defined by ArCoTL. + * @param inputDiagram + * The diagram, following the format defined by {@link Diagram}. + * @param outputDirectory + * The output directory. + * @param config + * The configuration. + */ + public void setUp(File inputArchitectureModel, File inputCodeModel, File inputDiagram, File outputDirectory, SortedMap config) { + this.definePipeline(inputArchitectureModel, inputCodeModel, inputDiagram); + this.setOutputDirectory(outputDirectory); + this.getArDoCo().applyConfiguration(config); + this.isSetUp = true; + } + + private void definePipeline(File inputArchitectureModel, File inputCodeModel, File inputDiagram) { + ArDoCo arDoCo = this.getArDoCo(); + DataRepository dataRepository = arDoCo.getDataRepository(); + + arDoCo.addPipelineStep(ArCoTLModelProviderAgent.get(inputArchitectureModel, ARCHITECTURE_MODEL_TYPE, inputCodeModel, new TreeMap<>(), dataRepository)); + + arDoCo.addPipelineStep(DiagramProviderAgent.get(inputDiagram, dataRepository)); + + arDoCo.addPipelineStep(DiagramModelSelectionAgent.get(AVAILABLE_MODEL_TYPES, dataRepository)); + arDoCo.addPipelineStep(DiagramModelMatchingAgent.get(dataRepository)); + arDoCo.addPipelineStep(DiagramModelInconsistencyAgent.get(dataRepository)); + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramInconsistencyStateImpl.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramInconsistencyStateImpl.java new file mode 100644 index 000000000..4a786da37 --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramInconsistencyStateImpl.java @@ -0,0 +1,39 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramModelInconsistencyState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.Inconsistency; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelType; + +/** + * Implementation of {@link DiagramModelInconsistencyState}. + */ +public class DiagramInconsistencyStateImpl implements DiagramModelInconsistencyState { + private final transient Map>> inconsistencies = new LinkedHashMap<>(); + private final transient Map>> extendedInconsistencies = new LinkedHashMap<>(); + + @Override + public void addInconsistency(ModelType modelType, Inconsistency inconsistency) { + this.inconsistencies.computeIfAbsent(modelType, k -> new ArrayList<>()).add(inconsistency); + } + + @Override + public List> getInconsistencies(ModelType modelType) { + return this.inconsistencies.getOrDefault(modelType, List.of()); + } + + @Override + public void setExtendedInconsistencies(ModelType modelType, List> inconsistencies) { + this.extendedInconsistencies.put(modelType, inconsistencies); + } + + @Override + public List> getExtendedInconsistencies(ModelType modelType) { + return this.extendedInconsistencies.getOrDefault(modelType, this.getInconsistencies(modelType)); + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramMatchingModelSelectionStateImpl.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramMatchingModelSelectionStateImpl.java new file mode 100644 index 000000000..fd58f5312 --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramMatchingModelSelectionStateImpl.java @@ -0,0 +1,91 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency; + +import java.util.*; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramMatchingModelSelectionState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.ElementRole; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.WeightedTextSimilarity; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelType; + +/** + * Implementation of {@link DiagramMatchingModelSelectionState}. + */ +public class DiagramMatchingModelSelectionStateImpl implements DiagramMatchingModelSelectionState { + + private final Map>> occurrences = new LinkedHashMap<>(); + private transient Set availableModelTypes = null; + private transient Set selectedModelTypes = null; + private transient Map explanation = null; + private transient WeightedTextSimilarity weightedTextSimilarity; + + @Override + public Set getAvailableModelTypes() { + return this.availableModelTypes; + } + + @Override + public void setAvailableModelTypes(Set availableModelTypes) { + this.availableModelTypes = Collections.unmodifiableSet(new LinkedHashSet<>(availableModelTypes)); + } + + @Override + public WeightedTextSimilarity getSimilarityFunction() { + return this.weightedTextSimilarity; + } + + @Override + public void setSimilarityFunction(WeightedTextSimilarity similarity) { + this.weightedTextSimilarity = similarity; + } + + @Override + public Set getSelection() { + return this.selectedModelTypes; + } + + @Override + public void setSelection(Set modelTypes) { + this.selectedModelTypes = Collections.unmodifiableSet(new LinkedHashSet<>(modelTypes)); + } + + @Override + public void addOccurrence(String diagramID, ModelType modelType, String modelID, ElementRole role) { + this.occurrences.computeIfAbsent(modelType, k -> new TreeMap<>()).computeIfAbsent(diagramID, k -> new ArrayList<>()).add(new Occurrence(modelID, role)); + } + + @Override + public List getOccurrences(String diagramID, ModelType modelType) { + var occurrencesPerBox = this.occurrences.get(modelType); + + if (occurrencesPerBox == null) { + return List.of(); + } + + var requestedOccurrences = occurrencesPerBox.get(diagramID); + + return Objects.requireNonNullElse(requestedOccurrences, List.of()); + } + + @Override + public List getOccurrences(String diagramID) { + var allOccurrences = new ArrayList(); + for (var occurrencesPerBox : this.occurrences.values()) { + var requestedOccurrences = occurrencesPerBox.get(diagramID); + if (requestedOccurrences != null) { + allOccurrences.addAll(requestedOccurrences); + } + } + return allOccurrences; + } + + @Override + public Map getSelectionExplanation() { + return this.explanation; + } + + @Override + public void setSelectionExplanation(Map explanation) { + this.explanation = explanation; + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramModelLinkStateImpl.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramModelLinkStateImpl.java new file mode 100644 index 000000000..de28f2693 --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramModelLinkStateImpl.java @@ -0,0 +1,40 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency; + +import java.util.Map; + +import org.eclipse.collections.api.bimap.MutableBiMap; +import org.eclipse.collections.impl.bimap.mutable.HashBiMap; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramModelLinkState; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelType; + +/** + * Implementation of {@link DiagramModelLinkState}. + */ +public class DiagramModelLinkStateImpl implements DiagramModelLinkState { + + private final Map> links = new HashBiMap<>(); + + @Override + public void addLink(ModelType modelType, String diagramID, String modelID) { + this.links.computeIfAbsent(modelType.getModelId(), k -> new HashBiMap<>()).put(diagramID, modelID); + } + + @Override + public MutableBiMap getLinks(ModelType modelType) { + return this.links.getOrDefault(modelType.getModelId(), new HashBiMap<>()); + } + + /** + * Set the links for a given model. + * + * @param modelType + * The model type to set the links for. + * @param links + * The links to set. Will overwrite existing links. + */ + public void setLinks(ModelType modelType, MutableBiMap links) { + this.links.put(modelType.getModelId(), links); + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramStateImpl.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramStateImpl.java new file mode 100644 index 000000000..febd64f8d --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/DiagramStateImpl.java @@ -0,0 +1,23 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramrecognition.Diagram; + +/** + * Implementation of {@link DiagramState}. + */ +public class DiagramStateImpl implements DiagramState { + + private transient Diagram diagram = null; + + @Override + public Diagram getDiagram() { + return this.diagram; + } + + @Override + public void setDiagram(Diagram diagram) { + this.diagram = diagram; + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramModelInconsistencyAgent.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramModelInconsistencyAgent.java new file mode 100644 index 000000000..96e4cc654 --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramModelInconsistencyAgent.java @@ -0,0 +1,51 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency.agents; + +import java.util.List; +import java.util.Optional; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramModelInconsistencyState; +import edu.kit.kastel.mcse.ardoco.core.data.DataRepository; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.DiagramInconsistencyStateImpl; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants.DiagramModelInconsistencyInformant; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants.InconsistencyGroupingInformant; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants.InconsistencyRefinementInformant; +import edu.kit.kastel.mcse.ardoco.core.pipeline.agent.PipelineAgent; + +/** + * This agent is responsible for finding inconsistencies between the diagram and the given models. + */ +public class DiagramModelInconsistencyAgent extends PipelineAgent { + /** + * Creates a new DiagramModelInconsistencyAgent. + * + * @param data + * The DataRepository. + */ + public DiagramModelInconsistencyAgent(DataRepository data) { + super(List.of(new DiagramModelInconsistencyInformant(data), new InconsistencyRefinementInformant(data), new InconsistencyGroupingInformant(data)), + DiagramModelInconsistencyAgent.class.getSimpleName(), data); + } + + /** + * Creates a new DiagramModelInconsistencyAgent. + * + * @param dataRepository + * The DataRepository. + * @return The DiagramModelInconsistencyAgent. + */ + public static DiagramModelInconsistencyAgent get(DataRepository dataRepository) { + return new DiagramModelInconsistencyAgent(dataRepository); + } + + @Override + protected void initializeState() { + DataRepository data = this.getDataRepository(); + Optional optionalDiagramModelInconsistencyState = data.getData(DiagramModelInconsistencyState.ID, + DiagramModelInconsistencyState.class); + DiagramModelInconsistencyState state = optionalDiagramModelInconsistencyState.orElseGet(DiagramInconsistencyStateImpl::new); + if (optionalDiagramModelInconsistencyState.isEmpty()) { + data.addData(DiagramModelInconsistencyState.ID, state); + } + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramModelMatchingAgent.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramModelMatchingAgent.java new file mode 100644 index 000000000..0829080c3 --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramModelMatchingAgent.java @@ -0,0 +1,48 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency.agents; + +import java.util.List; +import java.util.Optional; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramModelLinkState; +import edu.kit.kastel.mcse.ardoco.core.data.DataRepository; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.DiagramModelLinkStateImpl; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants.DiagramModelLinkInformant; +import edu.kit.kastel.mcse.ardoco.core.pipeline.agent.PipelineAgent; + +/** + * The diagram model matching stage. + */ +public class DiagramModelMatchingAgent extends PipelineAgent { + /** + * Creates a new DiagramModelMatchingAgent. + * + * @param data + * The DataRepository. + */ + public DiagramModelMatchingAgent(DataRepository data) { + super(List.of(new DiagramModelLinkInformant(data)), DiagramModelMatchingAgent.class.getSimpleName(), data); + } + + /** + * Creates a new DiagramModelMatchingAgent. + * + * @param data + * The DataRepository. + * @return The DiagramModelMatchingAgent. + */ + public static DiagramModelMatchingAgent get(DataRepository data) { + return new DiagramModelMatchingAgent(data); + } + + @Override + protected void initializeState() { + DataRepository data = this.getDataRepository(); + Optional optionalDiagramModelLinkState = data.getData(DiagramModelLinkState.ID, DiagramModelLinkState.class); + DiagramModelLinkState state = optionalDiagramModelLinkState.orElseGet(DiagramModelLinkStateImpl::new); + + if (optionalDiagramModelLinkState.isEmpty()) { + data.addData(DiagramModelLinkState.ID, state); + } + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramModelSelectionAgent.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramModelSelectionAgent.java new file mode 100644 index 000000000..912d9d077 --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramModelSelectionAgent.java @@ -0,0 +1,66 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency.agents; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramMatchingModelSelectionState; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelType; +import edu.kit.kastel.mcse.ardoco.core.data.DataRepository; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.DiagramMatchingModelSelectionStateImpl; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants.DiagramElementOccurrenceFinderInformant; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants.OccurrenceToDecisionInformant; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants.WeightedSimilarityInformant; +import edu.kit.kastel.mcse.ardoco.core.pipeline.agent.PipelineAgent; + +/** + * Selects the model a diagram probably represents. + */ +public class DiagramModelSelectionAgent extends PipelineAgent { + private final Set availableModelTypes; + + /** + * Creates a new DiagramModelSelectionAgent. + * + * @param availableModelTypes + * All model types that are in principle available, meaning their loading is attempted. + * @param dataRepository + * The DataRepository. + */ + public DiagramModelSelectionAgent(Set availableModelTypes, DataRepository dataRepository) { + super(List.of(new WeightedSimilarityInformant(dataRepository), new DiagramElementOccurrenceFinderInformant(dataRepository), + new OccurrenceToDecisionInformant(dataRepository)), DiagramModelSelectionAgent.class.getSimpleName(), dataRepository); + + this.availableModelTypes = availableModelTypes; + } + + /** + * Creates a new DiagramModelSelectionAgent. + * + * @param availableModelTypes + * All model types that are in principle available, meaning their loading is attempted. + * @param dataRepository + * The DataRepository. + * @return The DiagramModelSelectionAgent. + */ + public static DiagramModelSelectionAgent get(Set availableModelTypes, DataRepository dataRepository) { + return new DiagramModelSelectionAgent(availableModelTypes, dataRepository); + } + + @Override + protected void initializeState() { + DataRepository data = this.getDataRepository(); + + Optional optionalModelSelectionState = data.getData(DiagramMatchingModelSelectionState.ID, + DiagramMatchingModelSelectionState.class); + + DiagramMatchingModelSelectionState state = optionalModelSelectionState.orElseGet(DiagramMatchingModelSelectionStateImpl::new); + + state.setAvailableModelTypes(this.availableModelTypes); + + if (optionalModelSelectionState.isEmpty()) { + data.addData(DiagramMatchingModelSelectionState.ID, state); + } + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramProviderAgent.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramProviderAgent.java new file mode 100644 index 000000000..22566d876 --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/agents/DiagramProviderAgent.java @@ -0,0 +1,39 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency.agents; + +import java.io.File; +import java.util.List; + +import edu.kit.kastel.mcse.ardoco.core.data.DataRepository; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants.DiagramProviderInformant; +import edu.kit.kastel.mcse.ardoco.core.pipeline.agent.PipelineAgent; + +/** + * Agent that provides a diagram. + */ +public class DiagramProviderAgent extends PipelineAgent { + /** + * Creates a new DiagramProviderAgent. + * + * @param data + * The DataRepository. + * @param diagramFile + * The diagram file. + */ + public DiagramProviderAgent(DataRepository data, File diagramFile) { + super(List.of(new DiagramProviderInformant(data, diagramFile)), DiagramProviderAgent.class.getSimpleName(), data); + } + + /** + * Creates a new DiagramProviderAgent that will load the diagram from the given file. + * + * @param diagramFile + * The diagram file. + * @param dataRepository + * The DataRepository. + * @return The DiagramProviderAgent. + */ + public static DiagramProviderAgent get(File diagramFile, DataRepository dataRepository) { + return new DiagramProviderAgent(dataRepository, diagramFile); + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramElementOccurrenceFinderInformant.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramElementOccurrenceFinderInformant.java new file mode 100644 index 000000000..d2c0632d2 --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramElementOccurrenceFinderInformant.java @@ -0,0 +1,161 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants; + +import java.util.Set; +import java.util.SortedMap; +import java.util.function.BiFunction; +import java.util.function.Function; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramMatchingModelSelectionState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.DiagramUtility; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.ElementRole; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.Extractions; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.TextSimilarity; +import edu.kit.kastel.mcse.ardoco.core.api.diagramrecognition.Diagram; +import edu.kit.kastel.mcse.ardoco.core.api.models.Entity; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelStates; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelType; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.ArchitectureModel; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.CodeModel; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.Model; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.architecture.ArchitectureItem; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.code.CodeItem; +import edu.kit.kastel.mcse.ardoco.core.configuration.Configurable; +import edu.kit.kastel.mcse.ardoco.core.data.DataRepository; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.DiagramStateImpl; +import edu.kit.kastel.mcse.ardoco.core.pipeline.agent.Informant; + +/** + * Finds occurrences of diagram elements in the models. + */ +public class DiagramElementOccurrenceFinderInformant extends Informant { + /** + * The default threshold for the levenshtein similarity used for architecture models. + */ + /*package-private*/ static final double DEFAULT_SIMILARITY_THRESHOLD_ARCHITECTURE = 0.6; + /** + * The default threshold for the levenshtein similarity used for code models. + */ + /*package-private*/ static final double DEFAULT_SIMILARITY_THRESHOLD_CODE = 0.8; + + @Configurable + private double similarityThresholdArchitecture = DEFAULT_SIMILARITY_THRESHOLD_ARCHITECTURE; + @Configurable + private double similarityThresholdCode = DEFAULT_SIMILARITY_THRESHOLD_CODE; + + @Configurable + private boolean skip = false; + + @Configurable + private TextSimilarityFunction similarityFunction = TextSimilarityFunction.ADAPTED_JACCARD; + + private void findOccurrences(Input input, ElementRole role, double threshold, DiagramMatchingModelSelectionState selection) { + BiFunction similarity = this.getSimilarityFunction(selection); + for (var box : input.diagram().getBoxes()) { + for (var element : input.elements()) { + if (similarity.apply(DiagramUtility.getBoxText(box), element.getName()) > threshold) { + selection.addOccurrence(box.getUUID(), input.modelType(), input.idProvider().apply(element), role); + } + } + } + } + + /** + * Creates a new DiagramElementOccurrenceFinderInformant. + * + * @param dataRepository + * The DataRepository. + */ + public DiagramElementOccurrenceFinderInformant(DataRepository dataRepository) { + super(DiagramElementOccurrenceFinderInformant.class.getSimpleName(), dataRepository); + } + + @Override + public void run() { + if (this.skip) { + return; + } + + DataRepository data = this.getDataRepository(); + + ModelStates models = data.getData(ModelStates.ID, ModelStates.class).orElse(null); + DiagramState diagram = data.getData(DiagramState.ID, DiagramStateImpl.class).orElse(null); + DiagramMatchingModelSelectionState selection = data.getData(DiagramMatchingModelSelectionState.ID, DiagramMatchingModelSelectionState.class) + .orElse(null); + + if (models == null || diagram == null || selection == null) { + this.logger.error("DiagramElementOccurrenceFinderInformant: Could not find all required data."); + return; + } + + this.findOccurrencesInModels(diagram, models, selection); + } + + private void findOccurrencesInModels(DiagramState diagramState, ModelStates models, DiagramMatchingModelSelectionState selection) { + for (var modelType : selection.getAvailableModelTypes()) { + Model model = models.getModel(modelType.getModelId()); + if (model instanceof ArchitectureModel architectureModel) { + for (var entry : Extractions.extractItemsFromModel(architectureModel).entrySet()) { + this.findOccurrencesInArchitecture(architectureModel, modelType, diagramState.getDiagram(), entry.getKey(), entry.getValue(), selection); + } + } else if (model instanceof CodeModel codeModel) { + for (var entry : Extractions.extractItemsFromModel(codeModel).entrySet()) { + this.findOccurrencesInCode(codeModel, modelType, diagramState.getDiagram(), entry.getKey(), entry.getValue(), selection); + } + } else { + throw new IllegalArgumentException("Unexpected model type: " + modelType); + } + } + } + + private void findOccurrencesInArchitecture(Model model, ModelType modelType, Diagram diagram, ElementRole role, Set elements, + DiagramMatchingModelSelectionState selection) { + this.findOccurrences(new Input<>(model, modelType, diagram, elements, ArchitectureItem::getId), role, this.similarityThresholdArchitecture, selection); + } + + private void findOccurrencesInCode(Model model, ModelType modelType, Diagram diagram, ElementRole role, Set elements, + DiagramMatchingModelSelectionState selection) { + this.findOccurrences(new Input<>(model, modelType, diagram, elements, Extractions::getPath), role, this.similarityThresholdCode, selection); + } + + private BiFunction getSimilarityFunction(DiagramMatchingModelSelectionState selection) { + return switch (this.similarityFunction) { + case LEVENSHTEIN -> TextSimilarity::byLevenshteinCaseInsensitive; + case JARO_WINKLER -> TextSimilarity::byJaroWinkler; + case JACCARD -> TextSimilarity::byJaccard; + case ADAPTED_JACCARD -> (a, b) -> selection.getSimilarityFunction().apply(a, b); + }; + } + + /** + * The available text similarity functions. + */ + public enum TextSimilarityFunction { + /** + * Uses the levenshtein distance to calculate the similarity. + */ + LEVENSHTEIN, + /** + * Uses the jaro winkler distance to calculate the similarity. + */ + JARO_WINKLER, + /** + * Uses the jaccard similarity to calculate the similarity. + */ + JACCARD, + /** + * Uses the custom and adapted jaccard similarity to calculate the similarity. + */ + ADAPTED_JACCARD + } + + private record Input(Model model, ModelType modelType, Diagram diagram, Set elements, Function idProvider) { + + } + + @Override + protected void delegateApplyConfigurationToInternalObjects(SortedMap additionalConfiguration) { + // Intentionally left empty. + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramModelInconsistencyInformant.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramModelInconsistencyInformant.java new file mode 100644 index 000000000..af25c0e23 --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramModelInconsistencyInformant.java @@ -0,0 +1,170 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.SortedMap; +import java.util.stream.Collectors; + +import org.eclipse.collections.api.bimap.MutableBiMap; +import org.eclipse.collections.impl.bimap.mutable.HashBiMap; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramMatchingModelSelectionState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramModelInconsistencyState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramModelLinkState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.DiagramUtility; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.Extractions; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.Inconsistency; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.rules.AllBoxesMustBeLinked; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.rules.AllModelEntitiesMustBeRepresented; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.rules.BoxesMustBeInParent; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.rules.EntitiesMustBeConnectedExactlyToDependencies; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.rules.PackagesMustContainAllSubpackagesIfOneIsEmpty; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.rules.Rule; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.rules.SameNameForLinkedElements; +import edu.kit.kastel.mcse.ardoco.core.api.diagramrecognition.Box; +import edu.kit.kastel.mcse.ardoco.core.api.models.Entity; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelStates; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelType; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.ArchitectureModel; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.CodeModel; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.Model; +import edu.kit.kastel.mcse.ardoco.core.data.DataRepository; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.DiagramMatchingModelSelectionStateImpl; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.DiagramStateImpl; +import edu.kit.kastel.mcse.ardoco.core.pipeline.agent.Informant; + +/** + * Searches for inconsistencies between the diagram and the given models. + */ +public class DiagramModelInconsistencyInformant extends Informant { + private static final List BASIC_CONSISTENCY_RULES = List.of(new SameNameForLinkedElements(), new AllBoxesMustBeLinked(), + new EntitiesMustBeConnectedExactlyToDependencies(), new BoxesMustBeInParent()); + private static final List ARCHITECTURE_CONSISTENCY_RULES = addBasicConsistencyRules(List.of(new AllModelEntitiesMustBeRepresented())); + private static final List CODE_CONSISTENCY_RULES = addBasicConsistencyRules(List.of(new PackagesMustContainAllSubpackagesIfOneIsEmpty())); + + /** + * Creates a new DiagramModelInconsistencyInformant. + * + * @param data + * The DataRepository. + */ + public DiagramModelInconsistencyInformant(DataRepository data) { + super(DiagramModelInconsistencyInformant.class.getSimpleName(), data); + } + + private static List addBasicConsistencyRules(List rules) { + List combined = new ArrayList<>(); + combined.addAll(rules); + combined.addAll(BASIC_CONSISTENCY_RULES); + return combined; + } + + private static MutableBiMap getTranslatedLinks(MutableBiMap links, Map boxes, Map entities) { + MutableBiMap translatedLinks = new HashBiMap<>(); + for (var link : links.entrySet()) { + Box box = boxes.get(link.getKey()); + Entity entity = entities.get(link.getValue()); + translatedLinks.put(Objects.requireNonNull(box), Objects.requireNonNull(entity)); + } + return translatedLinks; + } + + private static void checkRulesForLinkedElements(MutableBiMap links, Map boxes, Map entities, Context context) { + for (var link : links.entrySet()) { + Box box = boxes.remove(link.getKey()); + Entity entity = entities.remove(link.getValue()); + + for (Rule rule : context.rules()) { + for (var inconsistency : rule.check(box, entity)) { + context.addInconsistency(inconsistency); + } + } + } + } + + private static void checkRulesForLooseBoxes(Map boxes, Context context) { + for (var box : boxes.values()) { + for (Rule rule : context.rules()) { + for (var inconsistency : rule.check(box, null)) { + context.addInconsistency(inconsistency); + } + } + } + } + + private static void checkRulesForLooseEntities(Map entities, Context context) { + for (var entity : entities.values()) { + for (Rule rule : context.rules()) { + for (var inconsistency : rule.check(null, entity)) { + context.addInconsistency(inconsistency); + } + } + } + } + + @Override + public void run() { + DataRepository data = this.getDataRepository(); + + ModelStates models = data.getData(ModelStates.ID, ModelStates.class).orElse(null); + DiagramState diagram = data.getData(DiagramState.ID, DiagramStateImpl.class).orElse(null); + DiagramMatchingModelSelectionState selection = data.getData(DiagramMatchingModelSelectionState.ID, DiagramMatchingModelSelectionStateImpl.class) + .orElse(null); + DiagramModelLinkState matching = data.getData(DiagramModelLinkState.ID, DiagramModelLinkState.class).orElse(null); + DiagramModelInconsistencyState inconsistencies = data.getData(DiagramModelInconsistencyState.ID, DiagramModelInconsistencyState.class).orElse(null); + + if (models == null || diagram == null || selection == null || matching == null || inconsistencies == null) { + this.logger.error("DiagramModelInconsistencyInformant: Could not find all required data."); + return; + } + + for (var selectedModelType : selection.getSelection()) { + Model model = models.getModel(selectedModelType.getModelId()); + + Map entities; + List rules; + + if (model instanceof ArchitectureModel architectureModel) { + entities = Extractions.extractEntitiesFromModel(architectureModel); + rules = ARCHITECTURE_CONSISTENCY_RULES; + } else if (model instanceof CodeModel codeModel) { + entities = Extractions.extractEntitiesFromModel(codeModel); + rules = CODE_CONSISTENCY_RULES; + } else { + this.logger.error("DiagramModelLinkInformant: Unknown model type: {}", model.getClass().getSimpleName()); + continue; + } + + Map entityToId = entities.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); + + Map boxes = DiagramUtility.getBoxes(diagram.getDiagram()); + + MutableBiMap links = matching.getLinks(selectedModelType); + MutableBiMap translatedLinks = getTranslatedLinks(links, boxes, entities); + + rules.forEach(rule -> rule.setup(diagram.getDiagram(), model, translatedLinks)); + + Context context = new Context(selectedModelType, entityToId, rules, inconsistencies); + checkRulesForLinkedElements(links, boxes, entities, context); + checkRulesForLooseBoxes(boxes, context); + checkRulesForLooseEntities(entities, context); + + rules.forEach(Rule::tearDown); + } + } + + private record Context(ModelType modelType, Map entityToId, List rules, DiagramModelInconsistencyState inconsistencies) { + public void addInconsistency(Inconsistency inconsistency) { + this.inconsistencies.addInconsistency(this.modelType, inconsistency.map(Box::getUUID, this.entityToId::get)); + } + } + + @Override + protected void delegateApplyConfigurationToInternalObjects(SortedMap additionalConfiguration) { + // Intentionally left empty. + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramModelLinkInformant.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramModelLinkInformant.java new file mode 100644 index 000000000..8398e43fc --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramModelLinkInformant.java @@ -0,0 +1,156 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants; + +import java.util.Map; +import java.util.SortedMap; +import java.util.function.Function; + +import org.eclipse.collections.api.bimap.MutableBiMap; +import org.jgrapht.graph.DirectedMultigraph; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramMatchingModelSelectionState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramModelLinkState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.Edge; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.Extractions; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.Label; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.Transformations; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.Vertex; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.WeightedTextSimilarity; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.similarityflooding.FixpointFormula; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.similarityflooding.OrderedMatchingFilter; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.similarityflooding.PropagationCoefficientFormula; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.similarityflooding.SimilarityFloodingAlgorithm; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.similarityflooding.SimilarityMapping; +import edu.kit.kastel.mcse.ardoco.core.api.diagramrecognition.Box; +import edu.kit.kastel.mcse.ardoco.core.api.diagramrecognition.Diagram; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelStates; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelType; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.ArchitectureModel; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.CodeModel; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.Model; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.architecture.ArchitectureItem; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.code.CodeItem; +import edu.kit.kastel.mcse.ardoco.core.configuration.Configurable; +import edu.kit.kastel.mcse.ardoco.core.data.DataRepository; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.DiagramMatchingModelSelectionStateImpl; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.DiagramStateImpl; +import edu.kit.kastel.mcse.ardoco.core.pipeline.agent.Informant; + +/** + * Searches for links between the model and the selected diagram. + */ +public class DiagramModelLinkInformant extends Informant { + /** + * The default epsilon stop threshold for the similarity flooding algorithm. + */ + /*package-private*/ static final double DEFAULT_EPSILON = 1.0; + /** + * The default maximum number of iterations for the similarity flooding algorithm. + */ + /*package-private*/ static final int DEFAULT_MAX_ITERATIONS = 100; + /** + * The default threshold for the levenshtein similarity, under which the similarity is considered to be zero. + */ + /*package-private*/ static final double DEFAULT_TEXT_SIMILARITY_THRESHOLD = 0.68; + /** + * The default threshold for the similarity, under which no link is created. + */ + /*package-private*/ static final double DEFAULT_SIMILARITY_THRESHOLD = 0.06; + @Configurable + private double epsilon = DEFAULT_EPSILON; + @Configurable + private int maxIterations = DEFAULT_MAX_ITERATIONS; + @Configurable + private double textSimilarityThreshold = DEFAULT_TEXT_SIMILARITY_THRESHOLD; + @Configurable + private double similarityThreshold = DEFAULT_SIMILARITY_THRESHOLD; + @Configurable + private boolean skip = false; + + /** + * Creates a new DiagramModelLinkInformant. + * + * @param data + * The DataRepository. + */ + public DiagramModelLinkInformant(DataRepository data) { + super(DiagramModelLinkInformant.class.getSimpleName(), data); + } + + @Override + public void run() { + if (this.skip) { + return; + } + + DataRepository data = this.getDataRepository(); + + ModelStates models = data.getData(ModelStates.ID, ModelStates.class).orElse(null); + DiagramState diagram = data.getData(DiagramState.ID, DiagramStateImpl.class).orElse(null); + DiagramMatchingModelSelectionState selection = data.getData(DiagramMatchingModelSelectionState.ID, DiagramMatchingModelSelectionStateImpl.class) + .orElse(null); + DiagramModelLinkState matching = data.getData(DiagramModelLinkState.ID, DiagramModelLinkState.class).orElse(null); + + if (models == null || diagram == null || selection == null || matching == null) { + this.logger.error("DiagramModelLinkInformant: Could not find all required data."); + return; + } + + for (var selectedModelType : selection.getSelection()) { + Model model = models.getModel(selectedModelType.getModelId()); + + if (model instanceof ArchitectureModel architectureModel) { + this.match(diagram.getDiagram(), architectureModel, selectedModelType, matching, selection.getSimilarityFunction()); + } else if (model instanceof CodeModel codeModel) { + this.match(diagram.getDiagram(), codeModel, selectedModelType, matching, selection.getSimilarityFunction()); + } else { + this.logger.error("DiagramModelLinkInformant: Unknown model type: {}", model.getClass().getSimpleName()); + } + } + } + + private void match(Diagram diagram, ArchitectureModel model, ModelType type, DiagramModelLinkState matching, WeightedTextSimilarity similarityFunction) { + DirectedMultigraph, Edge> diagramAsGraph = Transformations.toGraph(diagram); + DirectedMultigraph, Edge> modelAsGraph = Transformations.toGraph(model); + + this.match(type, matching, diagramAsGraph, modelAsGraph, ArchitectureItem::getId, similarityFunction); + } + + private void match(Diagram diagram, CodeModel model, ModelType type, DiagramModelLinkState matching, WeightedTextSimilarity similarityFunction) { + DirectedMultigraph, Edge> diagramAsGraph = Transformations.toGraph(diagram); + DirectedMultigraph, Edge> modelAsGraph = Transformations.toGraph(model); + + this.match(type, matching, diagramAsGraph, modelAsGraph, Extractions::getPath, similarityFunction); + } + + private void match(ModelType modelType, DiagramModelLinkState matching, DirectedMultigraph, Edge> diagramAsGraph, + DirectedMultigraph, Edge> modelAsGraph, Function idExtractor, WeightedTextSimilarity similarityFunction) { + SimilarityFloodingAlgorithm, Vertex, Label> algorithm = new SimilarityFloodingAlgorithm<>(this.epsilon, this.maxIterations, + PropagationCoefficientFormula.getInverseAverageFormula(), FixpointFormula.getCFormula()); + + SimilarityMapping, Vertex> initialSimilarity = new SimilarityMapping<>(pair -> { + double similarity = similarityFunction.apply(pair.getFirst().getName(), pair.getSecond().getName()); + return similarity < this.textSimilarityThreshold ? 0.0 : similarity; + }); + initialSimilarity.prepareCartesian(diagramAsGraph.vertexSet(), modelAsGraph.vertexSet()); + + SimilarityMapping, Vertex> mapping = algorithm.match(diagramAsGraph, modelAsGraph, initialSimilarity); + + OrderedMatchingFilter, Vertex> filter = new OrderedMatchingFilter<>(this.similarityThreshold, this.textSimilarityThreshold); + MutableBiMap, Vertex> filteredMapping = filter.filter(mapping, initialSimilarity); + for (Map.Entry, Vertex> entry : filteredMapping.entrySet()) { + Box box = entry.getKey().getRepresented(); + M item = entry.getValue().getRepresented(); + + if (box != null && item != null) { + matching.addLink(modelType, box.getUUID(), idExtractor.apply(item)); + } + } + } + + @Override + protected void delegateApplyConfigurationToInternalObjects(SortedMap additionalConfiguration) { + // Intentionally left empty. + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramProviderInformant.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramProviderInformant.java new file mode 100644 index 000000000..6bd0d7d6f --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/DiagramProviderInformant.java @@ -0,0 +1,91 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Optional; +import java.util.SortedMap; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.JsonMapping; +import edu.kit.kastel.mcse.ardoco.core.api.diagramrecognition.Diagram; +import edu.kit.kastel.mcse.ardoco.core.data.DataRepository; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.DiagramStateImpl; +import edu.kit.kastel.mcse.ardoco.core.pipeline.agent.Informant; +import edu.kit.kastel.mcse.ardoco.lissa.diagramrecognition.model.DiagramImpl; + +/** + * Loads a diagram from a file. + * The file must follow the JSON format of the {@link Diagram} class. + */ +public class DiagramProviderInformant extends Informant { + private final File diagramFile; + + /** + * Creates a new DiagramProviderInformant. + * + * @param data + * The DataRepository. + * @param diagramFile + * The file from which the diagram is loaded. + */ + public DiagramProviderInformant(DataRepository data, File diagramFile) { + super(DiagramProviderInformant.class.getSimpleName(), data); + this.diagramFile = diagramFile; + } + + /** + * Loads a diagram from a file. + * + * @param file + * The file from which the diagram is loaded. + * @return The loaded diagram. + * @throws IOException + * If the file could not be read. + */ + public static Diagram load(File file) throws IOException { + Diagram diagram; + + try (InputStream stream = new FileInputStream(file)) { + String text = new String(stream.readAllBytes(), StandardCharsets.UTF_8); + diagram = JsonMapping.OBJECT_MAPPER.readValue(text, DiagramImpl.class); + } + + return diagram; + } + + @Override + public void run() { + Diagram diagram = null; + + try { + diagram = load(this.diagramFile); + } catch (IOException e) { + this.logger.error("Could not read file " + this.diagramFile, e); + } + + if (diagram != null) { + this.addDiagramToState(diagram); + } + } + + private void addDiagramToState(Diagram diagram) { + DataRepository data = this.getDataRepository(); + Optional optionalDiagramState = data.getData(DiagramState.ID, DiagramState.class); + DiagramState state = optionalDiagramState.orElseGet(DiagramStateImpl::new); + + state.setDiagram(diagram); + + if (optionalDiagramState.isEmpty()) { + data.addData(DiagramState.ID, state); + } + } + + @Override + protected void delegateApplyConfigurationToInternalObjects(SortedMap additionalConfiguration) { + // Intentionally left empty. + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/InconsistencyGroupingInformant.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/InconsistencyGroupingInformant.java new file mode 100644 index 000000000..157509a4b --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/InconsistencyGroupingInformant.java @@ -0,0 +1,76 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramMatchingModelSelectionState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramModelInconsistencyState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.Inconsistency; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.refinement.Group; +import edu.kit.kastel.mcse.ardoco.core.data.DataRepository; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.DiagramMatchingModelSelectionStateImpl; +import edu.kit.kastel.mcse.ardoco.core.pipeline.agent.Informant; + +/** + * This informant groups inconsistencies into groups of related ones. + */ +public class InconsistencyGroupingInformant extends Informant { + /** + * Creates a new InconsistencyGroupingInformant. + * + * @param data + * The DataRepository. + */ + public InconsistencyGroupingInformant(DataRepository data) { + super(InconsistencyGroupingInformant.class.getSimpleName(), data); + } + + @Override + public void run() { + DataRepository data = this.getDataRepository(); + + DiagramMatchingModelSelectionState selection = data.getData(DiagramMatchingModelSelectionState.ID, DiagramMatchingModelSelectionStateImpl.class) + .orElse(null); + DiagramModelInconsistencyState inconsistencyState = data.getData(DiagramModelInconsistencyState.ID, DiagramModelInconsistencyState.class).orElse(null); + + if (selection == null || inconsistencyState == null) { + this.logger.error("InconsistencyGroupingInformant: Could not find all required data."); + return; + } + + for (var selectedModelType : selection.getSelection()) { + List> inconsistencies = inconsistencyState.getExtendedInconsistencies(selectedModelType); + + Map>> groups = new LinkedHashMap<>(); + List> ungrouped = new ArrayList<>(); + + for (var inconsistency : inconsistencies) { + if (inconsistency.getBox() != null) { + groups.computeIfAbsent(inconsistency.getBox(), k -> new ArrayList<>()).add(inconsistency); + } else { + ungrouped.add(inconsistency); + } + } + + List> newInconsistencies = new ArrayList<>(ungrouped); + for (var group : groups.entrySet()) { + if (group.getValue().size() > 1) { + newInconsistencies.add(new Group<>(group.getKey(), group.getValue())); + } else { + newInconsistencies.add(group.getValue().get(0)); + } + } + + inconsistencyState.setExtendedInconsistencies(selectedModelType, newInconsistencies); + } + } + + @Override + protected void delegateApplyConfigurationToInternalObjects(SortedMap additionalConfiguration) { + // Intentionally left empty. + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/InconsistencyRefinementInformant.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/InconsistencyRefinementInformant.java new file mode 100644 index 000000000..454134377 --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/InconsistencyRefinementInformant.java @@ -0,0 +1,101 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramMatchingModelSelectionState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramModelInconsistencyState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.DiagramUtility; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.Extractions; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.Inconsistency; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.refinement.Casing; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.refinement.LineInversion; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.refinement.NameExtension; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.inconsistencies.refinement.Swap; +import edu.kit.kastel.mcse.ardoco.core.api.diagramrecognition.Box; +import edu.kit.kastel.mcse.ardoco.core.api.diagramrecognition.Diagram; +import edu.kit.kastel.mcse.ardoco.core.api.models.Entity; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelStates; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelType; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.Model; +import edu.kit.kastel.mcse.ardoco.core.data.DataRepository; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.DiagramMatchingModelSelectionStateImpl; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.DiagramStateImpl; +import edu.kit.kastel.mcse.ardoco.core.pipeline.agent.Informant; + +/** + * This informant refines existing inconsistencies into more concrete ones. These can contain multiple original + * inconsistencies of various types. + */ +public class InconsistencyRefinementInformant extends Informant { + /** + * Creates a new InconsistencyRefinementInformant. + * + * @param data + * The DataRepository. + */ + public InconsistencyRefinementInformant(DataRepository data) { + super(InconsistencyRefinementInformant.class.getSimpleName(), data); + } + + private static Map getBoxNames(DiagramState diagramState) { + Diagram diagram = diagramState.getDiagram(); + return diagram.getBoxes().stream().collect(Collectors.toMap(Box::getUUID, DiagramUtility::getBoxText)); + } + + private static Map getEntityNames(ModelStates models, ModelType selectedModelType) { + Model model = models.getModel(selectedModelType.getModelId()); + Map entities = Extractions.extractEntitiesFromModel(model); + + Map entityIdToName = new LinkedHashMap<>(); + for (var entity : entities.entrySet()) { + entityIdToName.put(entity.getKey(), entity.getValue().getName()); + } + + return entityIdToName; + } + + @Override + public void run() { + DataRepository data = this.getDataRepository(); + + ModelStates models = data.getData(ModelStates.ID, ModelStates.class).orElse(null); + DiagramState diagram = data.getData(DiagramState.ID, DiagramStateImpl.class).orElse(null); + DiagramMatchingModelSelectionState selection = data.getData(DiagramMatchingModelSelectionState.ID, DiagramMatchingModelSelectionStateImpl.class) + .orElse(null); + DiagramModelInconsistencyState inconsistencyState = data.getData(DiagramModelInconsistencyState.ID, DiagramModelInconsistencyState.class).orElse(null); + + if (models == null || diagram == null || selection == null || inconsistencyState == null) { + this.logger.error("InconsistencyRefinementInformant: Could not find all required data."); + return; + } + + for (var selectedModelType : selection.getSelection()) { + List> inconsistencies = new ArrayList<>(inconsistencyState.getInconsistencies(selectedModelType)); + + Map boxIdToName = getBoxNames(diagram); + Map entityIdToName = getEntityNames(models, selectedModelType); + + List>>> rules = List.of(LineInversion::discover, list -> NameExtension.discover(list, + boxIdToName::get, entityIdToName::get), Casing::discover, Swap::discover); + + for (var refinementRule : rules) { + inconsistencies = refinementRule.apply(inconsistencies); + } + + inconsistencyState.setExtendedInconsistencies(selectedModelType, inconsistencies); + } + } + + @Override + protected void delegateApplyConfigurationToInternalObjects(SortedMap additionalConfiguration) { + // Intentionally left empty. + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/OccurrenceToDecisionInformant.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/OccurrenceToDecisionInformant.java new file mode 100644 index 000000000..76d3e4364 --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/OccurrenceToDecisionInformant.java @@ -0,0 +1,126 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramMatchingModelSelectionState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramrecognition.Diagram; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelStates; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelType; +import edu.kit.kastel.mcse.ardoco.core.configuration.Configurable; +import edu.kit.kastel.mcse.ardoco.core.data.DataRepository; +import edu.kit.kastel.mcse.ardoco.core.diagramconsistency.DiagramStateImpl; +import edu.kit.kastel.mcse.ardoco.core.pipeline.agent.Informant; + +/** + * Chooses a model based on the found occurrences of diagram elements in the available models. + */ +public class OccurrenceToDecisionInformant extends Informant { + /** + * The required minimum ratio of matching elements to choose a model. + */ + /*package-private*/ static final double DEFAULT_MATCH_THRESHOLD = 0.05; + /** + * The maximum delta of the ratio of two models to choose both models. + */ + /*package-private*/ static final double DEFAULT_MATCH_DELTA = 0.05; + + @Configurable + private double matchThreshold = DEFAULT_MATCH_THRESHOLD; + @Configurable + private double matchDelta = DEFAULT_MATCH_DELTA; + + @Configurable + private boolean skip = false; + + /** + * Creates a new OccurrenceToDecisionInformant. + * + * @param dataRepository + * The DataRepository. + */ + public OccurrenceToDecisionInformant(DataRepository dataRepository) { + super(OccurrenceToDecisionInformant.class.getSimpleName(), dataRepository); + } + + @Override + public void run() { + if (this.skip) { + return; + } + + DataRepository data = this.getDataRepository(); + + ModelStates models = data.getData(ModelStates.ID, ModelStates.class).orElse(null); + DiagramState diagram = data.getData(DiagramState.ID, DiagramStateImpl.class).orElse(null); + DiagramMatchingModelSelectionState selection = data.getData(DiagramMatchingModelSelectionState.ID, DiagramMatchingModelSelectionState.class) + .orElse(null); + + if (models == null || diagram == null || selection == null) { + this.logger.error("OccurrenceToDecisionInformant: Could not find all required data."); + return; + } + + Map ratios = this.calculateRatios(diagram, selection); + Set selectedModelTypes = this.selectModels(ratios); + + selection.setSelection(selectedModelTypes); + selection.setSelectionExplanation(ratios); + } + + private Map calculateRatios(DiagramState diagramState, DiagramMatchingModelSelectionState selection) { + Map ratios = new LinkedHashMap<>(); + + for (var modelType : selection.getAvailableModelTypes()) { + double ratio = this.calculateRatioInModel(diagramState.getDiagram(), modelType, selection); + ratios.put(modelType, ratio); + } + + return ratios; + } + + private Set selectModels(Map ratios) { + ModelType highestRatioModelType = null; + double highestRatio = Double.MIN_VALUE; + + for (var entry : ratios.entrySet()) { + if (highestRatioModelType == null || entry.getValue() > highestRatio) { + highestRatioModelType = entry.getKey(); + highestRatio = entry.getValue(); + } + } + + Set selectedModelTypes = new LinkedHashSet<>(); + + for (var entry : ratios.entrySet()) { + double delta = Math.abs(entry.getValue() - highestRatio); + if (entry.getValue() >= this.matchThreshold && delta < this.matchDelta) { + selectedModelTypes.add(entry.getKey()); + } + } + + return selectedModelTypes; + } + + private double calculateRatioInModel(Diagram diagram, ModelType modelType, DiagramMatchingModelSelectionState selection) { + int numberOfElementsWithOccurrences = 0; + + for (var box : diagram.getBoxes()) { + if (!selection.getOccurrences(box.getUUID(), modelType).isEmpty()) { + numberOfElementsWithOccurrences++; + } + } + + return (double) numberOfElementsWithOccurrences / (double) diagram.getBoxes().size(); + } + + @Override + protected void delegateApplyConfigurationToInternalObjects(SortedMap additionalConfiguration) { + // Intentionally left empty. + } +} diff --git a/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/WeightedSimilarityInformant.java b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/WeightedSimilarityInformant.java new file mode 100644 index 000000000..5f86e08d1 --- /dev/null +++ b/stages/diagram-consistency/src/main/java/edu/kit/kastel/mcse/ardoco/core/diagramconsistency/informants/WeightedSimilarityInformant.java @@ -0,0 +1,108 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.diagramconsistency.informants; + +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.DiagramMatchingModelSelectionState; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.ElementRole; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.Extractions; +import edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.WeightedTextSimilarity; +import edu.kit.kastel.mcse.ardoco.core.api.models.Entity; +import edu.kit.kastel.mcse.ardoco.core.api.models.ModelStates; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.ArchitectureModel; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.CodeModel; +import edu.kit.kastel.mcse.ardoco.core.api.models.arcotl.Model; +import edu.kit.kastel.mcse.ardoco.core.configuration.Configurable; +import edu.kit.kastel.mcse.ardoco.core.data.DataRepository; +import edu.kit.kastel.mcse.ardoco.core.pipeline.agent.Informant; + +/** + * Provides a weighted similarity function based on the word count in element names. + */ +public class WeightedSimilarityInformant extends Informant { + @Configurable + private double minWeight = 0.2; + @Configurable + private boolean skip = false; + + /** + * Creates a new WeightedSimilarityInformant. + * + * @param dataRepository + * The DataRepository. + */ + public WeightedSimilarityInformant(DataRepository dataRepository) { + super(WeightedSimilarityInformant.class.getSimpleName(), dataRepository); + } + + @Override + public void run() { + if (this.skip) { + return; + } + + DataRepository data = this.getDataRepository(); + + ModelStates models = data.getData(ModelStates.ID, ModelStates.class).orElse(null); + DiagramMatchingModelSelectionState selection = data.getData(DiagramMatchingModelSelectionState.ID, DiagramMatchingModelSelectionState.class) + .orElse(null); + + if (models == null || selection == null) { + this.logger.error("WeightedSimilarityInformant: Could not find all required data."); + return; + } + + Map wordCounts = this.countWordsInModels(models, selection); + + WeightedTextSimilarity similarity = new WeightedTextSimilarity(this.calculateWeights(wordCounts)); + selection.setSimilarityFunction(similarity); + } + + private Map countWordsInModels(ModelStates models, DiagramMatchingModelSelectionState selection) { + Map wordCounts = new TreeMap<>(); + + for (var modelType : selection.getAvailableModelTypes()) { + Model model = models.getModel(modelType.getModelId()); + if (model instanceof ArchitectureModel architectureModel) { + this.countWords(Extractions.extractItemsFromModel(architectureModel), wordCounts); + } else if (model instanceof CodeModel codeModel) { + this.countWords(Extractions.extractItemsFromModel(codeModel), wordCounts); + } else { + throw new IllegalArgumentException("Unexpected model type: " + modelType); + } + } + + return wordCounts; + } + + private void countWords(Map> elements, Map wordCounts) { + for (var entry : elements.entrySet()) { + for (Entity element : entry.getValue()) { + WeightedTextSimilarity.getWords(element.getName()).forEach(word -> wordCounts.merge(word, 1, Integer::sum)); + } + } + } + + private Map calculateWeights(Map wordCounts) { + double max = wordCounts.values().stream().mapToInt(Integer::intValue).max().orElse(0); + Map weights = new TreeMap<>(); + + double weightRange = 1.0 - this.minWeight; + + for (var entry : wordCounts.entrySet()) { + double commonness = 1.0 - entry.getValue() / max; + double weight = this.minWeight + weightRange * commonness; + weights.put(entry.getKey(), weight); + } + + return weights; + } + + @Override + protected void delegateApplyConfigurationToInternalObjects(SortedMap additionalConfiguration) { + // Intentionally left empty. + } +} diff --git a/stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/TextSimilarityTest.java b/stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/TextSimilarityTest.java new file mode 100644 index 000000000..4017bb5e4 --- /dev/null +++ b/stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/TextSimilarityTest.java @@ -0,0 +1,20 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class TextSimilarityTest { + @Test + void byLevenshtein() { + assertEquals(1.0, TextSimilarity.byLevenshtein("a", "a")); + assertEquals(0.0, TextSimilarity.byLevenshtein("x", "y")); + } + + @Test + void byJaccard() { + assertEquals(1.0, TextSimilarity.byJaccard("a", "a")); + assertEquals(0.0, TextSimilarity.byJaccard("x", "y")); + } +} diff --git a/stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/similarityflooding/SimilarityFloodingAlgorithmTest.java b/stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/similarityflooding/SimilarityFloodingAlgorithmTest.java new file mode 100644 index 000000000..3b73f64ea --- /dev/null +++ b/stages/diagram-consistency/src/test/java/edu/kit/kastel/mcse/ardoco/core/api/diagramconsistency/common/similarityflooding/SimilarityFloodingAlgorithmTest.java @@ -0,0 +1,136 @@ +/* Licensed under MIT 2023. */ +package edu.kit.kastel.mcse.ardoco.core.api.diagramconsistency.common.similarityflooding; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.eclipse.collections.api.bimap.MutableBiMap; +import org.jgrapht.alg.util.Pair; +import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.DirectedMultigraph; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class SimilarityFloodingAlgorithmTest { + private static final double DELTA = 0.1; + + @DisplayName("Match with the basic example") + @Test + void basicExample() { + // The example graphs and similarity values are taken from the work describing the similarity flooding algorithm. + // There, images of the graphs and the steps of the algorithm can be found. + // S. Melnik, H. Garcia-Molina, and E. Rahm, ‘Similarity flooding: a versatile graph matching algorithm and its application to schema matching’, in Proceedings 18th International Conference on Data Engineering, Feb. 2002, pp. 117–128. doi: 10.1109/ICDE.2002.994702. + + DirectedMultigraph a = this.buildGraphA(); + DirectedMultigraph b = this.buildGraphB(); + + SimilarityFloodingAlgorithm algorithm = new SimilarityFloodingAlgorithm<>(0.075, 100, PropagationCoefficientFormula + .getInverseProductFormula(), FixpointFormula.getBasicFormula()); + SimilarityMapping mapping = algorithm.match(a, b, new SimilarityMapping<>(1.0)); + + assertEquals(1.00, mapping.getSimilarity(GraphA.A, GraphB.B), DELTA); + assertEquals(0.91, mapping.getSimilarity(GraphA.A2, GraphB.B1), DELTA); + assertEquals(0.69, mapping.getSimilarity(GraphA.A1, GraphB.B2), DELTA); + assertEquals(0.39, mapping.getSimilarity(GraphA.A1, GraphB.B1), DELTA); + assertEquals(0.33, mapping.getSimilarity(GraphA.A1, GraphB.B), DELTA); + assertEquals(0.33, mapping.getSimilarity(GraphA.A2, GraphB.B2), DELTA); + + OrderedMatchingFilter filter = new OrderedMatchingFilter<>(); + MutableBiMap finalMapping = filter.filter(mapping, null); + + assertEquals(3, finalMapping.size()); + assertEquals(GraphB.B, finalMapping.get(GraphA.A)); + assertEquals(GraphB.B1, finalMapping.get(GraphA.A2)); + assertEquals(GraphB.B2, finalMapping.get(GraphA.A1)); + } + + @DisplayName("Match a large graph with itself") + @Test + void largeGraph() { + int size = 500; + DirectedMultigraph a = this.buildLargeGraph(size); + DirectedMultigraph b = this.buildLargeGraph(size); + + SimilarityFloodingAlgorithm algorithm = new SimilarityFloodingAlgorithm<>(0.0, size, PropagationCoefficientFormula + .getInverseProductFormula(), FixpointFormula.getBasicFormula()); + SimilarityMapping initialMapping = new SimilarityMapping<>(0.0); + initialMapping.updateSimilarity(new Pair<>(0, 0), 1.0); + SimilarityMapping mapping = algorithm.match(a, b, initialMapping); + + OrderedMatchingFilter filter = new OrderedMatchingFilter<>(); + MutableBiMap finalMapping = filter.filter(mapping, null); + + assertEquals(size, finalMapping.size()); + for (int i = 0; i < size; i++) { + assertEquals(i, finalMapping.get(i)); + } + } + + private DirectedMultigraph buildGraphA() { + DirectedMultigraph a = new DirectedMultigraph<>(Edge.class); + + a.addVertex(GraphA.A); + a.addVertex(GraphA.A1); + a.addVertex(GraphA.A2); + + a.addEdge(GraphA.A, GraphA.A1, new Edge(Label.L1)); + a.addEdge(GraphA.A, GraphA.A2, new Edge(Label.L1)); + a.addEdge(GraphA.A1, GraphA.A2, new Edge(Label.L2)); + + return a; + } + + private DirectedMultigraph buildGraphB() { + DirectedMultigraph b = new DirectedMultigraph<>(Edge.class); + + b.addVertex(GraphB.B); + b.addVertex(GraphB.B1); + b.addVertex(GraphB.B2); + + b.addEdge(GraphB.B, GraphB.B1, new Edge(Label.L1)); + b.addEdge(GraphB.B, GraphB.B2, new Edge(Label.L2)); // Note the different label of the edge. + b.addEdge(GraphB.B2, GraphB.B1, new Edge(Label.L2)); // Note the different direction of the edge. + + return b; + } + + private DirectedMultigraph buildLargeGraph(int size) { + DirectedMultigraph graph = new DirectedMultigraph<>(Edge.class); + + for (int i = 0; i < size; i++) { + graph.addVertex(i); + } + + for (int i = 0; i < size - 1; i++) { + graph.addEdge(i, i + 1, new Edge(Label.L1)); + + } + + return graph; + } + + enum Label { + L1, L2 + } + + enum GraphA { + A, A1, A2 + } + + enum GraphB { + B, B1, B2 + } + + static final class Edge extends DefaultEdge implements LabeledEdge