From e185c480f7fd3b3f8f8700ce1af7c8bde4b3bd7f Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Tue, 6 Aug 2024 15:18:38 +0200 Subject: [PATCH 01/11] add testutils and views project --- .../.classpath | 16 - .../.project | 34 -- .../org.eclipse.core.resources.prefs | 2 - .../META-INF/MANIFEST.MF | 16 - .../build.properties | 6 - .../plugin.xml | 9 - .../schema/application.exsd | 92 ---- .../applications/VitruvApplication.xtend | 9 - .../VitruvApplicationsRegistry.xtend | 64 --- .../tools.vitruv.framework.views/.classpath | 16 - bundles/tools.vitruv.framework.views/.project | 34 -- .../org.eclipse.core.resources.prefs | 2 - .../META-INF/MANIFEST.MF | 17 - .../build.properties | 5 - .../framework/views/ChangeableViewSource.java | 11 - .../framework/views/CommittableView.java | 41 -- .../views/ModifiableViewSelection.java | 49 -- .../tools/vitruv/framework/views/View.java | 138 ------ .../vitruv/framework/views/ViewProvider.java | 18 - .../vitruv/framework/views/ViewSelection.java | 15 - .../vitruv/framework/views/ViewSelector.java | 34 -- .../vitruv/framework/views/ViewSource.java | 28 -- .../vitruv/framework/views/ViewType.java | 24 - .../framework/views/ViewTypeFactory.xtend | 15 - .../framework/views/ViewTypeProvider.java | 15 - .../framework/views/ViewTypeRepository.xtend | 36 -- ...ltStateBasedChangeResolutionStrategy.xtend | 118 ----- .../StateBasedChangeResolutionStrategy.java | 54 -- .../views/impl/AbstractViewType.xtend | 16 - .../framework/views/impl/BasicView.xtend | 151 ------ .../views/impl/ChangeDerivingView.xtend | 105 ---- .../views/impl/ChangeRecordingView.xtend | 76 --- .../views/impl/IdentityMappingViewType.java | 89 ---- .../framework/views/impl/ModifiableView.xtend | 14 - .../views/impl/ViewCreatingViewType.java | 45 -- .../selection/AbstractViewSelection.java | 55 --- .../views/selection/ElementViewSelection.java | 24 - .../selectors/DirectViewElementSelector.xtend | 74 --- .../tools.vitruv.framework.vsum/.classpath | 16 - bundles/tools.vitruv.framework.vsum/.project | 34 -- .../org.eclipse.core.resources.prefs | 2 - .../META-INF/MANIFEST.MF | 19 - .../build.properties | 5 - .../vitruv/framework/vsum/VirtualModel.xtend | 18 - .../framework/vsum/VirtualModelBuilder.xtend | 107 ---- .../framework/vsum/VirtualModelManager.xtend | 32 -- .../vsum/helper/VsumFileSystemLayout.xtend | 103 ---- .../vsum/internal/InternalVirtualModel.xtend | 15 - .../vsum/internal/ModelInstance.xtend | 65 --- .../vsum/internal/ModelRepository.xtend | 20 - .../vsum/internal/ResourceRepositoryImpl.java | 215 -------- .../vsum/internal/VirtualModelImpl.java | 183 ------- .../vsum/internal/VirtualModelRegistry.java | 30 -- .../tools.vitruv.testutils.vsum/.classpath | 8 - bundles/tools.vitruv.testutils.vsum/.project | 34 -- .../org.eclipse.core.resources.prefs | 2 - .../org.eclipse.xtend.core.Xtend.prefs | 3 - .../META-INF/MANIFEST.MF | 21 - .../build.properties | 5 - .../.project | 17 - .../org.eclipse.core.resources.prefs | 2 - .../build.properties | 2 - .../feature.properties | 7 - .../feature.xml | 59 --- .../.project | 17 - .../org.eclipse.core.resources.prefs | 2 - .../build.properties | 2 - .../feature.properties | 7 - .../feature.xml | 43 -- pom.xml | 320 +++++++++++- releng/tools.vitruv.parent/pom.xml | 90 ---- releng/tools.vitruv.updatesite/.project | 11 - .../org.eclipse.core.resources.prefs | 2 - releng/tools.vitruv.updatesite/category.xml | 20 - releng/tools.vitruv.updatesite/pom.xml | 17 - .../.classpath | 12 - .../.project | 34 -- .../org.eclipse.core.resources.prefs | 2 - .../META-INF/MANIFEST.MF | 25 - .../build.properties | 6 - .../pom.xml | 32 -- .../.classpath | 16 - .../.project | 34 -- .../org.eclipse.core.resources.prefs | 2 - .../META-INF/MANIFEST.MF | 21 - .../build.properties | 7 - .../framework/vsum/VirtualModelTest.xtend | 396 --------------- .../framework/vsum/VirtualModelTestUtil.xtend | 128 ----- testutils/pom.xml | 23 + testutils/vsum/pom.xml | 66 +++ .../testutils/vsum}/TestViewFactory.java | 2 +- .../DefaultVirtualModelBasedTestView.xtend | 2 +- .../vsum}/LegacyCorrespondenceRetriever.xtend | 2 +- .../vsum}/LegacyVitruvApplicationTest.xtend | 2 +- .../ViewBasedVitruvApplicationTest.xtend | 2 +- .../vsum}/VirtualModelBasedTestView.xtend | 2 +- .../vsum}/VitruvApplicationTest.xtend | 2 +- views/pom.xml | 132 +++++ .../views/ViewTypeRepositoryTest.java | 0 .../framework/views/impl/BasicViewTest.java | 0 .../views/impl/ChangeDerivingViewTest.java | 0 .../views/impl/ChangeRecordingViewTest.java | 0 .../impl/IdentityMappingViewTypeTest.java | 0 .../selection/ElementViewSelectionTest.java | 0 .../DirectViewElementSelectorTest.java | 0 .../views/util/XmiIdEdgeCaseTest.java | 0 .../BasicStateChangePropagationTest.xtend | 0 .../EdgeCaseStateChangeTest.xtend | 0 .../changederivation/PcmStateChangeTest.xtend | 0 .../StateChangePropagationTest.xtend | 0 .../changederivation/UmlStateChangeTest.xtend | 0 .../views/ViewTypeRepositoryTest.java | 107 ++++ .../framework/views/impl/BasicViewTest.java | 262 ++++++++++ .../views/impl/ChangeDerivingViewTest.java | 410 ++++++++++++++++ .../views/impl/ChangeRecordingViewTest.java | 442 +++++++++++++++++ .../impl/IdentityMappingViewTypeTest.java | 464 ++++++++++++++++++ .../selection/ElementViewSelectionTest.java | 279 +++++++++++ .../DirectViewElementSelectorTest.java | 165 +++++++ .../views/util/XmiIdEdgeCaseTest.java | 111 +++++ .../BasicStateChangePropagationTest.xtend | 355 ++++++++++++++ .../EdgeCaseStateChangeTest.xtend | 41 ++ .../changederivation/PcmStateChangeTest.xtend | 74 +++ .../StateChangePropagationTest.xtend | 175 +++++++ .../changederivation/UmlStateChangeTest.xtend | 77 +++ 124 files changed, 3491 insertions(+), 3543 deletions(-) delete mode 100644 bundles/tools.vitruv.framework.applications/.classpath delete mode 100644 bundles/tools.vitruv.framework.applications/.project delete mode 100644 bundles/tools.vitruv.framework.applications/.settings/org.eclipse.core.resources.prefs delete mode 100644 bundles/tools.vitruv.framework.applications/META-INF/MANIFEST.MF delete mode 100644 bundles/tools.vitruv.framework.applications/build.properties delete mode 100644 bundles/tools.vitruv.framework.applications/plugin.xml delete mode 100644 bundles/tools.vitruv.framework.applications/schema/application.exsd delete mode 100644 bundles/tools.vitruv.framework.applications/src/tools/vitruv/framework/applications/VitruvApplication.xtend delete mode 100644 bundles/tools.vitruv.framework.applications/src/tools/vitruv/framework/applications/VitruvApplicationsRegistry.xtend delete mode 100644 bundles/tools.vitruv.framework.views/.classpath delete mode 100644 bundles/tools.vitruv.framework.views/.project delete mode 100644 bundles/tools.vitruv.framework.views/.settings/org.eclipse.core.resources.prefs delete mode 100644 bundles/tools.vitruv.framework.views/META-INF/MANIFEST.MF delete mode 100644 bundles/tools.vitruv.framework.views/build.properties delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ChangeableViewSource.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/CommittableView.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ModifiableViewSelection.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/View.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewProvider.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSelection.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSelector.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSource.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewType.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewTypeFactory.xtend delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewTypeProvider.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewTypeRepository.xtend delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/AbstractViewType.xtend delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/BasicView.xtend delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ModifiableView.xtend delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ViewCreatingViewType.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/selection/AbstractViewSelection.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/selection/ElementViewSelection.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/selectors/DirectViewElementSelector.xtend delete mode 100644 bundles/tools.vitruv.framework.vsum/.classpath delete mode 100644 bundles/tools.vitruv.framework.vsum/.project delete mode 100644 bundles/tools.vitruv.framework.vsum/.settings/org.eclipse.core.resources.prefs delete mode 100644 bundles/tools.vitruv.framework.vsum/META-INF/MANIFEST.MF delete mode 100644 bundles/tools.vitruv.framework.vsum/build.properties delete mode 100644 bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/VirtualModel.xtend delete mode 100644 bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/VirtualModelBuilder.xtend delete mode 100644 bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/VirtualModelManager.xtend delete mode 100644 bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend delete mode 100644 bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/InternalVirtualModel.xtend delete mode 100644 bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelInstance.xtend delete mode 100644 bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelRepository.xtend delete mode 100644 bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java delete mode 100644 bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelImpl.java delete mode 100644 bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelRegistry.java delete mode 100644 bundles/tools.vitruv.testutils.vsum/.classpath delete mode 100644 bundles/tools.vitruv.testutils.vsum/.project delete mode 100644 bundles/tools.vitruv.testutils.vsum/.settings/org.eclipse.core.resources.prefs delete mode 100644 bundles/tools.vitruv.testutils.vsum/.settings/org.eclipse.xtend.core.Xtend.prefs delete mode 100644 bundles/tools.vitruv.testutils.vsum/META-INF/MANIFEST.MF delete mode 100644 bundles/tools.vitruv.testutils.vsum/build.properties delete mode 100644 features/tools.vitruv.framework.vsum.feature/.project delete mode 100644 features/tools.vitruv.framework.vsum.feature/.settings/org.eclipse.core.resources.prefs delete mode 100644 features/tools.vitruv.framework.vsum.feature/build.properties delete mode 100644 features/tools.vitruv.framework.vsum.feature/feature.properties delete mode 100644 features/tools.vitruv.framework.vsum.feature/feature.xml delete mode 100644 features/tools.vitruv.testutils.vsum.feature/.project delete mode 100644 features/tools.vitruv.testutils.vsum.feature/.settings/org.eclipse.core.resources.prefs delete mode 100644 features/tools.vitruv.testutils.vsum.feature/build.properties delete mode 100644 features/tools.vitruv.testutils.vsum.feature/feature.properties delete mode 100644 features/tools.vitruv.testutils.vsum.feature/feature.xml delete mode 100644 releng/tools.vitruv.parent/pom.xml delete mode 100644 releng/tools.vitruv.updatesite/.project delete mode 100644 releng/tools.vitruv.updatesite/.settings/org.eclipse.core.resources.prefs delete mode 100644 releng/tools.vitruv.updatesite/category.xml delete mode 100644 releng/tools.vitruv.updatesite/pom.xml delete mode 100644 tests/tools.vitruv.framework.views.tests/.classpath delete mode 100644 tests/tools.vitruv.framework.views.tests/.project delete mode 100644 tests/tools.vitruv.framework.views.tests/.settings/org.eclipse.core.resources.prefs delete mode 100644 tests/tools.vitruv.framework.views.tests/META-INF/MANIFEST.MF delete mode 100644 tests/tools.vitruv.framework.views.tests/build.properties delete mode 100644 tests/tools.vitruv.framework.views.tests/pom.xml delete mode 100644 tests/tools.vitruv.framework.vsum.tests/.classpath delete mode 100644 tests/tools.vitruv.framework.vsum.tests/.project delete mode 100644 tests/tools.vitruv.framework.vsum.tests/.settings/org.eclipse.core.resources.prefs delete mode 100644 tests/tools.vitruv.framework.vsum.tests/META-INF/MANIFEST.MF delete mode 100644 tests/tools.vitruv.framework.vsum.tests/build.properties delete mode 100644 tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend delete mode 100644 tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend create mode 100644 testutils/pom.xml create mode 100644 testutils/vsum/pom.xml rename {bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils => testutils/vsum/src/main/java/tools/vitruv/framework/testutils/vsum}/TestViewFactory.java (98%) rename {bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils => testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum}/DefaultVirtualModelBasedTestView.xtend (97%) rename {bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils => testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum}/LegacyCorrespondenceRetriever.xtend (94%) rename {bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils => testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum}/LegacyVitruvApplicationTest.xtend (98%) rename {bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils => testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum}/ViewBasedVitruvApplicationTest.xtend (98%) rename {bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils => testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum}/VirtualModelBasedTestView.xtend (87%) rename {bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils => testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum}/VitruvApplicationTest.xtend (96%) create mode 100644 views/pom.xml rename {tests/tools.vitruv.framework.views.tests/src => views/src/main/java}/tools/vitruv/framework/views/ViewTypeRepositoryTest.java (100%) rename {tests/tools.vitruv.framework.views.tests/src => views/src/main/java}/tools/vitruv/framework/views/impl/BasicViewTest.java (100%) rename {tests/tools.vitruv.framework.views.tests/src => views/src/main/java}/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java (100%) rename {tests/tools.vitruv.framework.views.tests/src => views/src/main/java}/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java (100%) rename {tests/tools.vitruv.framework.views.tests/src => views/src/main/java}/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java (100%) rename {tests/tools.vitruv.framework.views.tests/src => views/src/main/java}/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java (100%) rename {tests/tools.vitruv.framework.views.tests/src => views/src/main/java}/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java (100%) rename {tests/tools.vitruv.framework.views.tests/src => views/src/main/java}/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java (100%) rename {tests/tools.vitruv.framework.views.tests/src => views/src/main/xtend}/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend (100%) rename {tests/tools.vitruv.framework.views.tests/src => views/src/main/xtend}/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend (100%) rename {tests/tools.vitruv.framework.views.tests/src => views/src/main/xtend}/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend (100%) rename {tests/tools.vitruv.framework.views.tests/src => views/src/main/xtend}/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend (100%) rename {tests/tools.vitruv.framework.views.tests/src => views/src/main/xtend}/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend (100%) create mode 100644 views/src/test/java/tools/vitruv/framework/views/ViewTypeRepositoryTest.java create mode 100644 views/src/test/java/tools/vitruv/framework/views/impl/BasicViewTest.java create mode 100644 views/src/test/java/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java create mode 100644 views/src/test/java/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java create mode 100644 views/src/test/java/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java create mode 100644 views/src/test/java/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java create mode 100644 views/src/test/java/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java create mode 100644 views/src/test/java/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java create mode 100644 views/src/test/xtend/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend create mode 100644 views/src/test/xtend/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend create mode 100644 views/src/test/xtend/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend create mode 100644 views/src/test/xtend/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend create mode 100644 views/src/test/xtend/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend diff --git a/bundles/tools.vitruv.framework.applications/.classpath b/bundles/tools.vitruv.framework.applications/.classpath deleted file mode 100644 index ef548cb968..0000000000 --- a/bundles/tools.vitruv.framework.applications/.classpath +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/bundles/tools.vitruv.framework.applications/.project b/bundles/tools.vitruv.framework.applications/.project deleted file mode 100644 index af1917f015..0000000000 --- a/bundles/tools.vitruv.framework.applications/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - tools.vitruv.framework.applications - - - - - - org.eclipse.xtext.ui.shared.xtextBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - org.eclipse.xtext.ui.shared.xtextNature - - diff --git a/bundles/tools.vitruv.framework.applications/.settings/org.eclipse.core.resources.prefs b/bundles/tools.vitruv.framework.applications/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 99f26c0203..0000000000 --- a/bundles/tools.vitruv.framework.applications/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -encoding/=UTF-8 diff --git a/bundles/tools.vitruv.framework.applications/META-INF/MANIFEST.MF b/bundles/tools.vitruv.framework.applications/META-INF/MANIFEST.MF deleted file mode 100644 index ab5c0a4c41..0000000000 --- a/bundles/tools.vitruv.framework.applications/META-INF/MANIFEST.MF +++ /dev/null @@ -1,16 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Vitruv Framework Applications Specification -Bundle-Vendor: vitruv.tools -Bundle-SymbolicName: tools.vitruv.framework.applications;singleton:=true -Automatic-Module-Name: tools.vitruv.framework.applications -Bundle-Version: 3.0.1.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-17 -Require-Bundle: tools.vitruv.change.propagation;visibility:=reexport, - com.google.guava, - org.apache.log4j, - org.eclipse.xtext.xbase.lib, - org.eclipse.xtend.lib, - org.eclipse.xtend.lib.macro, - org.eclipse.core.runtime -Export-Package: tools.vitruv.framework.applications diff --git a/bundles/tools.vitruv.framework.applications/build.properties b/bundles/tools.vitruv.framework.applications/build.properties deleted file mode 100644 index 9fd62f57d9..0000000000 --- a/bundles/tools.vitruv.framework.applications/build.properties +++ /dev/null @@ -1,6 +0,0 @@ -source.. = src/,\ - xtend-gen/ -output.. = target/classes/ -bin.includes = META-INF/,\ - .,\ - plugin.xml diff --git a/bundles/tools.vitruv.framework.applications/plugin.xml b/bundles/tools.vitruv.framework.applications/plugin.xml deleted file mode 100644 index c7916defc5..0000000000 --- a/bundles/tools.vitruv.framework.applications/plugin.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - diff --git a/bundles/tools.vitruv.framework.applications/schema/application.exsd b/bundles/tools.vitruv.framework.applications/schema/application.exsd deleted file mode 100644 index 112049f85f..0000000000 --- a/bundles/tools.vitruv.framework.applications/schema/application.exsd +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - Extension point for registering a Vitruv application - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A Vitruv application - - - - - - - - - - - - - - - - - - - - - - 0.1.0 - - - - - - - - - - - - Copyright (c) 2014-2017 Vitruv Tools Team\n\ -Karlsruhe Institute of Technology (KIT), Software Design and Quality, Karlsruhe, Germany - - - - diff --git a/bundles/tools.vitruv.framework.applications/src/tools/vitruv/framework/applications/VitruvApplication.xtend b/bundles/tools.vitruv.framework.applications/src/tools/vitruv/framework/applications/VitruvApplication.xtend deleted file mode 100644 index 944d2c9d5b..0000000000 --- a/bundles/tools.vitruv.framework.applications/src/tools/vitruv/framework/applications/VitruvApplication.xtend +++ /dev/null @@ -1,9 +0,0 @@ -package tools.vitruv.framework.applications - -import tools.vitruv.change.propagation.ChangePropagationSpecification -import java.util.Set - -interface VitruvApplication { - def Set getChangePropagationSpecifications(); - def String getName(); -} diff --git a/bundles/tools.vitruv.framework.applications/src/tools/vitruv/framework/applications/VitruvApplicationsRegistry.xtend b/bundles/tools.vitruv.framework.applications/src/tools/vitruv/framework/applications/VitruvApplicationsRegistry.xtend deleted file mode 100644 index 05bfb82e1a..0000000000 --- a/bundles/tools.vitruv.framework.applications/src/tools/vitruv/framework/applications/VitruvApplicationsRegistry.xtend +++ /dev/null @@ -1,64 +0,0 @@ -package tools.vitruv.framework.applications - -import org.eclipse.xtend.lib.annotations.Accessors -import java.util.Collection -import java.util.HashSet -import java.util.Collections -import java.util.List -import org.eclipse.core.runtime.Platform -import org.apache.log4j.Logger - -class VitruvApplicationsRegistry { - public static String EXTENSION_POINT_ID = "tools.vitruv.framework.applications.application" - static Logger LOGGER = Logger.getLogger(VitruvApplicationsRegistry) - - @Accessors(PUBLIC_GETTER) - static VitruvApplicationsRegistry instance = new VitruvApplicationsRegistry - - Collection applications; - boolean initialized; - - private new() { - applications = new HashSet - initialized = false; - } - - /** - * Adds the given application to the registry - * - * @param application - the {@link VitruvApplication} to add, must not be null - */ - def addApplication(VitruvApplication application) { - if (application === null) { - throw new IllegalArgumentException("Application must not be null") - } - applications += application; - } - - def getApplications() { - if (!initialized) { - applications += allApplicationsFromExtensionPoint - initialized = true - } - Collections.unmodifiableCollection(applications); - } - - /** - * Retrieves all applications from the extensions registered to the VitruvApplication - * extension point. - */ - private def static Iterable getAllApplicationsFromExtensionPoint() { - val List applications = newArrayList(); - if (Platform.running) { - Platform.getExtensionRegistry().getConfigurationElementsFor(EXTENSION_POINT_ID).map [ - try { - it.createExecutableExtension("class") - } catch (Exception e) { - LOGGER.warn("Error when loading application for extension " + it) - null; - } - ].filter(VitruvApplication).forEach[applications.add(it)]; - } - return applications; - } -} \ No newline at end of file diff --git a/bundles/tools.vitruv.framework.views/.classpath b/bundles/tools.vitruv.framework.views/.classpath deleted file mode 100644 index ef548cb968..0000000000 --- a/bundles/tools.vitruv.framework.views/.classpath +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/bundles/tools.vitruv.framework.views/.project b/bundles/tools.vitruv.framework.views/.project deleted file mode 100644 index 3ae2797f90..0000000000 --- a/bundles/tools.vitruv.framework.views/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - tools.vitruv.framework.views - - - - - - org.eclipse.xtext.ui.shared.xtextBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - org.eclipse.xtext.ui.shared.xtextNature - - diff --git a/bundles/tools.vitruv.framework.views/.settings/org.eclipse.core.resources.prefs b/bundles/tools.vitruv.framework.views/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 99f26c0203..0000000000 --- a/bundles/tools.vitruv.framework.views/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -encoding/=UTF-8 diff --git a/bundles/tools.vitruv.framework.views/META-INF/MANIFEST.MF b/bundles/tools.vitruv.framework.views/META-INF/MANIFEST.MF deleted file mode 100644 index e96ea43e07..0000000000 --- a/bundles/tools.vitruv.framework.views/META-INF/MANIFEST.MF +++ /dev/null @@ -1,17 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Vitruv Framework Views -Bundle-SymbolicName: tools.vitruv.framework.views;singleton:=true -Automatic-Module-Name: tools.vitruv.framework.views -Bundle-Version: 3.0.1.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-17 -Require-Bundle: org.apache.log4j, - tools.vitruv.change.composite, - org.eclipse.emf.compare, - org.eclipse.emf.ecore.xmi, - org.eclipse.xtend.lib, - edu.kit.ipd.sdq.activextendannotations, - edu.kit.ipd.sdq.commons.util.emf -Export-Package: tools.vitruv.framework.views, - tools.vitruv.framework.views.changederivation -Bundle-Vendor: vitruv.tools diff --git a/bundles/tools.vitruv.framework.views/build.properties b/bundles/tools.vitruv.framework.views/build.properties deleted file mode 100644 index 3c36c1d7e3..0000000000 --- a/bundles/tools.vitruv.framework.views/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -source.. = src/,\ - xtend-gen/ -output.. = target/classes/ -bin.includes = META-INF/,\ - . diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ChangeableViewSource.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ChangeableViewSource.java deleted file mode 100644 index 50b775d192..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ChangeableViewSource.java +++ /dev/null @@ -1,11 +0,0 @@ -package tools.vitruv.framework.views; - -import tools.vitruv.change.composite.propagation.ChangeableModelRepository; - -/** - * A specific {@link ViewSource} that can be changed, such that views are able - * to perform modifications to the underlying models. - */ -public interface ChangeableViewSource extends ViewSource, ChangeableModelRepository { - -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/CommittableView.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/CommittableView.java deleted file mode 100644 index e77aa4c771..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/CommittableView.java +++ /dev/null @@ -1,41 +0,0 @@ -package tools.vitruv.framework.views; - -/** - * A {@link View} that allows to commit its changes back to the underlying {@link ChangeableViewSource}. - */ -public interface CommittableView extends View { - /** - * Commits the changes made to the view and its containing elements to the - * underlying {@link ChangeableViewSource}. This explicitly includes all changes - * that have been made before calling this method. Whether changes will - * effectively be recorded depends on this view. It is permissible for a view - * not to record any changes if it deems them irrelevant. - * - * To ensure that the view is not modified ({@link #isModified()}) afterwards - * because further changes may be performed to the underlying sources during - * commit, consider using {@link #commitChangesAndUpdate()} instead to perform - * an update of the view afterwards. - * - * @throws IllegalStateException if called on a closed view - * @see #isClosed() - * @see #commitChangesAndUpdate() - */ - void commitChanges(); - - /** - * Convenience method for subsequent execution of {@link #commitChanges()} and - * {@link #update()}. Commits the changes made to the view and its containing - * elements to the underlying {@link ChangeableViewSource} and updates the view - * elements from the {@link ChangeableViewSource} afterwards to reflect - * potential further changes made during commit. - * - * @throws IllegalStateException if called on a closed view - * @see #commitChanges() - * @see #update() - * @see #isClosed() - */ - default void commitChangesAndUpdate() { - commitChanges(); - update(); - } -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ModifiableViewSelection.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ModifiableViewSelection.java deleted file mode 100644 index 7d343be0ab..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ModifiableViewSelection.java +++ /dev/null @@ -1,49 +0,0 @@ -package tools.vitruv.framework.views; - -import java.util.Collection; - -import org.eclipse.emf.ecore.EObject; - -/** - * A {@link ViewSelection} that can be changed by performing selections of the - * elements. - */ -public interface ModifiableViewSelection extends ViewSelection { - /** - * Returns the elements that can be selected, i.e., those that may be passed to - * {@link #isSelected(EObject)} and {@link #setSelected(EObject, boolean)}. - * - * @return the selectable {@link EObject}s - */ - Collection getSelectableElements(); - - /** - * Returns whether the given {@link EObject} is selected. Also returns - * false when the given object is null or not - * selectable at all. - * - * @param eObject the {@link EObject} to check the selection state for - * @return whether the given {@link EObject} is selected - */ - boolean isSelected(EObject eObject); - - /** - * Returns whether the given {@link EObject} can be selected. May only be called - * for objects that are selectable (i.e., contained in - * {@link #getSelectableElements()} and throws an exception otherwise. - * - * @param eObject the {@link EObject} to check the selection state for - * @return whether the given {@link EObject} is selected - */ - boolean isSelectable(EObject eObject); - - /** - * Sets the selection state of the given {@link EObject}. May only be called for - * objects that are selectable (i.e., contained in - * {@link #getSelectableElements()} and throws an exception otherwise. - * - * @param eObject the {@link EObject} to set the selection state for - * @param selected whether the given {@link EObject} should be selected or not - */ - void setSelected(EObject eObject, boolean selected); -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/View.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/View.java deleted file mode 100644 index b9807cf392..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/View.java +++ /dev/null @@ -1,138 +0,0 @@ -package tools.vitruv.framework.views; - -import java.util.Collection; - -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EObject; - -import com.google.common.annotations.Beta; -import com.google.common.collect.FluentIterable; - -import tools.vitruv.framework.views.changederivation.DefaultStateBasedChangeResolutionStrategy; -import tools.vitruv.framework.views.changederivation.StateBasedChangeResolutionStrategy; - -/** - * A Vitruv view on an underlying {@link ChangeableViewSource}. - */ -public interface View extends AutoCloseable { - - /** - * Provides the root model elements of this view. - * - * @throws IllegalStateException if called on a closed view. - * @see View#isClosed() - */ - Collection getRootObjects(); - - /** - * Provides all root model elements of this view that conform to a certain type. - * - * @param clazz is requested root element type. - * @throws IllegalStateException if called on a closed view. - * @see View#isClosed() - */ - default Collection getRootObjects(Class clazz) { - return FluentIterable.from(getRootObjects()).filter(clazz).toList(); - }; - - /** - * Returns whether the view was modified. - */ - boolean isModified(); - - /** - * Returns whether the view is outdated, i.e., whether the underlying view - * sources have changed. - */ - boolean isOutdated(); - - /** - * Updates the view from the underlying {@link ViewSource}, thus invalidating - * its previous state and now providing an updated view on the - * {@code ViewSource}. It reuses the {@link ViewSelection} with whom the view - * has been created. This can only be done for an unmodified view. - * - * @throws UnsupportedOperationException if called on a modified view - * @throws IllegalStateException if called on a closed view - * @see #isClosed() - * @see #isModified() - */ - void update(); - - /** - * Checks whether the view was closed. Closed views cannot be used further. All - * methods may thrown an {@link IllegalStateException}. - */ - boolean isClosed(); - - /** - * Persists the given object at the given {@link URI} and adds it as view root. - * The newly registered root will not be present in the view, you have to commit - * the view and checkout a new one (you probably also have to adapt the selection) - * in order to be able to see and modify the new root. - * - * This method is in beta state, as it is still under evaluation whether it is - * sufficient and appropriate for registering root objects in views. - */ - @Beta - void registerRoot(EObject object, URI persistAt); - - /** - * Moves the given object to the given {@link URI}. The given {@link EObject} - * must already be a root object of the view, otherwise an - * {@link IllegalStateException} is thrown. - * - * This method is in beta state, as it is still under evaluation whether it is - * sufficient and appropriate for registering root objects in views. - */ - @Beta - void moveRoot(EObject object, URI newLocation); - - /** - * Returns the {@link ViewSelection} with which this view has been created. - */ - ViewSelection getSelection(); - - /** - * Returns the {@link ViewType} this view conforms to. - */ - ViewType getViewType(); - - /** - * Returns a {@link CommittableView} based on the view's configuration. - * Changes to commit are identified by recording any changes made to the view. - * - * @throws UnsupportedOperationException if called on a modified view - * @throws IllegalStateException if called on a closed view - * @see #isClosed() - * @see #isModified() - */ - CommittableView withChangeRecordingTrait(); - - /** - * Returns a {@link CommittableView} based on the view's configuration. - * Changes to commit are identified by comparing the current view state with its state from the last update. - * - * @param changeResolutionStrategy The change resolution strategy to use for view state comparison. Must not be null. - * @throws UnsupportedOperationException if called on a modified view - * @throws IllegalStateException if called on a closed view - * @see #isClosed() - * @see #isModified() - */ - CommittableView withChangeDerivingTrait(StateBasedChangeResolutionStrategy changeResolutionStrategy); - - - /** - * Returns a {@link CommittableView} based on the view's configuration. - * Changes to commit are identified by comparing the current view state with its state from the last update. - * To compare states the {@link DefaultStateBasedChangeResolutionStrategy} is applied. - * - * @throws UnsupportedOperationException if called on a modified view - * @throws IllegalStateException if called on a closed view - * @see #isClosed() - * @see #isModified() - */ - default CommittableView withChangeDerivingTrait() { - return withChangeDerivingTrait(new DefaultStateBasedChangeResolutionStrategy()); - } -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewProvider.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewProvider.java deleted file mode 100644 index cc970895b3..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewProvider.java +++ /dev/null @@ -1,18 +0,0 @@ -package tools.vitruv.framework.views; - -/** - * A provider for views, i.e. this object contains the source models to create a - * view from and provides according selectors for given view types to - * instantiate a view from. - */ -public interface ViewProvider { - /** - * Returns a view selector for the given {@link ViewType} based on the source - * models covered by this object. - * - * @param viewType the {@link ViewType} to create a selector for - * @returns a {@link ViewSelector} for given view type and the source models - * covered by this object - */ - S createSelector(ViewType viewType); -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSelection.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSelection.java deleted file mode 100644 index b5e443bc25..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSelection.java +++ /dev/null @@ -1,15 +0,0 @@ -package tools.vitruv.framework.views; - -import org.eclipse.emf.ecore.EObject; - -/** - * A representation of the elements selected to be represented in a view. - */ -public interface ViewSelection { - /** - * Returns whether the given element is selected to be represented in a view. - * - * @return whether the given {@link EObject} is to be represented in the view - */ - boolean isViewObjectSelected(EObject eObject); -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSelector.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSelector.java deleted file mode 100644 index dcdc5b0bf3..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSelector.java +++ /dev/null @@ -1,34 +0,0 @@ -package tools.vitruv.framework.views; - -/** - * A selector for selecting the elements to be represented in a view. It - * encapsulates a modifiable {@link ViewSelection}, which it is able to validate - * and which is then passed to a created view. It is capable of acting as a - * builder for a view by providing an appropriate creation method. - */ -public interface ViewSelector extends ModifiableViewSelection { - /** - * Creates a view for the underlying source models and the view type this - * selector has been created for as well as for the selection performed in this - * selector. May only be called if the current selection is valid as returned by - * {@link #isValid()}. - * - * @return the created {@link View} - */ - View createView(); - - /** - * Checks whether the current selection is valid and thus calling - * {@link #createView()} is possible. - * - * @return whether the current selection is valid - */ - boolean isValid(); - - /** - * Returns an immutable copy of the selection resulting from this selector. - * - * @return an immutable copy of the selection in this selector - */ - ViewSelection getSelection(); -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSource.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSource.java deleted file mode 100644 index ba3a389131..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSource.java +++ /dev/null @@ -1,28 +0,0 @@ -package tools.vitruv.framework.views; - -import java.util.Collection; - -import org.eclipse.emf.ecore.resource.Resource; - -import tools.vitruv.change.atomic.uuid.UuidResolver; - -/** - * A view source giving access to the underlying source models of the view. - */ -public interface ViewSource { - /** - * Returns {@link Resource}s representing the source models of a view to be - * instantiated. - * - * @return {@link Resource}s as the sources of a view - */ - Collection getViewSourceModels(); - - /** - * Returns the {@link UuidResolver} associated with the resources in this view - * source. - * - * @return the {@link UuidResolver} of this view source. - */ - public UuidResolver getUuidResolver(); -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewType.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewType.java deleted file mode 100644 index 8ed8df58a4..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewType.java +++ /dev/null @@ -1,24 +0,0 @@ -package tools.vitruv.framework.views; - -/** - * A Vitruv view type on the virtual model, providing a view selector and allows - * creating views. - * - * @param the type of view selector this view type uses - */ -public interface ViewType { - /** - * Returns the name of the view type. - */ - String getName(); - - /** - * Returns the view selector of the view type, which allows configuring views - * based on the given {@link ChangeableViewSource}. - * - * @param viewSource the {@link ChangeableViewSource} from which views shall be - * derived. - * @returns a {@link ViewSelector} for this view type - */ - S createSelector(ChangeableViewSource viewSource); -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewTypeFactory.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewTypeFactory.xtend deleted file mode 100644 index 660479ba36..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewTypeFactory.xtend +++ /dev/null @@ -1,15 +0,0 @@ -package tools.vitruv.framework.views - -import edu.kit.ipd.sdq.activextendannotations.Utility -import tools.vitruv.framework.views.impl.IdentityMappingViewType - -@Utility -class ViewTypeFactory { - /** - * Creates a view type that generates a view via a one-to-one (identity) mapping of - * selected resource root elements in a view source to a {@link View}. - */ - static def ViewType createIdentityMappingViewType(String name) { - new IdentityMappingViewType(name) - } -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewTypeProvider.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewTypeProvider.java deleted file mode 100644 index 7bcc6b34d5..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewTypeProvider.java +++ /dev/null @@ -1,15 +0,0 @@ -package tools.vitruv.framework.views; - -import java.util.Collection; - -/** - * A provider for view types. - */ -public interface ViewTypeProvider { - /** - * Returns the view types covered by this provider. - * - * @return a collection of {@link ViewType}s - */ - public Collection> getViewTypes(); -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewTypeRepository.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewTypeRepository.xtend deleted file mode 100644 index 2727e06d2d..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewTypeRepository.xtend +++ /dev/null @@ -1,36 +0,0 @@ -package tools.vitruv.framework.views - -import java.util.ArrayList -import java.util.Collection -import java.util.HashMap -import java.util.Map -import static com.google.common.base.Preconditions.checkArgument -import static com.google.common.base.Preconditions.checkState - -/** - * Stores and manages all ViewTypes. - */ -class ViewTypeRepository implements ViewTypeProvider { - - val Map> registeredViewTypes - - new() { - registeredViewTypes = new HashMap - } - - override Collection> getViewTypes() { - return new ArrayList(registeredViewTypes.values) - } - - def void register(ViewType viewType) { - checkArgument(viewType !== null, "null cannot be added as a view type") - checkState(!registeredViewTypes.containsKey(viewType.name), "view type name '%s' already taken by another view type", viewType.name) - registeredViewTypes.put(viewType.name, viewType) - } - - def ViewType findViewType(String viewTypeName) { - checkArgument(!viewTypeName.nullOrEmpty, "view type name to search for must not be null or empty") - return registeredViewTypes.get(viewTypeName) - } - -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend deleted file mode 100644 index 9a5f991186..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend +++ /dev/null @@ -1,118 +0,0 @@ -package tools.vitruv.framework.views.changederivation - -import org.eclipse.emf.common.notify.Notifier -import org.eclipse.emf.common.util.BasicMonitor -import org.eclipse.emf.compare.EMFCompare -import org.eclipse.emf.compare.match.impl.MatchEngineFactoryImpl -import org.eclipse.emf.compare.match.impl.MatchEngineFactoryRegistryImpl -import org.eclipse.emf.compare.merge.BatchMerger -import org.eclipse.emf.compare.merge.IMerger -import org.eclipse.emf.compare.scope.DefaultComparisonScope -import org.eclipse.emf.compare.utils.UseIdentifiers -import org.eclipse.emf.ecore.resource.Resource -import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl -import org.eclipse.emf.ecore.util.EcoreUtil -import tools.vitruv.change.composite.description.VitruviusChangeResolver -import tools.vitruv.change.composite.recording.ChangeRecorder - -import static com.google.common.base.Preconditions.checkArgument - -import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceUtil.getReferencedProxies -import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceCopier - -/** - * This default strategy for diff based state changes uses EMFCompare to resolve a - * diff to a sequence of individual changes. - */ -class DefaultStateBasedChangeResolutionStrategy implements StateBasedChangeResolutionStrategy { - /** The identifier matching behavior used by this strategy */ - public val UseIdentifiers useIdentifiers - - /** - * Creates a new instance with the default identifier matching behavior - * which is match by identifier when available. - */ - new() { - this(UseIdentifiers.WHEN_AVAILABLE) - } - - /** - * Creates a new instance with the provided identifier matching behavior. - * @param useIdentifiers The identifier matching behavior to use. - */ - new(UseIdentifiers useIdentifiers) { - this.useIdentifiers = useIdentifiers - } - - private def checkNoProxies(Resource resource, String stateNotice) { - val proxies = resource.referencedProxies - checkArgument(proxies.empty, "%s '%s' should not contain proxies, but contains the following: %s", stateNotice, - resource.URI, String.join(", ", proxies.map[toString])) - } - - override getChangeSequenceBetween(Resource newState, Resource oldState) { - checkArgument(oldState !== null && newState !== null, "old state or new state must not be null!") - newState.checkNoProxies("new state") - oldState.checkNoProxies("old state") - val monitoredResourceSet = new ResourceSetImpl() - val currentStateCopy = ResourceCopier.copyViewResource(oldState, monitoredResourceSet) - return currentStateCopy.record [ - if (oldState.URI != newState.URI) { - currentStateCopy.URI = newState.URI - } - compareStatesAndReplayChanges(newState, currentStateCopy) - ] - } - - override getChangeSequenceForCreated(Resource newState) { - checkArgument(newState !== null, "new state must not be null!") - newState.checkNoProxies("new state") - // It is possible that root elements are automatically generated during resource creation (e.g., Java packages). - // Thus, we create the resource and then monitor the re-insertion of the elements - val monitoredResourceSet = new ResourceSetImpl() - val newResource = monitoredResourceSet.createResource(newState.URI) - newResource.contents.clear() - return newResource.record [ - newResource.contents += EcoreUtil.copyAll(newState.contents) - ] - } - - override getChangeSequenceForDeleted(Resource oldState) { - checkArgument(oldState !== null, "old state must not be null!") - oldState.checkNoProxies("old state") - // Setup resolver and copy state: - val monitoredResourceSet = new ResourceSetImpl() - val currentStateCopy = ResourceCopier.copyViewResource(oldState, monitoredResourceSet) - return currentStateCopy.record [ - currentStateCopy.contents.clear() - ] - } - - private def record(Resource resource, ()=>void function) { - try (val changeRecorder = new ChangeRecorder(resource.resourceSet)) { - changeRecorder.beginRecording - changeRecorder.addToRecording(resource) - function.apply() - val recordedChanges = changeRecorder.endRecording - val changeResolver = VitruviusChangeResolver.forHierarchicalIds(resource.resourceSet) - return changeResolver.assignIds(recordedChanges) - } - } - - /** - * Compares states using EMFCompare and replays the changes to the current state. - */ - private def compareStatesAndReplayChanges(Notifier newState, Notifier currentState) { - val scope = new DefaultComparisonScope(newState, currentState, null) - val emfCompare = (EMFCompare.builder => [ - matchEngineFactoryRegistry = MatchEngineFactoryRegistryImpl.createStandaloneInstance => [ - add(new MatchEngineFactoryImpl(useIdentifiers)) - ] - ]).build - val differences = emfCompare.compare(scope).differences - // Replay the EMF compare differences - val mergerRegistry = IMerger.RegistryImpl.createStandaloneInstance() - val merger = new BatchMerger(mergerRegistry) - merger.copyAllLeftToRight(differences, new BasicMonitor) - } -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.java deleted file mode 100644 index fa5d130cd5..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.java +++ /dev/null @@ -1,54 +0,0 @@ -package tools.vitruv.framework.views.changederivation; - -import org.eclipse.emf.ecore.resource.Resource; - -import tools.vitruv.change.atomic.hid.HierarchicalId; -import tools.vitruv.change.composite.description.VitruviusChange; - -/** - * Strategy for resolving state-based changes to individual change sequences. - * This strategy is used by the domains when there are no change sequences - * available and changes need to be propagated based on the difference between - * the old and new state. - */ -public interface StateBasedChangeResolutionStrategy { - - /** - * Resolves the state-based delta of two resources and returns the correlating - * change sequences. Changes must use hierarchical IDs and are returned - * unresolved. The {@code oldState} remains unmodified from calling this method. - * - * @param newState is the new state of the resource, must not be - * null and must not contain proxies. - * @param oldState is the current or old state of the resource, must not be - * null and must not contain proxies. - * @return a unresolved {@link VitruviusChange} that contains the individual - * change sequence. - */ - VitruviusChange getChangeSequenceBetween(Resource newState, Resource oldState); - - /** - * Resolves the state-based delta for creating the given resource and returns - * the correlating change sequences. Changes must use hierarchical IDs and are - * returned unresolved. - * - * @param newState is the new state of the resource, must not be - * null and must not contain proxies. - * @return a unresolved {@link VitruviusChange} that contains the individual - * change sequence. - */ - VitruviusChange getChangeSequenceForCreated(Resource newState); - - /** - * Resolves the state-based delta for deleting the given resource and returns - * the correlating change sequences. Changes must use hierarchical IDs and are - * returned unresolved. The {@code oldState} remains unmodified from calling - * this method. - * - * @param oldState is the new state of the resource, must not be - * null and must not contain proxies. - * @return a unresolved {@link VitruviusChange} that contains the individual - * change sequence. - */ - VitruviusChange getChangeSequenceForDeleted(Resource oldState); -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/AbstractViewType.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/AbstractViewType.xtend deleted file mode 100644 index 7eea1879ff..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/AbstractViewType.xtend +++ /dev/null @@ -1,16 +0,0 @@ -package tools.vitruv.framework.views.impl - -import org.eclipse.xtend.lib.annotations.Accessors -import tools.vitruv.framework.views.ViewSelector - -import static com.google.common.base.Preconditions.checkArgument - -abstract package class AbstractViewType implements ViewCreatingViewType { - @Accessors(PUBLIC_GETTER) - val String name - - new(String name) { - checkArgument(name !== null, "view type name must not be null") - this.name = name - } -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/BasicView.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/BasicView.xtend deleted file mode 100644 index 65ac02e0ee..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/BasicView.xtend +++ /dev/null @@ -1,151 +0,0 @@ -package tools.vitruv.framework.views.impl - -import org.eclipse.emf.common.notify.Notification -import org.eclipse.emf.common.notify.Notifier -import org.eclipse.emf.common.notify.impl.AdapterImpl -import org.eclipse.emf.common.util.URI -import org.eclipse.emf.ecore.EObject -import org.eclipse.emf.ecore.resource.Resource -import org.eclipse.emf.ecore.resource.ResourceSet -import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl -import org.eclipse.xtend.lib.annotations.Accessors -import tools.vitruv.change.atomic.hid.HierarchicalId -import tools.vitruv.change.atomic.uuid.Uuid -import tools.vitruv.change.composite.description.PropagatedChange -import tools.vitruv.change.composite.description.VitruviusChange -import tools.vitruv.change.composite.propagation.ChangePropagationListener -import tools.vitruv.framework.views.ChangeableViewSource -import tools.vitruv.framework.views.ViewSelection -import tools.vitruv.framework.views.ViewSelector -import tools.vitruv.framework.views.changederivation.StateBasedChangeResolutionStrategy - -import static com.google.common.base.Preconditions.checkArgument -import static com.google.common.base.Preconditions.checkState - -import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories - -package class BasicView implements ModifiableView, ChangePropagationListener { - @Accessors(PUBLIC_GETTER, PROTECTED_SETTER) - var ViewSelection selection - @Accessors(PUBLIC_GETTER, PROTECTED_SETTER) - var ViewCreatingViewType viewType - @Accessors(PUBLIC_GETTER, PROTECTED_SETTER) - var ChangeableViewSource viewSource - @Accessors(PROTECTED_GETTER) - var ResourceSet viewResourceSet - boolean modelChanged - @Accessors(PROTECTED_SETTER) - boolean viewChanged - boolean closed - - protected new(ViewCreatingViewType viewType, ChangeableViewSource viewSource, - ViewSelection selection) { - checkArgument(viewType !== null, "view type must not be null") - checkArgument(viewSource !== null, "view selection must not be null") - checkArgument(selection !== null, "view source must not be null") - this.viewType = viewType - this.viewSource = viewSource - this.selection = selection - viewSource.addChangePropagationListener(this) - viewResourceSet = new ResourceSetImpl().withGlobalFactories - update - } - - override getRootObjects() { - checkNotClosed() - viewResourceSet.resources.map[contents].flatten.toList - } - - override isModified() { - return viewChanged - } - - override isOutdated() { - return modelChanged - } - - override update() { - checkNotClosed() - checkState(!isModified, "cannot update from model when view is modified") - modelChanged = false - viewType.updateView(this) - viewChanged = false - viewResourceSet.addChangeListeners() - } - - override close() throws Exception { - if (!closed) { - closed = true - viewResourceSet.resources.forEach[unload()] - viewResourceSet.resources.clear() - viewResourceSet.removeChangeListeners() - } - viewSource.removeChangePropagationListener(this) - } - - override isClosed() { - return closed - } - - override finishedChangePropagation(Iterable propagatedChanges) { - modelChanged = true - } - - override startedChangePropagation(VitruviusChange changeToPropagate) { - // do nothing - } - - override void registerRoot(EObject object, URI persistAt) { - checkNotClosed() - checkArgument(object !== null, "object to register as root must not be null") - checkArgument(persistAt !== null, "URI for root to register must not be null") - viewResourceSet.createResource(persistAt) => [ - contents += object - ] - } - - override void moveRoot(EObject object, URI newLocation) { - checkNotClosed() - checkArgument(object !== null, "object to move must not be null") - checkState(rootObjects.contains(object), "view must contain element %s to move", object) - checkArgument(newLocation !== null, "URI for new location of root must not be null") - viewResourceSet.resources.findFirst[contents.contains(object)].URI = newLocation - } - - def void checkNotClosed() { - checkState(!closed, "view is already closed!") - } - - private def void addChangeListeners(Notifier notifier) { - notifier.eAdapters += new AdapterImpl() { - override notifyChanged(Notification message) { - viewChanged = true - } - } - switch (notifier) { - ResourceSet: notifier.resources.forEach[addChangeListeners()] - Resource: notifier.contents.forEach[addChangeListeners()] - EObject: notifier.eContents.forEach[addChangeListeners] - } - } - - private def void removeChangeListeners(ResourceSet resourceSet) { - resourceSet.allContents.forEach [ - eAdapters.clear() - ] - } - - override modifyContents((ResourceSet)=>void modificationFunction) { - modificationFunction.apply(viewResourceSet) - } - - override withChangeRecordingTrait() { - checkNotClosed() - return new ChangeRecordingView(this) - } - - override withChangeDerivingTrait(StateBasedChangeResolutionStrategy changeResolutionStrategy) { - checkNotClosed() - return new ChangeDerivingView(this, changeResolutionStrategy) - } -} \ No newline at end of file diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend deleted file mode 100644 index e4d9688e91..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend +++ /dev/null @@ -1,105 +0,0 @@ -package tools.vitruv.framework.views.impl - -import java.util.ArrayList -import java.util.HashMap -import java.util.HashSet -import org.eclipse.emf.ecore.resource.Resource -import org.eclipse.emf.ecore.resource.ResourceSet -import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl -import org.eclipse.xtend.lib.annotations.Delegate -import tools.vitruv.change.atomic.hid.HierarchicalId -import tools.vitruv.change.composite.description.VitruviusChange -import tools.vitruv.change.composite.description.VitruviusChangeFactory -import tools.vitruv.framework.views.CommittableView -import tools.vitruv.framework.views.View -import tools.vitruv.framework.views.changederivation.StateBasedChangeResolutionStrategy - -import static com.google.common.base.Preconditions.checkArgument -import static com.google.common.base.Preconditions.checkState - -import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.common.util.URIUtil.isPathmap -import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceCopier - -/** - * A {@link View} that derives changes based on the changed state of its resources and allows to propagate them - * back to the underlying models using the {@link #commitChanges} method. - */ -class ChangeDerivingView implements ModifiableView, CommittableView { - @Delegate - BasicView view - - val StateBasedChangeResolutionStrategy changeResolutionStrategy - var ResourceSet originalStateViewResourceSet - var HashMap originalStateResourceMapping - - protected new(BasicView view, StateBasedChangeResolutionStrategy changeResolutionStrategy) { - checkArgument(view !== null, "view must not be null") - checkState(!view.isModified, "view must not be modified") - checkState(!view.isOutdated, "view must not be outdated") - checkArgument(changeResolutionStrategy !== null, "change resolution strategy must not be null") - this.view = view - this.changeResolutionStrategy = changeResolutionStrategy - setupReferenceState - } - - override update() { - closeOriginalState - view.update - setupReferenceState - } - - private def setupReferenceState() { - originalStateViewResourceSet = new ResourceSetImpl - ResourceCopier.copyViewResources(view.viewResourceSet.resources, originalStateViewResourceSet) - originalStateResourceMapping = new HashMap - view.viewResourceSet.resources.forEach[resource | originalStateResourceMapping.put(resource, originalStateViewResourceSet.resources.findFirst[URI === resource.URI])] - } - - override commitChanges() { - view.checkNotClosed() - val changes = new ArrayList() - val allResources = new HashSet(originalStateResourceMapping.keySet) - allResources.addAll(view.viewResourceSet.resources) // consider newly added resources - for (changedResource : allResources.filter[!URI.isPathmap]) { - val change = generateChange(changedResource, originalStateResourceMapping.get(changedResource)) - changes += change - } - val change = VitruviusChangeFactory.instance.createCompositeChange(changes) - view.viewType.commitViewChanges(this, change) - view.viewChanged = false - } - - override close() throws Exception { - if (!isClosed) { - closeOriginalState - } - view.close - } - - private def VitruviusChange generateChange(Resource newState, Resource referenceState) { - if (referenceState === null) { - return changeResolutionStrategy.getChangeSequenceForCreated(newState) - } else if (newState === null) { - return changeResolutionStrategy.getChangeSequenceForDeleted(referenceState) - } else { - return changeResolutionStrategy.getChangeSequenceBetween(newState, referenceState) - } - } - - private def closeOriginalState() { - originalStateViewResourceSet.resources.forEach[unload] - originalStateViewResourceSet.resources.clear - } - - override withChangeRecordingTrait() { - val newView = view.withChangeRecordingTrait - closeOriginalState - return newView - } - - override withChangeDerivingTrait(StateBasedChangeResolutionStrategy changeResolutionStrategy) { - val newView = view.withChangeDerivingTrait(changeResolutionStrategy) - closeOriginalState - return newView - } -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend deleted file mode 100644 index 7187b96da5..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend +++ /dev/null @@ -1,76 +0,0 @@ -package tools.vitruv.framework.views.impl - -import org.eclipse.xtend.lib.annotations.Delegate -import tools.vitruv.change.composite.description.VitruviusChangeResolver -import tools.vitruv.change.composite.recording.ChangeRecorder -import tools.vitruv.framework.views.CommittableView -import tools.vitruv.framework.views.View -import tools.vitruv.framework.views.changederivation.StateBasedChangeResolutionStrategy - -import static com.google.common.base.Preconditions.checkArgument -import static com.google.common.base.Preconditions.checkState - -/** - * A {@link View} that records changes to its resources and allows to propagate them - * back to the underlying models using the {@link #commitChanges} method. - */ -class ChangeRecordingView implements ModifiableView, CommittableView { - @Delegate - BasicView view - ChangeRecorder changeRecorder - - protected new(BasicView view) { - checkArgument(view !== null, "view must not be null") - checkState(!view.isModified, "view must not be modified") - this.view = view - setupChangeRecorder - } - - override update() { - changeRecorder.endRecordingAndClose() - view.update() - setupChangeRecorder - } - - private def setupChangeRecorder() { - changeRecorder = new ChangeRecorder(view.viewResourceSet) - changeRecorder.addToRecording(view.viewResourceSet) - changeRecorder.beginRecording() - } - - override commitChanges() { - view.checkNotClosed() - val recordedChange = changeRecorder.endRecording() - val changeResolver = VitruviusChangeResolver.forHierarchicalIds(view.viewResourceSet) - val unresolvedChanges = changeResolver.assignIds(recordedChange) - view.viewType.commitViewChanges(this, unresolvedChanges) - view.viewChanged = false - changeRecorder.beginRecording() - } - - override close() throws Exception { - if (!isClosed) { - changeRecorder.close() - } - view.close() - } - - private def void endRecordingAndClose(ChangeRecorder recorder) { - if (recorder.isRecording) { - recorder.endRecording() - } - recorder.close() - } - - override withChangeRecordingTrait() { - val newView = view.withChangeRecordingTrait - changeRecorder.close - return newView - } - - override withChangeDerivingTrait(StateBasedChangeResolutionStrategy changeResolutionStrategy) { - val newView = view.withChangeDerivingTrait(changeResolutionStrategy) - changeRecorder.close - return newView - } -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java deleted file mode 100644 index f8d7c7ed17..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java +++ /dev/null @@ -1,89 +0,0 @@ -package tools.vitruv.framework.views.impl; - -import static com.google.common.base.Preconditions.checkArgument; -import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Stream; - -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.emf.ecore.resource.ResourceSet; -import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; - -import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceCopier; -import tools.vitruv.change.atomic.hid.HierarchicalId; -import tools.vitruv.change.atomic.uuid.Uuid; -import tools.vitruv.change.atomic.uuid.UuidResolver; -import tools.vitruv.change.composite.description.VitruviusChange; -import tools.vitruv.change.composite.description.VitruviusChangeResolver; -import tools.vitruv.framework.views.ChangeableViewSource; -import tools.vitruv.framework.views.View; -import tools.vitruv.framework.views.ViewSelection; -import tools.vitruv.framework.views.ViewSource; -import tools.vitruv.framework.views.selectors.DirectViewElementSelector; - -/** - * A view type that allows creating views based on a basic element-wise - * selection mechanism and providing a one-to-one (identity) mapping of elements - * within the {@link ViewSource} to a created {@link View}. - */ -public class IdentityMappingViewType extends AbstractViewType, HierarchicalId> { - public IdentityMappingViewType(String name) { - super(name); - } - - @Override - public DirectViewElementSelector createSelector(ChangeableViewSource viewSource) { - return new DirectViewElementSelector<>(this, viewSource, - viewSource.getViewSourceModels().stream().map(resource -> { - if (!resource.getContents().isEmpty() && ResourceCopier.requiresFullCopy(resource)) { - // Some resources (like UML) can only be copied as a whole, so no option to select - // specific root elements - return Stream.of(resource.getContents().get(0)); - } - return resource.getContents().stream(); - }).flatMap(Function.identity()).filter(it -> it != null).toList()); - } - - @Override - public ModifiableView createView(DirectViewElementSelector selector) { - checkArgument(selector.getViewType() == this, "cannot create view with selector for different view type"); - return new BasicView(selector.getViewType(), selector.getViewSource(), selector.getSelection()); - } - - @Override - public void updateView(ModifiableView view) { - view.modifyContents((viewResourceSet) -> { - viewResourceSet.getResources().forEach(Resource::unload); - viewResourceSet.getResources().clear(); - createViewResources(view, viewResourceSet); - }); - } - - @Override - public void commitViewChanges(ModifiableView view, VitruviusChange viewChange) { - ResourceSet viewSourceCopyResourceSet = withGlobalFactories(new ResourceSetImpl()); - VitruviusChangeResolver idChangeResolver = VitruviusChangeResolver.forHierarchicalIds(viewSourceCopyResourceSet); - UuidResolver viewSourceCopyUuidResolver = UuidResolver.create(viewSourceCopyResourceSet); - VitruviusChangeResolver uuidChangeResolver = VitruviusChangeResolver.forUuids(viewSourceCopyUuidResolver); - Map mapping = createViewResources(view, viewSourceCopyResourceSet); - view.getViewSource().getUuidResolver().resolveResources(mapping, viewSourceCopyUuidResolver); - - VitruviusChange resolvedChange = idChangeResolver.resolveAndApply(viewChange); - VitruviusChange unresolvedChanges = uuidChangeResolver.assignIds(resolvedChange); - view.getViewSource().propagateChange(unresolvedChanges); - } - - private Map createViewResources(ModifiableView view, ResourceSet viewResourceSet) { - Collection viewSources = view.getViewSource().getViewSourceModels(); - ViewSelection selection = view.getSelection(); - List resourcesWithSelectedElements = viewSources.stream() - .filter(resource -> resource.getContents().stream().anyMatch(selection::isViewObjectSelected)).toList(); - return ResourceCopier.copyViewSourceResources(resourcesWithSelectedElements, viewResourceSet, - selection::isViewObjectSelected); - } -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ModifiableView.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ModifiableView.xtend deleted file mode 100644 index 2d667d3e5e..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ModifiableView.xtend +++ /dev/null @@ -1,14 +0,0 @@ -package tools.vitruv.framework.views.impl - -import org.eclipse.emf.ecore.resource.ResourceSet -import tools.vitruv.framework.views.ChangeableViewSource -import tools.vitruv.framework.views.View - -/** - * A view whose contents can be modified, in particular by a view type implementation. - */ -interface ModifiableView extends View { - def void modifyContents((ResourceSet)=>void modificationFunction); - - def ChangeableViewSource getViewSource() -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ViewCreatingViewType.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ViewCreatingViewType.java deleted file mode 100644 index b11791df43..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ViewCreatingViewType.java +++ /dev/null @@ -1,45 +0,0 @@ -package tools.vitruv.framework.views.impl; - -import tools.vitruv.change.composite.description.VitruviusChange; -import tools.vitruv.framework.views.ChangeableViewSource; -import tools.vitruv.framework.views.ViewSelector; -import tools.vitruv.framework.views.ViewType; - -/** - * A specific view type that is able to create and update views. This is not its - * public interface but only for internal usage by views and their selectors. - * - * @param the type of view selector this view type uses. - * @param the type of Id the changes to commit must have. - */ -public interface ViewCreatingViewType extends ViewType { - /** - * Creates a view for the given {@link ViewSelector}. The selector must have - * been created by calling the {@link #createSelector} method of the same - * {@link ViewCreatingViewType}. - * - * @param selector the {@link ViewSelector} to create a view for - * @return a {@link ModifiableView} with elements according to the selector. - */ - ModifiableView createView(S selector); - - /** - * Updates a view that is created from this view type to ensure it is consistent - * with the virtual model. - * - * @param view is the view to be updated. - */ - void updateView(ModifiableView view); - - /** - * Commits the changes made to the view and its containing elements to the - * underlying {@link ChangeableViewSource}. Since view elements do not - * necessarily correspond to elements of the underlying view source, the view - * type is responsible for transforming the given {@link VitruviusChange} such - * that the underlying view source can process it. - * - * @param view is the modified view. - * @param viewChange are the changes performed to the view. - */ - void commitViewChanges(ModifiableView view, VitruviusChange viewChange); -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/selection/AbstractViewSelection.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/selection/AbstractViewSelection.java deleted file mode 100644 index b38480ffd3..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/selection/AbstractViewSelection.java +++ /dev/null @@ -1,55 +0,0 @@ -package tools.vitruv.framework.views.selection; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.eclipse.emf.ecore.EObject; - -import tools.vitruv.framework.views.ModifiableViewSelection; - -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.base.Preconditions.checkNotNull; - -public abstract class AbstractViewSelection implements ModifiableViewSelection { - final Map elementsSelection = new HashMap<>(); - - public AbstractViewSelection(Collection selectableElements) { - selectableElements.forEach(object -> this.elementsSelection - .put(checkNotNull(object, "element to select must not be null"), false)); - } - - public AbstractViewSelection(ModifiableViewSelection sourceViewSelection) { - this(sourceViewSelection.getSelectableElements()); - for (EObject selectableElement : sourceViewSelection.getSelectableElements()) { - setSelected(selectableElement, sourceViewSelection.isSelected(selectableElement)); - } - } - - private void checkIsSelectable(EObject eObject) { - checkState(isSelectable(eObject), "given object %s must be contained in the selector elements", eObject); - } - - @Override - public boolean isSelected(EObject eObject) { - return elementsSelection.getOrDefault(eObject, false); - } - - @Override - public boolean isSelectable(EObject eObject) { - return elementsSelection.keySet().contains(eObject); - } - - @Override - public Collection getSelectableElements() { - return Collections.unmodifiableSet(elementsSelection.keySet()); - } - - @Override - public void setSelected(EObject eObject, boolean selected) { - checkIsSelectable(eObject); - elementsSelection.put(eObject, selected); - } - -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/selection/ElementViewSelection.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/selection/ElementViewSelection.java deleted file mode 100644 index c8d00630ec..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/selection/ElementViewSelection.java +++ /dev/null @@ -1,24 +0,0 @@ -package tools.vitruv.framework.views.selection; - -import java.util.Collection; - -import org.eclipse.emf.ecore.EObject; - -import tools.vitruv.framework.views.ModifiableViewSelection; - -public class ElementViewSelection extends AbstractViewSelection { - - public ElementViewSelection(Collection selectableElements) { - super(selectableElements); - } - - public ElementViewSelection(ModifiableViewSelection sourceViewSelection) { - super(sourceViewSelection); - } - - @Override - public boolean isViewObjectSelected(EObject eObject) { - return isSelected(eObject); - } - -} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/selectors/DirectViewElementSelector.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/selectors/DirectViewElementSelector.xtend deleted file mode 100644 index 50add0b64f..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/selectors/DirectViewElementSelector.xtend +++ /dev/null @@ -1,74 +0,0 @@ -package tools.vitruv.framework.views.selectors - -import java.util.Collection -import org.eclipse.emf.ecore.EObject -import org.eclipse.xtend.lib.annotations.Accessors -import org.eclipse.xtend.lib.annotations.Delegate -import tools.vitruv.framework.views.ChangeableViewSource -import tools.vitruv.framework.views.ModifiableViewSelection -import tools.vitruv.framework.views.ViewSelector -import tools.vitruv.framework.views.ViewType -import tools.vitruv.framework.views.impl.ViewCreatingViewType -import tools.vitruv.framework.views.selection.ElementViewSelection - -import static com.google.common.base.Preconditions.checkArgument -import static com.google.common.base.Preconditions.checkState - -/** - * A view selector that provides a selection of elements being view objects at - * the same time. This means, there is no indirection between selected elements - * and view elements (such as selecting types but providing instances in the view), - * but a selection is performed on the view elements themselves. - */ -class DirectViewElementSelector implements ViewSelector { - @Delegate - val ModifiableViewSelection viewSelection - - @Accessors(PUBLIC_GETTER) - val ChangeableViewSource viewSource - - @Accessors(PUBLIC_GETTER) - val ViewCreatingViewType, Id> viewType - - /** - * Creates a new selector based on the given collection of selectable elements - * for the given {@link ViewType} and {@link ChangeableViewSource}. All arguments - * must not be null. - * All elements will be unselected after creation. - * - * @param viewType - the {@link ViewType} to create a view for when - * calling {@link createView} - * @param viewSource - the {@link ChangeableViewSource} to create a view - * from - * @param selectableElements - the elements to select from to be used by the - * {@link ViewType} when creating a view - */ - new(ViewCreatingViewType, Id> viewType, ChangeableViewSource viewSource, - Collection selectableElements) { - checkArgument(selectableElements !== null, "selectable elements must not be null") - checkArgument(viewType !== null, "view type must not be null") - checkArgument(viewSource !== null, "view source must not be null") - this.viewType = viewType - this.viewSource = viewSource - this.viewSelection = new ElementViewSelection(selectableElements) - } - - override createView() { - checkState(isValid(), "the current selection is invalid, thus a view cannot be created") - return viewType.createView(this) - } - - /** - * {@link DirectViewElementSelector}s are always valid. - */ - override boolean isValid() { - // A basic selection is always valid. In particular, it does not require at least one element to be selected - // because it must be possible to create empty views upon creation of a (virtual) model. - true - } - - override getSelection() { - return new ElementViewSelection(viewSelection) - } - -} diff --git a/bundles/tools.vitruv.framework.vsum/.classpath b/bundles/tools.vitruv.framework.vsum/.classpath deleted file mode 100644 index ef548cb968..0000000000 --- a/bundles/tools.vitruv.framework.vsum/.classpath +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/bundles/tools.vitruv.framework.vsum/.project b/bundles/tools.vitruv.framework.vsum/.project deleted file mode 100644 index 7ba2fe4de0..0000000000 --- a/bundles/tools.vitruv.framework.vsum/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - tools.vitruv.framework.vsum - - - - - - org.eclipse.xtext.ui.shared.xtextBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - org.eclipse.xtext.ui.shared.xtextNature - - diff --git a/bundles/tools.vitruv.framework.vsum/.settings/org.eclipse.core.resources.prefs b/bundles/tools.vitruv.framework.vsum/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 99f26c0203..0000000000 --- a/bundles/tools.vitruv.framework.vsum/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -encoding/=UTF-8 diff --git a/bundles/tools.vitruv.framework.vsum/META-INF/MANIFEST.MF b/bundles/tools.vitruv.framework.vsum/META-INF/MANIFEST.MF deleted file mode 100644 index 4abb89d0e1..0000000000 --- a/bundles/tools.vitruv.framework.vsum/META-INF/MANIFEST.MF +++ /dev/null @@ -1,19 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Vitruv Framework Virtual Single Underlying Model -Bundle-SymbolicName: tools.vitruv.framework.vsum;singleton:=true -Automatic-Module-Name: tools.vitruv.framework.vsum -Bundle-Version: 3.0.1.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-17 -Require-Bundle: org.apache.log4j, - tools.vitruv.change.composite;visibility:=reexport, - tools.vitruv.framework.views;visibility:=reexport, - tools.vitruv.change.propagation;visibility:=reexport, - org.eclipse.xtend.lib, - org.eclipse.emf.ecore.xmi, - edu.kit.ipd.sdq.activextendannotations, - edu.kit.ipd.sdq.commons.util.emf, - edu.kit.ipd.sdq.commons.util.java -Export-Package: tools.vitruv.framework.vsum, - tools.vitruv.framework.vsum.internal;x-internal:=true -Bundle-Vendor: vitruv.tools diff --git a/bundles/tools.vitruv.framework.vsum/build.properties b/bundles/tools.vitruv.framework.vsum/build.properties deleted file mode 100644 index 3c36c1d7e3..0000000000 --- a/bundles/tools.vitruv.framework.vsum/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -source.. = src/,\ - xtend-gen/ -output.. = target/classes/ -bin.includes = META-INF/,\ - . diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/VirtualModel.xtend b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/VirtualModel.xtend deleted file mode 100644 index 3775c2f399..0000000000 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/VirtualModel.xtend +++ /dev/null @@ -1,18 +0,0 @@ -package tools.vitruv.framework.vsum - -import java.nio.file.Path -import tools.vitruv.change.composite.propagation.ChangeableModelRepository -import tools.vitruv.framework.views.ViewProvider -import tools.vitruv.framework.views.ViewTypeProvider -import tools.vitruv.change.propagation.ChangePropagationMode - -interface VirtualModel extends ChangeableModelRepository, ViewProvider, ViewTypeProvider { - def Path getFolder() - - /** - * Defines how changes are propagated when passed to {@link #propagateChange(VitruviusChange)}. - * By default, {@link ChangePropagationMode#TRANSITIVE_CYCLIC} is used, i.e., changes are - * transitively propagated until no further changes are produced. - */ - def void setChangePropagationMode(ChangePropagationMode changePropagationMode) -} diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/VirtualModelBuilder.xtend b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/VirtualModelBuilder.xtend deleted file mode 100644 index 3b16eb0373..0000000000 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/VirtualModelBuilder.xtend +++ /dev/null @@ -1,107 +0,0 @@ -package tools.vitruv.framework.vsum - -import java.io.File -import java.nio.file.Path -import java.util.Collection -import java.util.HashSet -import java.util.Set -import tools.vitruv.change.propagation.ChangePropagationSpecification -import tools.vitruv.change.propagation.ChangePropagationSpecificationRepository -import tools.vitruv.change.interaction.InteractionResultProvider -import tools.vitruv.change.interaction.InternalUserInteractor -import tools.vitruv.change.interaction.UserInteractionFactory -import tools.vitruv.framework.views.ViewType -import tools.vitruv.framework.views.ViewTypeRepository -import tools.vitruv.framework.vsum.helper.VsumFileSystemLayout -import tools.vitruv.framework.vsum.internal.InternalVirtualModel -import tools.vitruv.framework.vsum.internal.VirtualModelImpl -import tools.vitruv.change.propagation.ProjectMarker - -import static com.google.common.base.Preconditions.checkState - -class VirtualModelBuilder { - val Set> viewTypes = new HashSet() - val Set changePropagationSpecifications = new HashSet() - var Path storageFolder - var InternalUserInteractor userInteractor - - def VirtualModelBuilder withStorageFolder(File folder) { - withStorageFolder(folder.toPath) - } - - def VirtualModelBuilder withStorageFolder(Path folder) { - checkState(storageFolder === null || storageFolder == folder, "There is already another storage folder set: %s", storageFolder) - storageFolder = folder - return this - } - - def VirtualModelBuilder withUserInteractorForResultProvider(InteractionResultProvider resultProvider) { - withUserInteractor(UserInteractionFactory.instance.createUserInteractor(resultProvider)) - } - - def VirtualModelBuilder withUserInteractor(InternalUserInteractor userInteractor) { - checkState(this.userInteractor === null || this.userInteractor == userInteractor, - "There is already another user interactor set: %s", this.userInteractor) - this.userInteractor = userInteractor - return this - } - - def VirtualModelBuilder withViewType(ViewType viewType) { - viewTypes += viewType - return this - } - - def VirtualModelBuilder withViewTypes(Collection> viewTypes) { - for (viewType : viewTypes) withViewType(viewType) - return this - } - - def VirtualModelBuilder withChangePropagationSpecifications(ChangePropagationSpecification... changePropagationSpecifications) { - for(spec : changePropagationSpecifications) withChangePropagationSpecification(spec) - return this - } - - def VirtualModelBuilder withChangePropagationSpecifications(Iterable changePropagationSpecifications) { - for(spec : changePropagationSpecifications) withChangePropagationSpecification(spec) - return this - } - - def VirtualModelBuilder withChangePropagationSpecification(ChangePropagationSpecification changePropagationSpecification) { - if (changePropagationSpecifications.contains(changePropagationSpecification)) return this - - for (existingPropagationSpecification : changePropagationSpecifications) { - if(existingPropagationSpecification == changePropagationSpecification) return this - - if (existingPropagationSpecification.sourceMetamodelDescriptor.equals( - changePropagationSpecification.sourceMetamodelDescriptor) && - existingPropagationSpecification.targetMetamodelDescriptor.equals( - changePropagationSpecification.targetMetamodelDescriptor)) { - throw new IllegalArgumentException( - '''This virtual model configuration already contains the change propagation specification «existingPropagationSpecification» between «existingPropagationSpecification.sourceMetamodelDescriptor» and «existingPropagationSpecification.targetMetamodelDescriptor»!''' - ) - } - } - - changePropagationSpecifications += changePropagationSpecification - return this - } - - def InternalVirtualModel buildAndInitialize() { - checkState(storageFolder !== null, "No storage folder was configured!") - checkState(userInteractor !== null, "No user interactor was configured!") - val viewTypeRepository = new ViewTypeRepository() - viewTypes.forEach[viewTypeRepository.register(it)] - val changeSpecificationRepository = new ChangePropagationSpecificationRepository(changePropagationSpecifications) - - val fileSystemLayout = new VsumFileSystemLayout(storageFolder) - fileSystemLayout.prepare() - val vsum = new VirtualModelImpl(fileSystemLayout, userInteractor, viewTypeRepository, changeSpecificationRepository) - vsum.loadExistingModels() - try { - ProjectMarker.getProjectRootFolder(storageFolder) - } catch (IllegalStateException exception) { - ProjectMarker.markAsProjectRootFolder(storageFolder) - } - return vsum - } -} \ No newline at end of file diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/VirtualModelManager.xtend b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/VirtualModelManager.xtend deleted file mode 100644 index 66d81c6325..0000000000 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/VirtualModelManager.xtend +++ /dev/null @@ -1,32 +0,0 @@ -package tools.vitruv.framework.vsum - -import java.nio.file.Path -import tools.vitruv.framework.vsum.internal.VirtualModelRegistry -import tools.vitruv.framework.vsum.internal.InternalVirtualModel - -final class VirtualModelManager { - static val instance = new VirtualModelManager(); - - private new() { - } - - static def getInstance() { - return instance; - } - - def InternalVirtualModel getVirtualModel(Path folder) { - var virtualModel = VirtualModelRegistry.instance.getVirtualModel(folder) - if (virtualModel === null) { - // get the workspace root -// val root = ResourcesPlugin.getWorkspace().getRoot(); -// // get the project handle -// val project = root.getProject(name); -// // open up this newly-created project in Eclipse -// project.open(new NullProgressMonitor()); -// // TODO HK: Extract VSUM from project - throw new UnsupportedOperationException("Virtual models cannot be loaded yet"); - } - return virtualModel - } - -} diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend deleted file mode 100644 index 06f682af3f..0000000000 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend +++ /dev/null @@ -1,103 +0,0 @@ -package tools.vitruv.framework.vsum.helper - -import java.io.IOException -import java.net.URLEncoder -import java.nio.file.Path - -import static com.google.common.base.Preconditions.checkArgument -import static com.google.common.base.Preconditions.checkState -import static java.nio.charset.StandardCharsets.UTF_8 -import static java.nio.file.Files.createDirectories -import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.common.util.URIUtil.createFileURI -import org.eclipse.emf.common.util.URI - -class VsumFileSystemLayout { - static final String CORRESPONDENCES_FILE = "correspondences.correspondence"; - static final String UUIDS_FILE = "uuid.uuid"; - static final String MODELS_FILE = "models.models"; - static final String VSUM_FOLDER_NAME = "vsum"; - static final String CONSISTENCY_METADATA_FOLDER_NAME = "consistencymetadata"; - - val Path vsumProjectFolder - var prepared = false - - new(Path vsumProjectFolder) { - this.vsumProjectFolder = vsumProjectFolder - } - - def void prepare() throws IOException { - createDirectories(vsumFolder) - createDirectories(consistencyMetadataFolder) - prepared = true - } - - def private Path getMetadataFilePath(String... metadataKey) { - checkArgument(metadataKey !== null || metadataKey.length > 0, "The key must have at least one part!") - checkArgument(metadataKey.get(metadataKey.length - 1).contains('.'), "metadataKey is missing a file extension!") - - return metadataKey.fold(Path.of("")) [ last, keyPart | - checkArgument(keyPart !== null, "A key part must not be null!") - // URL-encoding the string makes it save for being a file part, - // except for the cases '', '.' and '..' - // we thus use _ as an escape character - // This also ensures that the resulting path is always located - // within the metadata folder. - val preparedKeyPart = switch (keyPart) { - case ".": "_." - case "..": "_._." - case "": "_" - default: keyPart.replaceAll("_", "__") - } - last.resolve(URLEncoder.encode(preparedKeyPart, UTF_8)) - ] - } - - /** - * Gets the {@link URI} of a model that stores metadata. - * @param metadataKeyThe key uniquely identifying the metadata model. The different parts of the key - * can be used to convey some sort of hierarchy in the metadata. The key may contain - * arbitrary characters. The last key part contains the metadata model's file name - * and extension. - * @return the URI of the specified metadata model - */ - def URI getConsistencyMetadataModelURI(String... metadataKey) { - checkPrepared() - var metadataPath = consistencyMetadataFolder.resolve(getMetadataFilePath(metadataKey)) - return metadataPath.toFile.createFileURI() - } - - def URI getCorrespondencesURI() { - checkPrepared() - return vsumFolder.resolve(CORRESPONDENCES_FILE).toFile.createFileURI() - } - - def URI getUuidsURI() { - checkPrepared() - return vsumFolder.resolve(UUIDS_FILE).toFile.createFileURI() - } - - def Path getModelsNamesFilesPath() { - checkPrepared() - return vsumFolder.resolve(MODELS_FILE) - } - - def Path getVsumProjectFolder() { - return this.vsumProjectFolder - } - - def private getVsumFolder() { - vsumProjectFolder.resolve(VSUM_FOLDER_NAME) - } - - def private Path getConsistencyMetadataFolder() { - vsumProjectFolder.resolve(CONSISTENCY_METADATA_FOLDER_NAME) - } - - override String toString() { - return '''@«vsumProjectFolder»''' - } - - def private void checkPrepared() { - checkState(prepared, "The file system layout has not been loaded yet!") - } -} diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/InternalVirtualModel.xtend b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/InternalVirtualModel.xtend deleted file mode 100644 index 0b3e6a6f8e..0000000000 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/InternalVirtualModel.xtend +++ /dev/null @@ -1,15 +0,0 @@ -package tools.vitruv.framework.vsum.internal - -import org.eclipse.emf.common.util.URI -import tools.vitruv.change.correspondence.Correspondence -import tools.vitruv.change.correspondence.view.EditableCorrespondenceModelView -import tools.vitruv.framework.views.ChangeableViewSource -import tools.vitruv.framework.vsum.VirtualModel - -interface InternalVirtualModel extends VirtualModel, ChangeableViewSource { - def EditableCorrespondenceModelView getCorrespondenceModel() - - def ModelInstance getModelInstance(URI modelUri) - - def void dispose() -} diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelInstance.xtend b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelInstance.xtend deleted file mode 100644 index 0475e803c0..0000000000 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelInstance.xtend +++ /dev/null @@ -1,65 +0,0 @@ -package tools.vitruv.framework.vsum.internal - -import java.io.IOException -import org.apache.log4j.Logger -import org.eclipse.emf.common.util.URI -import org.eclipse.emf.ecore.EObject -import org.eclipse.emf.ecore.resource.Resource -import org.eclipse.xtend.lib.annotations.Accessors - -import static com.google.common.base.Preconditions.checkArgument - -class ModelInstance { - static val LOGGER = Logger.getLogger(ModelInstance) - @Accessors(PUBLIC_GETTER) - Resource resource - - new(Resource resource) { - checkArgument(resource !== null, "cannot create a model instance for a null resource") - this.resource = resource - LOGGER.debug('''Create model instance for resource with URI: «URI»''') - } - - def URI getURI() { - return resource.URI - } - - def void addRoot(EObject root) { - resource.contents += root - resource.modified = true - LOGGER.debug('''Add root to resource: «resource»''') - } - - def void markModified() { - resource.modified = true - } - - def boolean isEmpty() { - resource.contents.isEmpty - } - - def void save() { - if (!resource.modified) { - return - } - LOGGER.debug('''Save resource: «resource»''') - try { - resource.save(null) - resource.modified = false - } catch (IOException e) { - LOGGER.error('''Model could not be saved: «URI»''', e) - throw new IllegalStateException('''Could not save URI «URI»''', e) - } - } - - def void delete() { - LOGGER.debug('''Delete resource: «resource»''') - try { - resource.delete(null) - } catch (IOException e) { - LOGGER.error('''Deletion of resource «resource» did not work.''', e) - throw new IllegalStateException('''Could not delete URI «URI»''', e) - } - } - -} diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelRepository.xtend b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelRepository.xtend deleted file mode 100644 index dc664e44c2..0000000000 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelRepository.xtend +++ /dev/null @@ -1,20 +0,0 @@ -package tools.vitruv.framework.vsum.internal - -import java.util.Collection -import org.eclipse.emf.common.util.URI -import org.eclipse.emf.ecore.resource.Resource -import tools.vitruv.change.propagation.ChangeRecordingModelRepository - -package interface ModelRepository extends ChangeRecordingModelRepository { - /** - * Returns the model at the given {@link URI} if it was already loaded to or created in - * the repository. Returns null otherwise. - */ - def ModelInstance getModel(URI modelUri) - - def void loadExistingModels() - - def void saveOrDeleteModels() - - def Collection getModelResources() -} diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java deleted file mode 100644 index 27a1b8d79a..0000000000 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java +++ /dev/null @@ -1,215 +0,0 @@ -package tools.vitruv.framework.vsum.internal; - -import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.getOrCreateResource; -import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.loadOrCreateResource; -import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories; -import static tools.vitruv.change.correspondence.model.CorrespondenceModelFactory.createPersistableCorrespondenceModel; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.apache.log4j.Logger; -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.emf.ecore.resource.ResourceSet; -import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; - -import tools.vitruv.change.atomic.uuid.Uuid; -import tools.vitruv.change.atomic.uuid.UuidResolver; -import tools.vitruv.change.composite.description.TransactionalChange; -import tools.vitruv.change.composite.description.VitruviusChange; -import tools.vitruv.change.composite.description.VitruviusChangeResolver; -import tools.vitruv.change.composite.recording.ChangeRecorder; -import tools.vitruv.change.correspondence.Correspondence; -import tools.vitruv.change.correspondence.model.PersistableCorrespondenceModel; -import tools.vitruv.change.correspondence.view.CorrespondenceModelViewFactory; -import tools.vitruv.change.correspondence.view.EditableCorrespondenceModelView; -import tools.vitruv.change.propagation.impl.ResourceRegistrationAdapter; -import tools.vitruv.framework.vsum.helper.VsumFileSystemLayout; - -class ResourceRepositoryImpl implements ModelRepository { - private static final Logger LOGGER = Logger.getLogger(ResourceRepositoryImpl.class); - - private final ResourceSet modelsResourceSet = withGlobalFactories(new ResourceSetImpl()); - private final Map modelInstances = new HashMap<>(); - private final PersistableCorrespondenceModel correspondenceModel; - private UuidResolver uuidResolver = UuidResolver.create(modelsResourceSet); - private final ChangeRecorder changeRecorder = new ChangeRecorder(modelsResourceSet); - private final VitruviusChangeResolver changeResolver = VitruviusChangeResolver.forUuids(uuidResolver); - - private final VsumFileSystemLayout fileSystemLayout; - - private boolean isRecording = false; - private boolean isLoading = false; - - ResourceRepositoryImpl(VsumFileSystemLayout fileSystemLayout) { - this.fileSystemLayout = fileSystemLayout; - this.correspondenceModel = createPersistableCorrespondenceModel(fileSystemLayout.getCorrespondencesURI()); - modelsResourceSet.eAdapters() - .add(new ResourceRegistrationAdapter(resource -> getCreateOrLoadModelUnlessLoading(resource.getURI()))); - } - - @Override - public void loadExistingModels() { - isLoading = true; - try { - readModelsFile(); - } catch (IOException e) { - throw new IllegalStateException(e); - } - correspondenceModel.loadSerializedCorrespondences(modelsResourceSet); - isLoading = false; - } - - private void writeModelsFile() throws IOException { - Files.write(fileSystemLayout.getModelsNamesFilesPath(), modelsResourceSet.getResources().stream() - .map(Resource::getURI).map(URI::toString).toList()); - } - - private void readModelsFile() throws IOException { - List modelUris; - try { - modelUris = Files.readAllLines(fileSystemLayout.getModelsNamesFilesPath()).stream().map(URI::createURI) - .toList(); - - } catch (NoSuchFileException e) { - // There are no existing models, so don't do anything - return; - } - modelUris.forEach(uri -> loadOrCreateResource(modelsResourceSet, uri)); - uuidResolver.loadFromUri(fileSystemLayout.getUuidsURI()); - modelUris.forEach(this::createOrLoadModel); - } - - @Override - public EditableCorrespondenceModelView getCorrespondenceModel() { - return CorrespondenceModelViewFactory.createEditableCorrespondenceModelView(correspondenceModel); - } - - @Override - public ModelInstance getModel(URI modelUri) { - return modelInstances.get(modelUri); - } - - @Override - public UuidResolver getUuidResolver() { - return uuidResolver; - } - - private ModelInstance getCreateOrLoadModelUnlessLoading(URI modelUri) { - if (isLoading) { - return null; - } - return getCreateOrLoadModel(modelUri); - } - - private ModelInstance getCreateOrLoadModel(URI modelUri) { - ModelInstance instance = getModel(modelUri); - if (instance != null) { - return instance; - } - return createOrLoadModel(modelUri); - } - - private ModelInstance createOrLoadModel(URI modelUri) { - Resource resource; - if (modelUri.isFile() || modelUri.isPlatform()) { - resource = getOrCreateResource(modelsResourceSet, modelUri); - } else { - resource = loadOrCreateResource(modelsResourceSet, modelUri); - } - ModelInstance modelInstance = new ModelInstance(resource); - modelInstances.put(modelUri, modelInstance); - registerRecorder(modelInstance); - return modelInstance; - } - - private void registerRecorder(ModelInstance modelInstance) { - // Only monitor modifiable models (file / platform URIs, not pathmap URIs) - if (modelInstance.getURI().isFile() || modelInstance.getURI().isPlatform()) { - changeRecorder.addToRecording(modelInstance.getResource()); - if (isRecording && !changeRecorder.isRecording()) { - changeRecorder.beginRecording(); - } - } - } - - @Override - public void persistAsRoot(EObject rootObject, URI uri) { - getCreateOrLoadModel(uri).addRoot(rootObject); - } - - @Override - public void saveOrDeleteModels() { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Saving all models of model repository for VSUM " + fileSystemLayout); - } - Iterator> modelInstancesIterator = modelInstances.entrySet().iterator(); - while (modelInstancesIterator.hasNext()) { - ModelInstance modelInstance = modelInstancesIterator.next().getValue(); - if (modelInstance.isEmpty()) { - modelInstance.delete(); - modelInstancesIterator.remove(); - } else { - modelInstance.save(); - } - } - correspondenceModel.save(); - try { - writeModelsFile(); - uuidResolver.storeAtUri(fileSystemLayout.getUuidsURI()); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - @Override - public Iterable> recordChanges(Runnable changeApplicator) { - changeRecorder.beginRecording(); - isRecording = true; - LOGGER.debug("Start recording virtual model"); - changeApplicator.run(); - LOGGER.debug("End recording virtual model"); - isRecording = false; - changeRecorder.endRecording(); - TransactionalChange change = changeRecorder.getChange(); - changeResolver.assignIds(change); - return change.containsConcreteChange() ? List.of(change) : List.of(); - } - - @Override - public VitruviusChange applyChange(VitruviusChange change) { - return changeResolver.resolveAndApply(change); - } - - @Override - public URI getMetadataModelURI(String... metadataKey) { - return fileSystemLayout.getConsistencyMetadataModelURI(metadataKey); - } - - @Override - public Resource getModelResource(URI uri) { - return getCreateOrLoadModel(uri).getResource(); - } - - @Override - public Collection getModelResources() { - return modelsResourceSet.getResources(); - } - - @Override - public void close() { - changeRecorder.close(); - modelsResourceSet.getResources().forEach(Resource::unload); - modelsResourceSet.getResources().clear(); - uuidResolver = null; - } -} diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelImpl.java b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelImpl.java deleted file mode 100644 index 0f09d7d5a5..0000000000 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelImpl.java +++ /dev/null @@ -1,183 +0,0 @@ -package tools.vitruv.framework.vsum.internal; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.nio.file.Path; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; - -import org.apache.log4j.Logger; -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.resource.Resource; - -import tools.vitruv.change.atomic.uuid.Uuid; -import tools.vitruv.change.atomic.uuid.UuidResolver; -import tools.vitruv.change.composite.description.PropagatedChange; -import tools.vitruv.change.composite.description.VitruviusChange; -import tools.vitruv.change.composite.propagation.ChangePropagationListener; -import tools.vitruv.change.correspondence.Correspondence; -import tools.vitruv.change.correspondence.view.EditableCorrespondenceModelView; -import tools.vitruv.change.interaction.InternalUserInteractor; -import tools.vitruv.change.propagation.ChangePropagationMode; -import tools.vitruv.change.propagation.ChangePropagationSpecificationProvider; -import tools.vitruv.change.propagation.impl.ChangePropagator; -import tools.vitruv.framework.views.ViewSelector; -import tools.vitruv.framework.views.ViewType; -import tools.vitruv.framework.views.ViewTypeProvider; -import tools.vitruv.framework.views.ViewTypeRepository; -import tools.vitruv.framework.vsum.helper.VsumFileSystemLayout; - -public class VirtualModelImpl implements InternalVirtualModel { - private static final Logger LOGGER = Logger.getLogger(VirtualModelImpl.class); - - private final ModelRepository resourceRepository; - private final ViewTypeProvider viewTypeRepository; - private final VsumFileSystemLayout fileSystemLayout; - private final List changePropagationListeners = new LinkedList<>(); - - private final ChangePropagationSpecificationProvider changePropagationSpecificationProvider; - private final InternalUserInteractor userInteractor; - private ChangePropagationMode changePropagationMode = ChangePropagationMode.TRANSITIVE_CYCLIC; - - public VirtualModelImpl(VsumFileSystemLayout fileSystemLayout, InternalUserInteractor userInteractor, - ViewTypeRepository viewTypeRepository, - ChangePropagationSpecificationProvider changePropagationSpecificationProvider) { - this.fileSystemLayout = fileSystemLayout; - this.viewTypeRepository = viewTypeRepository; - resourceRepository = new ResourceRepositoryImpl(fileSystemLayout); - - this.changePropagationSpecificationProvider = changePropagationSpecificationProvider; - this.userInteractor = userInteractor; - } - - public void loadExistingModels() { - resourceRepository.loadExistingModels(); - } - - @Override - public synchronized EditableCorrespondenceModelView getCorrespondenceModel() { - return resourceRepository.getCorrespondenceModel(); - } - - @Override - public synchronized ModelInstance getModelInstance(URI modelUri) { - return resourceRepository.getModel(modelUri); - } - - @Override - public UuidResolver getUuidResolver() { - return resourceRepository.getUuidResolver(); - } - - private synchronized void save() { - resourceRepository.saveOrDeleteModels(); - } - - @Override - public synchronized List propagateChange(VitruviusChange change) { - checkNotNull(change, "change to propagate"); - checkArgument(change.containsConcreteChange(), "This change contains no concrete change:%s%s", - System.lineSeparator(), change); - - LOGGER.info("Starting change propagation"); - startChangePropagation(change); - - ChangePropagator changePropagator = new ChangePropagator(resourceRepository, - changePropagationSpecificationProvider, userInteractor, changePropagationMode); - List result = changePropagator.propagateChange(change); - save(); - - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Propagated changes: " + result); - } - - finishChangePropagation(change, result); - LOGGER.info("Finished change propagation"); - return result; - } - - private void startChangePropagation(VitruviusChange change) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Started synchronizing change: " + change); - } - changePropagationListeners.stream().forEach(it -> it.startedChangePropagation(change)); - } - - private void finishChangePropagation(VitruviusChange inputChange, Iterable generatedChanges) { - changePropagationListeners.stream().forEach(it -> it.finishedChangePropagation(generatedChanges)); - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Finished synchronizing change: " + inputChange); - } - } - - @Override - public Path getFolder() { - return fileSystemLayout.getVsumProjectFolder(); - } - - /** - * Registers the given {@link ChangePropagationListener}. The listener must not - * be null. - */ - @Override - public synchronized void addChangePropagationListener(ChangePropagationListener propagationListener) { - this.changePropagationListeners.add(checkNotNull(propagationListener, "propagationListener")); - } - - /** - * Unregisters the given {@link ChangePropagationListener}. The listener must - * not be null. - */ - @Override - public synchronized void removeChangePropagationListener(ChangePropagationListener propagationListener) { - this.changePropagationListeners.remove(checkNotNull(propagationListener, "propagationListener")); - } - - /** - * Returns the name of the virtual model. - * - * @return The name of the virtual model - */ - public String getName() { - return getFolder().getFileName().toString(); - } - - @Override - public void dispose() { - try { - resourceRepository.close(); - } catch (Exception e) { - throw new IllegalStateException(e); - } - VirtualModelRegistry.getInstance().deregisterVirtualModel(this); - } - - @Override - public Collection getViewSourceModels() { - return resourceRepository.getModelResources(); - } - - @Override - public Collection> getViewTypes() { - return viewTypeRepository.getViewTypes(); - } - - @Override - public S createSelector(ViewType viewType) { - /* - * Note that ViewType.createSelector() accepts a ChangeableViewSource, which - * VirtualModelImpl implements but not its publicly used interface VitualModel. - * Thus calling viewType.createSelector(virtualModel) with virtualModel having - * the static type VirtualModel is not possible, i.e., this method hides - * implementation details and is not a convenience method. - */ - return viewType.createSelector(this); - } - - @Override - public void setChangePropagationMode(ChangePropagationMode changePropagationMode) { - this.changePropagationMode = changePropagationMode; - } -} diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelRegistry.java b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelRegistry.java deleted file mode 100644 index 14298f46bc..0000000000 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelRegistry.java +++ /dev/null @@ -1,30 +0,0 @@ -package tools.vitruv.framework.vsum.internal; - -import java.nio.file.Path; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class VirtualModelRegistry { - private static VirtualModelRegistry instance = new VirtualModelRegistry(); - - private Map folderToVirtualModelMap = new ConcurrentHashMap<>(); - - private VirtualModelRegistry() { - } - - public static VirtualModelRegistry getInstance() { - return instance; - } - - public InternalVirtualModel getVirtualModel(Path folder) { - return folderToVirtualModelMap.get(folder); - } - - public void registerVirtualModel(InternalVirtualModel model) { - folderToVirtualModelMap.put(model.getFolder(), model); - } - - public void deregisterVirtualModel(InternalVirtualModel model) { - folderToVirtualModelMap.remove(model.getFolder()); - } -} diff --git a/bundles/tools.vitruv.testutils.vsum/.classpath b/bundles/tools.vitruv.testutils.vsum/.classpath deleted file mode 100644 index 5b259b9051..0000000000 --- a/bundles/tools.vitruv.testutils.vsum/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/bundles/tools.vitruv.testutils.vsum/.project b/bundles/tools.vitruv.testutils.vsum/.project deleted file mode 100644 index 15645db9b4..0000000000 --- a/bundles/tools.vitruv.testutils.vsum/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - tools.vitruv.testutils.vsum - - - - - - org.eclipse.xtext.ui.shared.xtextBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - org.eclipse.xtext.ui.shared.xtextNature - - diff --git a/bundles/tools.vitruv.testutils.vsum/.settings/org.eclipse.core.resources.prefs b/bundles/tools.vitruv.testutils.vsum/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 99f26c0203..0000000000 --- a/bundles/tools.vitruv.testutils.vsum/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -encoding/=UTF-8 diff --git a/bundles/tools.vitruv.testutils.vsum/.settings/org.eclipse.xtend.core.Xtend.prefs b/bundles/tools.vitruv.testutils.vsum/.settings/org.eclipse.xtend.core.Xtend.prefs deleted file mode 100644 index 4c6a5c4c83..0000000000 --- a/bundles/tools.vitruv.testutils.vsum/.settings/org.eclipse.xtend.core.Xtend.prefs +++ /dev/null @@ -1,3 +0,0 @@ -ValidatorConfiguration.is_project_specific=true -eclipse.preferences.version=1 -org.eclipse.xtext.xbase.validation.IssueCodes.discouraged_reference=info diff --git a/bundles/tools.vitruv.testutils.vsum/META-INF/MANIFEST.MF b/bundles/tools.vitruv.testutils.vsum/META-INF/MANIFEST.MF deleted file mode 100644 index 6d62fbad54..0000000000 --- a/bundles/tools.vitruv.testutils.vsum/META-INF/MANIFEST.MF +++ /dev/null @@ -1,21 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Vitruv V-SUM Testutils -Bundle-SymbolicName: tools.vitruv.testutils.vsum -Bundle-Version: 3.0.1.qualifier -Bundle-Vendor: tools.vitruv -Automatic-Module-Name: tools.vitruv.testutils.vsum -Bundle-RequiredExecutionEnvironment: JavaSE-17 -Import-Package: org.junit.jupiter.api, - org.junit.jupiter.api.extension -Require-Bundle: com.google.guava, - org.eclipse.xtext.xbase.lib, - org.eclipse.xtend.lib, - org.eclipse.xtend.lib.macro, - org.hamcrest.core, - tools.vitruv.testutils;visibility:=reexport, - tools.vitruv.framework.vsum;visibility:=reexport, - edu.kit.ipd.sdq.activextendannotations, - edu.kit.ipd.sdq.commons.util.emf, - edu.kit.ipd.sdq.commons.util.java -Export-Package: tools.vitruv.testutils diff --git a/bundles/tools.vitruv.testutils.vsum/build.properties b/bundles/tools.vitruv.testutils.vsum/build.properties deleted file mode 100644 index 3c36c1d7e3..0000000000 --- a/bundles/tools.vitruv.testutils.vsum/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -source.. = src/,\ - xtend-gen/ -output.. = target/classes/ -bin.includes = META-INF/,\ - . diff --git a/features/tools.vitruv.framework.vsum.feature/.project b/features/tools.vitruv.framework.vsum.feature/.project deleted file mode 100644 index f525ebcb33..0000000000 --- a/features/tools.vitruv.framework.vsum.feature/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - tools.vitruv.framework.vsum.feature - - - - - - org.eclipse.pde.FeatureBuilder - - - - - - org.eclipse.pde.FeatureNature - - diff --git a/features/tools.vitruv.framework.vsum.feature/.settings/org.eclipse.core.resources.prefs b/features/tools.vitruv.framework.vsum.feature/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 99f26c0203..0000000000 --- a/features/tools.vitruv.framework.vsum.feature/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -encoding/=UTF-8 diff --git a/features/tools.vitruv.framework.vsum.feature/build.properties b/features/tools.vitruv.framework.vsum.feature/build.properties deleted file mode 100644 index b3a611b5c9..0000000000 --- a/features/tools.vitruv.framework.vsum.feature/build.properties +++ /dev/null @@ -1,2 +0,0 @@ -bin.includes = feature.xml,\ - feature.properties diff --git a/features/tools.vitruv.framework.vsum.feature/feature.properties b/features/tools.vitruv.framework.vsum.feature/feature.properties deleted file mode 100644 index a1b90d7780..0000000000 --- a/features/tools.vitruv.framework.vsum.feature/feature.properties +++ /dev/null @@ -1,7 +0,0 @@ -featureName=Vitruv V-SUM Framework -description=The Vitruv V-SUM framework providing the essential view-based features of Vitruv -providerName=vitruv.tools -copyrightURL=http://vitruv.tools -copyright=\ -Copyright (c) 2014-2022 Vitruv Tools Team\n\ -Karlsruhe Institute of Technology (KIT), Karlsruhe, Germany \ No newline at end of file diff --git a/features/tools.vitruv.framework.vsum.feature/feature.xml b/features/tools.vitruv.framework.vsum.feature/feature.xml deleted file mode 100644 index ed46d93a83..0000000000 --- a/features/tools.vitruv.framework.vsum.feature/feature.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - %description - - - - %copyright - - - - %license - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/features/tools.vitruv.testutils.vsum.feature/.project b/features/tools.vitruv.testutils.vsum.feature/.project deleted file mode 100644 index 5b7ef72c11..0000000000 --- a/features/tools.vitruv.testutils.vsum.feature/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - tools.vitruv.testutils.vsum.feature - - - - - - org.eclipse.pde.FeatureBuilder - - - - - - org.eclipse.pde.FeatureNature - - diff --git a/features/tools.vitruv.testutils.vsum.feature/.settings/org.eclipse.core.resources.prefs b/features/tools.vitruv.testutils.vsum.feature/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 99f26c0203..0000000000 --- a/features/tools.vitruv.testutils.vsum.feature/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -encoding/=UTF-8 diff --git a/features/tools.vitruv.testutils.vsum.feature/build.properties b/features/tools.vitruv.testutils.vsum.feature/build.properties deleted file mode 100644 index b3a611b5c9..0000000000 --- a/features/tools.vitruv.testutils.vsum.feature/build.properties +++ /dev/null @@ -1,2 +0,0 @@ -bin.includes = feature.xml,\ - feature.properties diff --git a/features/tools.vitruv.testutils.vsum.feature/feature.properties b/features/tools.vitruv.testutils.vsum.feature/feature.properties deleted file mode 100644 index 53264726db..0000000000 --- a/features/tools.vitruv.testutils.vsum.feature/feature.properties +++ /dev/null @@ -1,7 +0,0 @@ -featureName=Vitruv V-SUM Test Utilities -description=Utilities for defining tests for V-SUMs in the Vitruv framework -providerName=vitruv.tools -copyrightURL=http://vitruv.tools -copyright=\ -Copyright (c) 2014-2022 Vitruv Tools Team\n\ -Karlsruhe Institute of Technology (KIT), Karlsruhe, Germany \ No newline at end of file diff --git a/features/tools.vitruv.testutils.vsum.feature/feature.xml b/features/tools.vitruv.testutils.vsum.feature/feature.xml deleted file mode 100644 index 1cc48c9717..0000000000 --- a/features/tools.vitruv.testutils.vsum.feature/feature.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - %description - - - - %copyright - - - - %license - - - - - - - - - - - - - - - - - - - diff --git a/pom.xml b/pom.xml index a82daa70fe..e42fda98e9 100644 --- a/pom.xml +++ b/pom.xml @@ -1,22 +1,304 @@ - - - 4.0.0 - - tools.vitruv - framework-parent - 3.0.1-SNAPSHOT - releng/tools.vitruv.parent - - tools.vitruv - pom - - - bundles - tests - features - releng/tools.vitruv.updatesite - + + 4.0.0 + + + + tools.vitruv + parent + 3.0.0-SNAPSHOT + + + + tools.vitruv.framework + 3.0.1-SNAPSHOT + pom + + + Vitruv Framework + The Vitruv framework providing the definition of views and v-sums. + + + + Eclipse Public License - v 1.0 + + + + + + testutils + views + + + + + 3.3 + 2.2.0 + 1.6.0 + + + + + + org.openntf.maven + p2-layout-resolver + + + + + + + + central + Maven Central + https://repo1.maven.org/maven2/ + + false + + + + + + emf-compare + EMF Compare + p2 + https://download.eclipse.org/modeling/emf/compare/updates/releases/${repo.emf-compare.version} + + + sdq-commons + SDQ Commons + https://kit-sdq.github.io/updatesite/release/commons/${repo.sdq-commons.version} + p2 + + + xannotations + XAnnotations + p2 + https://kit-sdq.github.io/updatesite/release/xannotations/${repo.xannotations.version} + + + + + + + + + + tools.vitruv + tools.vitruv.change.correspondence + 3.0.1-SNAPSHOT + + + tools.vitruv + tools.vitruv.change.composite + 3.0.1-SNAPSHOT + + + tools.vitruv + tools.vitruv.change.interaction + 3.0.1-SNAPSHOT + + + tools.vitruv + tools.vitruv.change.propagation + 3.0.1-SNAPSHOT + + + tools.vitruv + tools.vitruv.change.changederivation + 3.0.1-SNAPSHOT + + + + ch.qos.logback + logback-classic + 1.4.11 + + + ch.qos.logback + logback-core + 1.4.11 + + + com.google.guava + guava + 30.1-jre + + + emf-compare + org.eclipse.emf.compare + 3.5.3.202212280858 + + + log4j + log4j + 1.2.17 + + + org.eclipse.emf + org.eclipse.emf.common + 2.29.0 + + + org.eclipse.emf + org.eclipse.emf.ecore + 2.35.0 + + + org.eclipse.emf + org.eclipse.emf.ecore.change + 2.15.0 + + + org.eclipse.emf + org.eclipse.emf.ecore.xmi + 2.36.0 + + + org.eclipse.emf + org.eclipse.emf.edit + 2.19.0 + + + org.eclipse.platform + org.eclipse.core.resources + 3.19.100 + + + org.eclipse.platform + org.eclipse.core.runtime + 3.29.0 + + + org.eclipse.platform + org.eclipse.equinox.common + 3.18.100 + + + org.eclipse.platform + org.eclipse.jface + 3.31.0 + + + + org.eclipse.platform + org.eclipse.swt + + + + + org.eclipse.platform + org.eclipse.swt.${swt.platform} + 3.124.100 + + + + org.eclipse.platform + org.eclipse.swt + + + + + org.eclipse.platform + org.eclipse.ui.workbench + 3.130.0 + + + + org.eclipse.platform + org.eclipse.swt + + + + + org.eclipse.xtend + org.eclipse.xtend.lib + 2.32.0 + + + org.eclipse.xtend + org.eclipse.xtend.lib.macro + 2.32.0 + + + org.eclipse.xtext + org.eclipse.xtext.xbase.lib + 2.32.0 + + + org.hamcrest + hamcrest + 2.2 + + + org.junit.jupiter + junit-jupiter-api + 5.10.1 + + + org.junit.jupiter + junit-jupiter-params + 5.10.1 + + + org.junit.platform + junit-platform-commons + 1.10.1 + + + org.slf4j + slf4j-api + 2.0.9 + + + sdq-commons + edu.kit.ipd.sdq.commons.util.eclipse + 2.3.0.202304271319 + + + + * + * + + + + + sdq-commons + edu.kit.ipd.sdq.commons.util.emf + 2.3.0.202304271319 + + + + * + * + + + + + sdq-commons + edu.kit.ipd.sdq.commons.util.java + 2.3.0.202304271319 + + + + * + * + + + + + xannotations + edu.kit.ipd.sdq.activextendannotations + 1.6.0 + + + org.mockito + mockito-core + 5.12.0 + test + + + diff --git a/releng/tools.vitruv.parent/pom.xml b/releng/tools.vitruv.parent/pom.xml deleted file mode 100644 index d2f77a3362..0000000000 --- a/releng/tools.vitruv.parent/pom.xml +++ /dev/null @@ -1,90 +0,0 @@ - - - - 4.0.0 - - tools.vitruv - parent - 2.1.5 - - framework-parent - 3.0.1-SNAPSHOT - pom - - - - https://vitruv-tools.github.io/updatesite/nightly/change - - - - - - Vitruv Change - p2 - ${vitruv.change.url} - - - Demo Metamodels - p2 - https://kit-sdq.github.io/updatesite/release/metamodels/demo/${sdq.demometamodels.version} - - - SDQ Commons - p2 - https://kit-sdq.github.io/updatesite/release/commons/${sdq.commons.version} - - - XAnnotations - p2 - https://kit-sdq.github.io/updatesite/release/xannotations/${sdq.xannotations.version} - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - ${project.build.directory}/work/data - - - - - org.apache.maven.plugins - maven-dependency-plugin - 3.6.0 - - - copy-dependencies - - copy-dependencies - - - true - - - - - - - - - - - local-change - - - vitruv.change.path - - - - file:///${vitruv.change.path}/releng/tools.vitruv.change.updatesite/target/repository - - - - - diff --git a/releng/tools.vitruv.updatesite/.project b/releng/tools.vitruv.updatesite/.project deleted file mode 100644 index df346ecb5e..0000000000 --- a/releng/tools.vitruv.updatesite/.project +++ /dev/null @@ -1,11 +0,0 @@ - - - tools.vitruv.updatesite - - - - - - - - diff --git a/releng/tools.vitruv.updatesite/.settings/org.eclipse.core.resources.prefs b/releng/tools.vitruv.updatesite/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 99f26c0203..0000000000 --- a/releng/tools.vitruv.updatesite/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -encoding/=UTF-8 diff --git a/releng/tools.vitruv.updatesite/category.xml b/releng/tools.vitruv.updatesite/category.xml deleted file mode 100644 index 93e9df1775..0000000000 --- a/releng/tools.vitruv.updatesite/category.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - The Vitruv framework, including the essential V-SUM and optional extensions - - - diff --git a/releng/tools.vitruv.updatesite/pom.xml b/releng/tools.vitruv.updatesite/pom.xml deleted file mode 100644 index 9c293272ec..0000000000 --- a/releng/tools.vitruv.updatesite/pom.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - 4.0.0 - - tools.vitruv - framework-parent - 3.0.1-SNAPSHOT - ../tools.vitruv.parent - - - tools.vitruv.updatesite - Vitruv Framework Update Site - eclipse-repository - - \ No newline at end of file diff --git a/tests/tools.vitruv.framework.views.tests/.classpath b/tests/tools.vitruv.framework.views.tests/.classpath deleted file mode 100644 index f136b38310..0000000000 --- a/tests/tools.vitruv.framework.views.tests/.classpath +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/tests/tools.vitruv.framework.views.tests/.project b/tests/tools.vitruv.framework.views.tests/.project deleted file mode 100644 index 43bbaf1914..0000000000 --- a/tests/tools.vitruv.framework.views.tests/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - tools.vitruv.framework.views.tests - - - - - - org.eclipse.xtext.ui.shared.xtextBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - org.eclipse.xtext.ui.shared.xtextNature - - diff --git a/tests/tools.vitruv.framework.views.tests/.settings/org.eclipse.core.resources.prefs b/tests/tools.vitruv.framework.views.tests/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 99f26c0203..0000000000 --- a/tests/tools.vitruv.framework.views.tests/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -encoding/=UTF-8 diff --git a/tests/tools.vitruv.framework.views.tests/META-INF/MANIFEST.MF b/tests/tools.vitruv.framework.views.tests/META-INF/MANIFEST.MF deleted file mode 100644 index 212b391edf..0000000000 --- a/tests/tools.vitruv.framework.views.tests/META-INF/MANIFEST.MF +++ /dev/null @@ -1,25 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Vitruv Framwork Views Tests -Bundle-SymbolicName: tools.vitruv.framework.views.tests;singleton:=true -Automatic-Module-Name: tools.vitruv.framework.views.tests -Bundle-Version: 3.0.1.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-17 -Fragment-Host: tools.vitruv.framework.views -Import-Package: org.junit.jupiter.api, - org.junit.jupiter.api.extension, - org.junit.jupiter.api.function, - org.junit.jupiter.params, - org.junit.jupiter.params.provider -Require-Bundle: org.apache.log4j, - tools.vitruv.testutils.vsum, - tools.vitruv.testutils.metamodels, - org.eclipse.core.expressions, - org.eclipse.xtend.lib, - org.eclipse.uml2.uml, - org.hamcrest.core, - edu.kit.ipd.sdq.commons.util.java -Bundle-ClassPath: ., - target/dependency/mockito-core.jar -Bundle-ActivationPolicy: lazy -Bundle-Vendor: vitruv.tools diff --git a/tests/tools.vitruv.framework.views.tests/build.properties b/tests/tools.vitruv.framework.views.tests/build.properties deleted file mode 100644 index 5a0ec391e1..0000000000 --- a/tests/tools.vitruv.framework.views.tests/build.properties +++ /dev/null @@ -1,6 +0,0 @@ -bin.includes = .,\ - META-INF/,\ - target/dependency/ -source.. = src/,\ - xtend-gen -output.. = target/classes/ diff --git a/tests/tools.vitruv.framework.views.tests/pom.xml b/tests/tools.vitruv.framework.views.tests/pom.xml deleted file mode 100644 index 3512bdf344..0000000000 --- a/tests/tools.vitruv.framework.views.tests/pom.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - 4.0.0 - - tools.vitruv - tests - 3.0.1-SNAPSHOT - ../.polyglot.pom.tycho - - tools.vitruv.framework.views.tests - eclipse-test-plugin - [test-bundle] Vitruv Framwork Views Tests - - - - - org.mockito - mockito-core - 4.2.0 - test - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - - \ No newline at end of file diff --git a/tests/tools.vitruv.framework.vsum.tests/.classpath b/tests/tools.vitruv.framework.vsum.tests/.classpath deleted file mode 100644 index 2d43d32ef7..0000000000 --- a/tests/tools.vitruv.framework.vsum.tests/.classpath +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/tests/tools.vitruv.framework.vsum.tests/.project b/tests/tools.vitruv.framework.vsum.tests/.project deleted file mode 100644 index d83747898c..0000000000 --- a/tests/tools.vitruv.framework.vsum.tests/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - tools.vitruv.framework.vsum.tests - - - - - - org.eclipse.xtext.ui.shared.xtextBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - org.eclipse.xtext.ui.shared.xtextNature - - diff --git a/tests/tools.vitruv.framework.vsum.tests/.settings/org.eclipse.core.resources.prefs b/tests/tools.vitruv.framework.vsum.tests/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 99f26c0203..0000000000 --- a/tests/tools.vitruv.framework.vsum.tests/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -encoding/=UTF-8 diff --git a/tests/tools.vitruv.framework.vsum.tests/META-INF/MANIFEST.MF b/tests/tools.vitruv.framework.vsum.tests/META-INF/MANIFEST.MF deleted file mode 100644 index f7f6ca05bc..0000000000 --- a/tests/tools.vitruv.framework.vsum.tests/META-INF/MANIFEST.MF +++ /dev/null @@ -1,21 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Vitruv Framwork Virtual Single Underlying Model Tests -Bundle-SymbolicName: tools.vitruv.framework.vsum.tests;singleton:=true -Automatic-Module-Name: tools.vitruv.framework.vsum.tests -Bundle-Version: 3.0.1.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-17 -Fragment-Host: tools.vitruv.framework.vsum -Import-Package: org.junit.jupiter.api, - org.junit.jupiter.api.extension -Require-Bundle: org.apache.log4j, - tools.vitruv.testutils.vsum, - tools.vitruv.testutils.metamodels, - org.eclipse.core.expressions, - org.eclipse.xtend.lib, - org.eclipse.uml2.uml, - org.hamcrest.core, - edu.kit.ipd.sdq.commons.util.java, - edu.kit.ipd.sdq.commons.util.emf -Bundle-ActivationPolicy: lazy -Bundle-Vendor: vitruv.tools diff --git a/tests/tools.vitruv.framework.vsum.tests/build.properties b/tests/tools.vitruv.framework.vsum.tests/build.properties deleted file mode 100644 index 133eae807e..0000000000 --- a/tests/tools.vitruv.framework.vsum.tests/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -# - -bin.includes = .,\ - META-INF/ -source.. = src/,\ - xtend-gen -output.. = target/classes/ diff --git a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend b/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend deleted file mode 100644 index a90f4b059b..0000000000 --- a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend +++ /dev/null @@ -1,396 +0,0 @@ -package tools.vitruv.framework.vsum - -import allElementTypes.Root -import java.nio.file.Path -import java.util.HashSet -import org.eclipse.emf.ecore.resource.ResourceSet -import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.^extension.ExtendWith -import tools.vitruv.change.atomic.eobject.CreateEObject -import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute -import tools.vitruv.change.atomic.feature.reference.ReplaceSingleValuedEReference -import tools.vitruv.change.atomic.root.InsertRootEObject -import tools.vitruv.change.atomic.uuid.UuidResolver -import tools.vitruv.change.composite.description.VitruviusChangeResolver -import tools.vitruv.change.composite.recording.ChangeRecorder -import tools.vitruv.framework.views.View -import tools.vitruv.framework.views.ViewTypeFactory -import tools.vitruv.framework.vsum.VirtualModelTestUtil.RedundancyChangePropagationSpecification -import tools.vitruv.testutils.TestProject -import tools.vitruv.testutils.TestProjectManager - -import static org.hamcrest.CoreMatchers.* -import static org.hamcrest.MatcherAssert.assertThat -import static org.junit.jupiter.api.Assertions.assertEquals -import static org.junit.jupiter.api.Assertions.assertNotEquals -import static org.junit.jupiter.api.Assertions.assertNull -import static tools.vitruv.testutils.matchers.ModelMatchers.containsModelOf -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet - -import static extension com.google.common.base.Preconditions.checkNotNull -import static extension edu.kit.ipd.sdq.commons.util.java.lang.IterableUtil.claimOne -import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories -import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceUtil.getFirstRootEObject -import static extension tools.vitruv.framework.vsum.VirtualModelTestUtil.* - -@ExtendWith(TestProjectManager) -class VirtualModelTest { - static val String NON_ROOT_ID = "NonRootId" - static val String ROOT_ID = "RootId" - - var Path projectFolder - - @BeforeEach - def void initializeProjectFolder(@TestProject Path projectFolder) { - this.projectFolder = projectFolder - } - - @Test - @DisplayName("propagate a simple change into a virtual model") - def void propagateIntoVirtualModel() { - val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) - val resourceSet = new ResourceSetImpl().withGlobalFactories - val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet) - changeRecorder.addToRecording(resourceSet) - changeRecorder.beginRecording - val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ - contents += aet.Root => [ - id = 'root' - ] - ] - val recordedChange = changeRecorder.endRecording(uuidResolver) - virtualModel.propagateChange(recordedChange) - val vsumModel = virtualModel.getModelInstance(createTestModelResourceUri("")) - assertThat(vsumModel.resource, containsModelOf(monitoredResource)) - } - - @Test - @DisplayName("propagate a simple change into a virtual model and preserve consistency") - def void propagateIntoVirtualModelWithConsistency() { - val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) - val resourceSet = new ResourceSetImpl().withGlobalFactories - val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet) - changeRecorder.addToRecording(resourceSet) - changeRecorder.beginRecording - val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ - contents += aet.Root => [ - id = 'root' - ] - ] - val recordedChange = changeRecorder.endRecording(uuidResolver) - virtualModel.propagateChange(recordedChange) - val sorceModel = virtualModel.getModelInstance(createTestModelResourceUri("")) - val targetModel = virtualModel.getModelInstance( - RedundancyChangePropagationSpecification.getTargetResourceUri(createTestModelResourceUri(""))) - assertThat(targetModel.resource, containsModelOf(monitoredResource)) - assertEquals(1, - virtualModel.correspondenceModel.getCorrespondingEObjects(sorceModel.resource.contents.get(0)).size) - } - - @Test - @DisplayName("persist element as resource root also contained in other persisted element") - def void singleChangeForRootElementInMultipleResource() { - val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) - val resourceSet = new ResourceSetImpl().withGlobalFactories - val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet) - changeRecorder.addToRecording(resourceSet) - changeRecorder.beginRecording - val containedRoot = aet.Root - resourceSet.createResource(createTestModelResourceUri("")) => [ - contents += aet.Root => [ - id = 'root' - recursiveRoot = containedRoot => [ - id = 'containedRoot' - ] - ] - ] - resourceSet.createResource(createTestModelResourceUri("Contained")) => [ - contents += containedRoot - ] - val recordedChange = changeRecorder.endRecording(uuidResolver) - val propagatedChanges = virtualModel.propagateChange(recordedChange) - val consequentialChanges = propagatedChanges.map[consequentialChanges.EChanges].flatten - assertEquals(2, consequentialChanges.filter(CreateEObject).size) - assertEquals(2, consequentialChanges.filter(InsertRootEObject).size) - assertEquals(1, consequentialChanges.filter(ReplaceSingleValuedEReference).size) - assertEquals(2, consequentialChanges.filter(ReplaceSingleValuedEAttribute).size) - assertEquals(7, consequentialChanges.size) - } - - @Test - @DisplayName("add element to containment of element persisted in two resources") - def void singleChangeForElementContainedInRootElementInMultipleResource() { - val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) - val resourceSet = new ResourceSetImpl().withGlobalFactories - val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet) - changeRecorder.addToRecording(resourceSet) - changeRecorder.beginRecording - val containedRoot = aet.Root - val containedInContainedRoot = aet.Root - resourceSet.createResource(createTestModelResourceUri("")) => [ - contents += aet.Root => [ - id = 'root' - recursiveRoot = containedRoot => [ - id = 'containedRoot' - recursiveRoot = containedInContainedRoot => [ - id = 'containedInContained' - ] - ] - ] - ] - resourceSet.createResource(createTestModelResourceUri("Contained")) => [ - contents += containedRoot - ] - resourceSet.createResource(createTestModelResourceUri("ContainedInContained")) => [ - contents += containedInContainedRoot - ] - val recordedChange = changeRecorder.endRecording(uuidResolver) - val propagatedChanges = virtualModel.propagateChange(recordedChange) - val consequentialChanges = propagatedChanges.map[consequentialChanges.EChanges].flatten - assertEquals(3, consequentialChanges.filter(CreateEObject).size) - assertEquals(3, consequentialChanges.filter(InsertRootEObject).size) - assertEquals(2, consequentialChanges.filter(ReplaceSingleValuedEReference).size) - assertEquals(3, consequentialChanges.filter(ReplaceSingleValuedEAttribute).size) - assertEquals(11, consequentialChanges.size) - } - - @Test - @DisplayName("load resource that should have been saved after propagating a change into a virtual model") - def void savedVirtualModel() { - val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) - val resourceSet = new ResourceSetImpl().withGlobalFactories - val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet) - changeRecorder.addToRecording(resourceSet) - changeRecorder.beginRecording - val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ - contents += aet.Root => [ - id = 'root' - ] - ] - val recordedChange = changeRecorder.endRecording(uuidResolver) - virtualModel.propagateChange(recordedChange) - val reloadedResource = new ResourceSetImpl().withGlobalFactories.getResource(createTestModelResourceUri(""), - true) - assertThat(reloadedResource, containsModelOf(monitoredResource)) - } - - @Test - @DisplayName("reload a virtual model to which a simple change was propagated") - def void reloadVirtualModel() { - val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) - val resourceSet = new ResourceSetImpl().withGlobalFactories - val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet) - changeRecorder.addToRecording(resourceSet) - changeRecorder.beginRecording - val root = aet.Root - val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ - contents += root => [ - id = 'root' - ] - ] - val recordedChange = changeRecorder.endRecording(uuidResolver) - virtualModel.propagateChange(recordedChange) - val originalModel = virtualModel.getModelInstance(createTestModelResourceUri("")) - val reloadedVirtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) - val reloadedModel = reloadedVirtualModel.getModelInstance(createTestModelResourceUri("")) - assertThat(reloadedModel.resource, containsModelOf(monitoredResource)) - assertNotEquals(originalModel, reloadedModel) - // Propagate another change to reloaded virtual model to ensure that everything is loaded correctly - changeRecorder.beginRecording - root.singleValuedEAttribute = 1 - val secondRecordedChange = changeRecorder.endRecording(uuidResolver) - val propagatedChange = reloadedVirtualModel.propagateChange(secondRecordedChange) - assertEquals(1, propagatedChange.size) - } - - @Test - @DisplayName("reload a virtual model with consistency preservation to which a simple change was propagated") - def void reloadVirtualModelWithConsistency() { - val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) - val resourceSet = new ResourceSetImpl().withGlobalFactories - val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet) - changeRecorder.addToRecording(resourceSet) - changeRecorder.beginRecording - val root = aet.Root - val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ - contents += root => [ - id = 'root' - ] - ] - val recordedChange = changeRecorder.endRecording(uuidResolver) - virtualModel.propagateChange(recordedChange) - val originalModel = virtualModel.getModelInstance(createTestModelResourceUri("")) - val reloadedVirtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) - val reloadedModel = reloadedVirtualModel.getModelInstance(createTestModelResourceUri("")) - assertThat(reloadedModel.resource, containsModelOf(monitoredResource)) - assertNotEquals(originalModel, reloadedModel) - val reloadedTargetModel = reloadedVirtualModel.getModelInstance( - RedundancyChangePropagationSpecification.getTargetResourceUri(createTestModelResourceUri(""))) - assertThat(reloadedTargetModel.resource, containsModelOf(monitoredResource)) - assertEquals(1, - reloadedVirtualModel.correspondenceModel.getCorrespondingEObjects(reloadedModel.resource.contents.get(0)). - size) - } - - @Test - @DisplayName("move element such that corresponding element is moved from one resource to another and back") - def void moveCorrespondingToOtherResourceAndBack() { - val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) - val resourceSet = new ResourceSetImpl().withGlobalFactories - val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet) - changeRecorder.addToRecording(resourceSet) - changeRecorder.beginRecording - val root = aet.Root - val testUri = createTestModelResourceUri("") - val monitoredResource = resourceSet.createResource(testUri) => [ - contents += root => [ - id = 'root' - ] - ] - virtualModel.propagateChange(changeRecorder.endRecording(uuidResolver)) - changeRecorder.beginRecording - val testIntermediateUri = createTestModelResourceUri("intermediate") - resourceSet.createResource(testIntermediateUri) => [ - contents += root - ] - virtualModel.propagateChange(changeRecorder.endRecording(uuidResolver)) - // There must not be the old and the old corresponding model - assertNull(virtualModel.getModelInstance(testUri)) - assertNull( - virtualModel.getModelInstance(RedundancyChangePropagationSpecification.getTargetResourceUri(testUri))) - changeRecorder.beginRecording - monitoredResource => [ - contents += root - ] - virtualModel.propagateChange(changeRecorder.endRecording(uuidResolver)) - assertNull(virtualModel.getModelInstance(testIntermediateUri)) - assertNull( - virtualModel.getModelInstance( - RedundancyChangePropagationSpecification.getTargetResourceUri(testIntermediateUri))) - } - - @Test - @DisplayName("create a view for a virtual model") - def void createView() { - val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) - val resourceSet = new ResourceSetImpl().withGlobalFactories - val uuidResolver = UuidResolver.create(resourceSet) - virtualModel.createAndPropagateRoot(resourceSet, uuidResolver, ROOT_ID) - val testView = virtualModel.createTestView - // Check initial state: - assertThat(new HashSet(testView.rootObjects), not(is(emptySet()))) - assertEquals(testView.rootObjects.claimOne, testView.getRootObjects(Root).claimOne) - assertThat(testView.getRootObjects(Root).claimOne.id, is(ROOT_ID)) - assertThat("view source must not have been changed", !testView.outdated) - assertThat("view must not have been modified", !testView.isModified) - } - - @Test - @DisplayName("update view after a change in the virtual model") - def void updateView() { - val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) - val resourceSet = new ResourceSetImpl().withGlobalFactories - val uuidResolver = UuidResolver.create(resourceSet) - virtualModel.createAndPropagateRoot(resourceSet, uuidResolver, ROOT_ID) - val testView = virtualModel.createTestView - - // Modify model - virtualModel.propagateChange(resourceSet.recordChanges(uuidResolver) [ - val resource = resourceSet.resources.claimOne - resource.firstRootEObject as Root => [ - multiValuedContainmentEReference += aet.NonRoot => [ - id = NON_ROOT_ID - ] - ] - ]) - - // Assert VSUM changed but view not modified: - assertThat("view source must have been changed", testView.outdated) - assertThat("view must not have been modified", !testView.isModified) - assertThat(testView.getRootObjects(Root).claimOne.multiValuedContainmentEReference, is(emptyList())) - - // Update view and assert view was updated correctly - testView.update() - assertThat("view source must not have been changed", !testView.outdated) - assertThat("view must not have been modified", !testView.isModified) - val viewRoot = testView.getRootObjects(Root).claimOne - assertThat(viewRoot.multiValuedContainmentEReference.claimOne.id, is(NON_ROOT_ID)) - } - - @Test - @DisplayName("change view and commit changes") - def void commitView() { - val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) - val resourceSet = new ResourceSetImpl().withGlobalFactories - val uuidResolver = UuidResolver.create(resourceSet) - virtualModel.createAndPropagateRoot(resourceSet, uuidResolver, ROOT_ID) - val testView = virtualModel.createTestView.withChangeRecordingTrait - - // Modify view: - assertThat("view must not have been modified", !testView.isModified) - testView.getRootObjects(Root).claimOne => [ - multiValuedContainmentEReference += aet.NonRoot => [ - id = NON_ROOT_ID - ] - ] - - // Assert view modified but VSUM not changed: - assertThat("view source must not have been changed", !testView.outdated) - assertThat("view must have been modified", testView.isModified) - - // Commit changes and assert VSUM was updated correctly - testView.commitChanges() - assertThat("view source must have been changed", testView.outdated) - assertThat("view must not have been modified", !testView.isModified) - - testView.update(); - assertThat("view source must not have been changed", !testView.outdated) - assertThat("view must not have been modified", !testView.isModified) - - val reopenedViewRoot = virtualModel.createTestView.getRootObjects(Root).claimOne - assertThat(reopenedViewRoot.multiValuedContainmentEReference.claimOne.id, is(NON_ROOT_ID)) - } - - private def createTestModelResourceUri(String suffix) { - projectFolder.createTestModelResourceUri(suffix) - } - - private def getPathToVirtualModelProjectFolder() { - projectFolder.resolve("vsum") - } - - private def endRecording(ChangeRecorder changeRecorder, UuidResolver uuidResolver) { - val change = changeRecorder.endRecording - val changeResolver = VitruviusChangeResolver.forUuids(uuidResolver) - return changeResolver.assignIds(change) - } - - def private createAndPropagateRoot(VirtualModel virtualModel, ResourceSet resourceSet, UuidResolver uuidResolver, String rootId) { - virtualModel.propagateChange(resourceSet.recordChanges(uuidResolver) [ - resourceSet.createResource(projectFolder.createTestModelResourceUri("")) => [ - contents += aet.Root => [ - id = rootId - ] - ] - ]) - } - - def private static View createTestView(VirtualModel virtualModel) { - val viewType = ViewTypeFactory.createIdentityMappingViewType("").checkNotNull("cannot create view type") - val selector = virtualModel.createSelector(viewType).checkNotNull("cannot create selector") - selector.selectableElements.forEach[selector.setSelected(it, true)] - return selector.createView.checkNotNull("Cannot create view from selector!") - } - -} diff --git a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend b/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend deleted file mode 100644 index 6eb6031b19..0000000000 --- a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend +++ /dev/null @@ -1,128 +0,0 @@ -package tools.vitruv.framework.vsum - -import allElementTypes.AllElementTypesPackage -import allElementTypes.Root -import edu.kit.ipd.sdq.activextendannotations.Utility -import java.nio.file.Path -import org.eclipse.emf.common.util.URI -import org.eclipse.emf.ecore.EObject -import org.eclipse.emf.ecore.resource.ResourceSet -import tools.vitruv.change.atomic.EChange -import tools.vitruv.change.atomic.root.InsertRootEObject -import tools.vitruv.change.atomic.uuid.Uuid -import tools.vitruv.change.atomic.uuid.UuidResolver -import tools.vitruv.change.composite.MetamodelDescriptor -import tools.vitruv.change.composite.description.VitruviusChange -import tools.vitruv.change.composite.description.VitruviusChangeResolver -import tools.vitruv.change.composite.recording.ChangeRecorder -import tools.vitruv.change.correspondence.Correspondence -import tools.vitruv.change.correspondence.view.EditableCorrespondenceModelView -import tools.vitruv.change.interaction.UserInteractionFactory -import tools.vitruv.change.propagation.ResourceAccess -import tools.vitruv.change.propagation.impl.AbstractChangePropagationSpecification - -import static org.junit.jupiter.api.Assertions.assertEquals -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet - -/** - * Utility methods for the VSUM and view test cases. - */ -@Utility -class VirtualModelTestUtil { - - /** - * Create a recorder, start recording a resource set, apply changes, stop and return the recorded changes. - */ - def static VitruviusChange recordChanges(ResourceSet resourceSet, UuidResolver uuidResolver, Runnable changesToPerform) { - val recorder = new ChangeRecorder(resourceSet) - val changeResolver = VitruviusChangeResolver.forUuids(uuidResolver) - recorder.addToRecording(resourceSet) - recorder.beginRecording - changesToPerform.run() - val result = recorder.endRecording - val resolvedChange = changeResolver.assignIds(result) - recorder.close - return resolvedChange - } - - /** - * Creates an empty virtual model without a change propagation specification. - */ - static def createAndLoadTestVirtualModel(Path folder) { - return new VirtualModelBuilder().withStorageFolder(folder).withUserInteractor( - UserInteractionFactory.instance.createUserInteractor( - UserInteractionFactory.instance.createPredefinedInteractionResultProvider(null))).buildAndInitialize() - } - - /** - * Creates an empty virtual model with a {@link RedundancyChangePropagationSpecification}. - */ - static def createAndLoadTestVirtualModelWithConsistencyPreservation(Path folder) { - return new VirtualModelBuilder().withStorageFolder(folder). - withChangePropagationSpecification(new RedundancyChangePropagationSpecification( - MetamodelDescriptor.with(AllElementTypesPackage.eNS_URI), MetamodelDescriptor.with(AllElementTypesPackage.eNS_URI) - )). - withUserInteractor(UserInteractionFactory.instance.createUserInteractor( - UserInteractionFactory.instance.createPredefinedInteractionResultProvider(null))).buildAndInitialize() - } - - /** - * Creates a model resource URI for a given project folder. Use the suffix to distinguish multiple URIs. - */ - static def createTestModelResourceUri(Path projectFolder, String suffix) { - URI.createFileURI(projectFolder.resolve("root" + suffix + ".allElementTypes").toString) - } - - static class RedundancyChangePropagationSpecification extends AbstractChangePropagationSpecification { - static def getTargetResourceUri(URI sourceUri) { - val sourceUriWithoutFileExtension = sourceUri.trimFileExtension.toFileString - val copySuffix = "Copy" - if (sourceUriWithoutFileExtension.endsWith(copySuffix)) { - val sourceUriWithoutSuffix = sourceUriWithoutFileExtension.substring(0, sourceUriWithoutFileExtension.length - copySuffix.length) - URI.createFileURI(sourceUriWithoutSuffix + "." + sourceUri.fileExtension) - } else { - URI.createFileURI(sourceUriWithoutFileExtension + copySuffix + "." + sourceUri.fileExtension) - } - } - - new(MetamodelDescriptor sourceMetamodelDescriptor, MetamodelDescriptor targetMetamodelDescriptor) { - super(sourceMetamodelDescriptor, targetMetamodelDescriptor) - } - - override doesHandleChange(EChange change, EditableCorrespondenceModelView correspondenceModel) { - if(change instanceof InsertRootEObject) { - return change.newValue instanceof Root - } - return false - } - - override propagateChange(EChange change, EditableCorrespondenceModelView correspondenceModel, - extension ResourceAccess resourceAccess) { - if(!doesHandleChange(change, correspondenceModel)) { - return - } - val typedChange = change as InsertRootEObject - val insertedRoot = typedChange.newValue as Root - // If there is a corresponding element, reuse it, otherwise create one - val correspondingRoots = correspondenceModel.getCorrespondingEObjects(insertedRoot).filter(Root) - val correspondingRoot = if(correspondingRoots.size == 1) { - correspondingRoots.get(0) - } else { - val newRoot = aet.Root => [ - id = insertedRoot.id - ] - correspondenceModel.addCorrespondenceBetween(insertedRoot, newRoot, null) - newRoot - } - - if(insertedRoot.eContainer !== null) { - val correspondingObjects = correspondenceModel.getCorrespondingEObjects(insertedRoot.eContainer, null).filter(Root) - assertEquals(1, correspondingObjects.size) - correspondingObjects.get(0).recursiveRoot = correspondingRoot - } - val resourceURI = typedChange.resource.URI - persistAsRoot(correspondingRoot, resourceURI.targetResourceUri) - } - } - -} diff --git a/testutils/pom.xml b/testutils/pom.xml new file mode 100644 index 0000000000..440fee65c4 --- /dev/null +++ b/testutils/pom.xml @@ -0,0 +1,23 @@ + + + + 4.0.0 + + + tools.vitruv + tools.vitruv.framework + 3.0.1-SNAPSHOT + + + tools.vitruv.framework.testutils + pom + + Vitruv Framework Test Utilities + Utilities for defining tests using the Vitruv Framework + + + vsum + + \ No newline at end of file diff --git a/testutils/vsum/pom.xml b/testutils/vsum/pom.xml new file mode 100644 index 0000000000..170d96ab60 --- /dev/null +++ b/testutils/vsum/pom.xml @@ -0,0 +1,66 @@ + + + + 4.0.0 + + + tools.vitruv + tools.vitruv.framework.testutils + 3.0.1-SNAPSHOT + + + tools.vitruv.framework.testutils.vsum + + Vitruv Framework Test Utilities + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + org.eclipse.xtend + xtend-maven-plugin + + + + + + + + ${project.groupId} + tools.vitruv.change.utils + ${project.version} + + + + + com.google.guava + guava + + + org.eclipse.emf + org.eclipse.emf.common + + + org.eclipse.emf + org.eclipse.emf.ecore + + + org.junit.jupiter + junit-jupiter-api + + + org.junit.jupiter + junit-jupiter-params + + + xannotations + edu.kit.ipd.sdq.activextendannotations + + + \ No newline at end of file diff --git a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/TestViewFactory.java b/testutils/vsum/src/main/java/tools/vitruv/framework/testutils/vsum/TestViewFactory.java similarity index 98% rename from bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/TestViewFactory.java rename to testutils/vsum/src/main/java/tools/vitruv/framework/testutils/vsum/TestViewFactory.java index 42854a80a8..09ce25de40 100644 --- a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/TestViewFactory.java +++ b/testutils/vsum/src/main/java/tools/vitruv/framework/testutils/vsum/TestViewFactory.java @@ -1,4 +1,4 @@ -package tools.vitruv.testutils; +package tools.vitruv.framework.testutils.vsum; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.not; diff --git a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/DefaultVirtualModelBasedTestView.xtend b/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/DefaultVirtualModelBasedTestView.xtend similarity index 97% rename from bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/DefaultVirtualModelBasedTestView.xtend rename to testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/DefaultVirtualModelBasedTestView.xtend index 21b0c31a11..62c75d5ba9 100644 --- a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/DefaultVirtualModelBasedTestView.xtend +++ b/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/DefaultVirtualModelBasedTestView.xtend @@ -1,4 +1,4 @@ -package tools.vitruv.testutils +package tools.vitruv.testutils.framework.vsum import java.nio.file.Path import org.eclipse.xtend.lib.annotations.Delegate diff --git a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyCorrespondenceRetriever.xtend b/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyCorrespondenceRetriever.xtend similarity index 94% rename from bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyCorrespondenceRetriever.xtend rename to testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyCorrespondenceRetriever.xtend index 32c55c86f4..2723b358e0 100644 --- a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyCorrespondenceRetriever.xtend +++ b/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyCorrespondenceRetriever.xtend @@ -1,4 +1,4 @@ -package tools.vitruv.testutils +package tools.vitruv.testutils.framework.vsum import org.eclipse.emf.ecore.EObject diff --git a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend b/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyVitruvApplicationTest.xtend similarity index 98% rename from bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend rename to testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyVitruvApplicationTest.xtend index ef72804982..637f4132fe 100644 --- a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend +++ b/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyVitruvApplicationTest.xtend @@ -1,4 +1,4 @@ -package tools.vitruv.testutils +package tools.vitruv.testutils.framework.vsum import edu.kit.ipd.sdq.activextendannotations.DelegateExcept import java.nio.file.Path diff --git a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/ViewBasedVitruvApplicationTest.xtend b/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/ViewBasedVitruvApplicationTest.xtend similarity index 98% rename from bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/ViewBasedVitruvApplicationTest.xtend rename to testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/ViewBasedVitruvApplicationTest.xtend index 9e50da0a83..7021e5748d 100644 --- a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/ViewBasedVitruvApplicationTest.xtend +++ b/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/ViewBasedVitruvApplicationTest.xtend @@ -1,4 +1,4 @@ -package tools.vitruv.testutils +package tools.vitruv.testutils.framework.vsum import org.junit.jupiter.api.^extension.ExtendWith import tools.vitruv.framework.vsum.VirtualModel diff --git a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/VirtualModelBasedTestView.xtend b/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/VirtualModelBasedTestView.xtend similarity index 87% rename from bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/VirtualModelBasedTestView.xtend rename to testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/VirtualModelBasedTestView.xtend index 8174e4a7d6..a75b12fce4 100644 --- a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/VirtualModelBasedTestView.xtend +++ b/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/VirtualModelBasedTestView.xtend @@ -1,4 +1,4 @@ -package tools.vitruv.testutils +package tools.vitruv.testutils.framework.vsum import tools.vitruv.framework.vsum.VirtualModel import tools.vitruv.testutils.views.TestView diff --git a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/VitruvApplicationTest.xtend b/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/VitruvApplicationTest.xtend similarity index 96% rename from bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/VitruvApplicationTest.xtend rename to testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/VitruvApplicationTest.xtend index 7ad28bb44b..01da26f2ee 100644 --- a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/VitruvApplicationTest.xtend +++ b/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/VitruvApplicationTest.xtend @@ -1,4 +1,4 @@ -package tools.vitruv.testutils +package tools.vitruv.testutils.framework.vsum import java.nio.file.Path import org.eclipse.xtend.lib.annotations.Delegate diff --git a/views/pom.xml b/views/pom.xml new file mode 100644 index 0000000000..e26f6e229f --- /dev/null +++ b/views/pom.xml @@ -0,0 +1,132 @@ + + + + 4.0.0 + + + tools.vitruv + tools.vitruv.framework + 3.0.1-SNAPSHOT + + + tools.vitruv.framework.views + + Vitruv View Definition + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + org.eclipse.xtend + xtend-maven-plugin + + + + + + + + ${project.groupId} + tools.vitruv.change.composite + ${project.version} + + + ${project.groupId} + tools.vitruv.change.changederivation + ${project.version} + + + + + ${project.groupId} + tools.vitruv.change.testutils.core + ${project.version} + test + + + ${project.groupId} + tools.vitruv.change.testutils.metamodels + ${project.version} + test + + + + + + + + log4j + log4j + + + com.google.guava + guava + + + org.eclipse.emf + org.eclipse.emf.common + + + org.eclipse.emf + org.eclipse.emf.ecore + + + org.eclipse.emf + org.eclipse.emf.edit + + + org.eclipse.xtext + org.eclipse.xtext.xbase.lib + + + sdq-commons + edu.kit.ipd.sdq.commons.util.emf + + + xannotations + edu.kit.ipd.sdq.activextendannotations + + + + + org.eclipse.xtend + org.eclipse.xtend.lib + test + + + org.hamcrest + hamcrest + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params + test + + + sdq-commons + edu.kit.ipd.sdq.commons.util.java + test + + + org.mockito + mockito-core + test + + + \ No newline at end of file diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/ViewTypeRepositoryTest.java b/views/src/main/java/tools/vitruv/framework/views/ViewTypeRepositoryTest.java similarity index 100% rename from tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/ViewTypeRepositoryTest.java rename to views/src/main/java/tools/vitruv/framework/views/ViewTypeRepositoryTest.java diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/BasicViewTest.java b/views/src/main/java/tools/vitruv/framework/views/impl/BasicViewTest.java similarity index 100% rename from tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/BasicViewTest.java rename to views/src/main/java/tools/vitruv/framework/views/impl/BasicViewTest.java diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java b/views/src/main/java/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java similarity index 100% rename from tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java rename to views/src/main/java/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java b/views/src/main/java/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java similarity index 100% rename from tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java rename to views/src/main/java/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java b/views/src/main/java/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java similarity index 100% rename from tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java rename to views/src/main/java/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java b/views/src/main/java/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java similarity index 100% rename from tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java rename to views/src/main/java/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java b/views/src/main/java/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java similarity index 100% rename from tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java rename to views/src/main/java/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java b/views/src/main/java/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java similarity index 100% rename from tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java rename to views/src/main/java/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend b/views/src/main/xtend/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend similarity index 100% rename from tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend rename to views/src/main/xtend/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend b/views/src/main/xtend/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend similarity index 100% rename from tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend rename to views/src/main/xtend/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend b/views/src/main/xtend/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend similarity index 100% rename from tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend rename to views/src/main/xtend/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend b/views/src/main/xtend/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend similarity index 100% rename from tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend rename to views/src/main/xtend/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend b/views/src/main/xtend/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend similarity index 100% rename from tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend rename to views/src/main/xtend/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend diff --git a/views/src/test/java/tools/vitruv/framework/views/ViewTypeRepositoryTest.java b/views/src/test/java/tools/vitruv/framework/views/ViewTypeRepositoryTest.java new file mode 100644 index 0000000000..86a6f1c7e1 --- /dev/null +++ b/views/src/test/java/tools/vitruv/framework/views/ViewTypeRepositoryTest.java @@ -0,0 +1,107 @@ +package tools.vitruv.framework.views; + +import static java.util.Collections.emptySet; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static tools.vitruv.framework.views.ViewTypeFactory.createIdentityMappingViewType; + +import java.util.HashSet; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import tools.vitruv.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.testutils.TestLogging; + +@ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) +public class ViewTypeRepositoryTest { + @Nested + @DisplayName("register") + public class Register { + @Test + @DisplayName("proper view type") + public void properViewType() { + ViewTypeRepository repository = new ViewTypeRepository(); + ViewType viewType = createIdentityMappingViewType("type"); + repository.register(viewType); + assertThat(repository.getViewTypes().size(), is(1)); + assertThat(repository.getViewTypes(), hasItem(viewType)); + } + + @Test + @DisplayName("null view type") + public void nullViewType() { + ViewTypeRepository repository = new ViewTypeRepository(); + assertThrows(IllegalArgumentException.class, () -> repository.register(null)); + } + + @Test + @DisplayName("same view type twice") + public void sameViewTypeTwice() { + ViewTypeRepository repository = new ViewTypeRepository(); + ViewType viewType = createIdentityMappingViewType("type"); + repository.register(viewType); + assertThrows(IllegalStateException.class, () -> repository.register(viewType)); + } + + @Test + @DisplayName("another view type with same name") + public void viewTypeWithSameName() { + ViewTypeRepository repository = new ViewTypeRepository(); + ViewType firstViewType = createIdentityMappingViewType("type"); + ViewType secondViewType = createIdentityMappingViewType("type"); + repository.register(firstViewType); + assertThrows(IllegalStateException.class, () -> repository.register(secondViewType)); + } + } + + @Nested + @DisplayName("retrieve") + public class Retrieve { + @Test + @DisplayName("provides only a copy of view type list") + public void providesOnlyCopy() { + ViewTypeRepository repository = new ViewTypeRepository(); + ViewType viewType = createIdentityMappingViewType("type"); + repository.getViewTypes().add(viewType); + assertThat(new HashSet<>(repository.getViewTypes()), is(emptySet())); + } + } + + @Nested + @DisplayName("find") + public class Find { + @Test + @DisplayName("registered view type by name") + public void registeredViewType() { + ViewTypeRepository repository = new ViewTypeRepository(); + ViewType viewType = createIdentityMappingViewType("type"); + repository.register(viewType); + assertThat(repository.findViewType("type"), is(viewType)); + } + + @Test + @DisplayName("non-registered view type by name") + public void nonRegisteredViewType() { + ViewTypeRepository repository = new ViewTypeRepository(); + ViewType viewType = createIdentityMappingViewType("type"); + repository.register(viewType); + assertThat(repository.findViewType("other"), equalTo(null)); + } + + @Test + @DisplayName("null name") + public void nullName() { + ViewTypeRepository repository = new ViewTypeRepository(); + ViewType viewType = createIdentityMappingViewType("type"); + repository.register(viewType); + assertThrows(IllegalArgumentException.class, () -> repository.findViewType(null)); + } + + } +} diff --git a/views/src/test/java/tools/vitruv/framework/views/impl/BasicViewTest.java b/views/src/test/java/tools/vitruv/framework/views/impl/BasicViewTest.java new file mode 100644 index 0000000000..97616f2b03 --- /dev/null +++ b/views/src/test/java/tools/vitruv/framework/views/impl/BasicViewTest.java @@ -0,0 +1,262 @@ +package tools.vitruv.framework.views.impl; + +import static org.hamcrest.CoreMatchers.anything; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; + +import org.eclipse.emf.common.util.URI; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import allElementTypes.NonRoot; +import allElementTypes.Root; +import tools.vitruv.change.atomic.hid.HierarchicalId; +import tools.vitruv.framework.views.ChangeableViewSource; +import tools.vitruv.framework.views.ModifiableViewSelection; +import tools.vitruv.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.testutils.TestLogging; + +@ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) +public class BasicViewTest { + @Mock + ViewCreatingViewType mockViewType; + @Mock + ChangeableViewSource mockChangeableViewSource; + @Mock + ModifiableViewSelection mockViewSelection; + + @BeforeEach + public void initializeMocks() { + MockitoAnnotations.openMocks(this); + } + + @Nested + @DisplayName("initialize") + public class Initialize { + @Test + @DisplayName("with null view type") + public void withNullViewType() { + assertThrows(IllegalArgumentException.class, + () -> new BasicView(null, mockChangeableViewSource, mockViewSelection)); + } + + @Test + @DisplayName("with null view source") + public void withNullViewSource() { + assertThrows(IllegalArgumentException.class, + () -> new BasicView(mockViewType, null, mockViewSelection)); + } + + @Test + @DisplayName("with null view selection") + public void withNullViewSelection() { + assertThrows(IllegalArgumentException.class, + () -> new BasicView(mockViewType, mockChangeableViewSource, null)); + } + + @Test + @DisplayName("with proper arguments") + public void withEmptySource() throws Exception { + try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { + verify(mockViewType).updateView(view); + assertThat(view.isClosed(), is(false)); + assertThat(view.getRootObjects(), not(hasItem(anything()))); + } + } + } + + @Nested + @DisplayName("retrieve roots") + public class RetrieveRootElements { + @Test + @DisplayName("all of same type") + public void allOfSameType() throws Exception { + try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { + Root root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + Root root2 = aet.Root(); + view.registerRoot(root2, URI.createURI("test://test2.aet")); + assertThat(view.getRootObjects().size(), is(2)); + assertThat(view.getRootObjects(Root.class).size(), is(2)); + assertThat(view.getRootObjects(), hasItem(root)); + assertThat(view.getRootObjects(Root.class), hasItem(root2)); + assertThat(view.getRootObjects(), hasItem(root)); + assertThat(view.getRootObjects(Root.class), hasItem(root2)); + } + } + + @Test + @DisplayName("all of one out of two types") + public void containingAllOfOneType() throws Exception { + try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { + Root root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + NonRoot otherRoot = aet.NonRoot(); + view.registerRoot(otherRoot, URI.createURI("test://test2.aet")); + assertThat(view.getRootObjects().size(), is(2)); + assertThat(view.getRootObjects(Root.class).size(), is(1)); + assertThat(view.getRootObjects(), hasItem(root)); + assertThat(view.getRootObjects(Root.class), hasItem(root)); + assertThat(view.getRootObjects(), hasItem(otherRoot)); + } + } + + @Test + @DisplayName("containing none of a type") + public void containingNoneOfType() throws Exception { + try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { + Root root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + Root otherRoot = aet.Root(); + view.registerRoot(otherRoot, URI.createURI("test://test2.aet")); + assertThat(view.getRootObjects().size(), is(2)); + assertThat(view.getRootObjects(), hasItem(root)); + assertThat(view.getRootObjects(), hasItem(otherRoot)); + assertThat(view.getRootObjects(NonRoot.class), not(hasItem(anything()))); + } + } + } + + @Nested + @DisplayName("update") + public class Update { + @Test + @DisplayName("without previous modification") + public void withoutPreviousModification() throws Exception { + try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { + view.update(); + verify(mockViewType, times(2)).updateView(view); + } + } + + @Test + @DisplayName("with previous modification") + public void withPreviousModification() throws Exception { + try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { + view.modifyContents((resourceSet) -> resourceSet.createResource(URI.createURI("test://test.aet"))); + assertThrows(IllegalStateException.class, () -> view.update()); + } + } + } + + @Nested + @DisplayName("add root") + public class AddRoot { + @Test + @DisplayName("being null") + public void nullElement() throws Exception { + try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { + assertThrows(IllegalArgumentException.class, + () -> view.registerRoot(null, URI.createURI("test://test.aet"))); + } + } + + @Test + @DisplayName("with null URI") + public void nullUri() throws Exception { + try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { + Root root = aet.Root(); + assertThrows(IllegalArgumentException.class, () -> view.registerRoot(root, null)); + } + } + + @Test + @DisplayName("with proper arguments") + public void properArguments() throws Exception { + try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { + Root root = aet.Root(); + String testResourceUriString = "test://test.aet"; + view.registerRoot(root, URI.createURI(testResourceUriString)); + assertThat(view.getRootObjects(), hasItem(root)); + } + } + } + + @Nested + @DisplayName("move root") + public class MoveRoot { + @Test + @DisplayName("being null") + public void nullElement() throws Exception { + try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { + assertThrows(IllegalArgumentException.class, + () -> view.moveRoot(null, URI.createURI("test://test.aet"))); + } + } + + @Test + @DisplayName("with null URI") + public void nullUri() throws Exception { + try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { + Root root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + assertThrows(IllegalArgumentException.class, () -> view.moveRoot(root, null)); + } + } + + @Test + @DisplayName("with element not beeing root") + public void notBeingRoot() throws Exception { + try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { + Root root = aet.Root(); + assertThrows(IllegalStateException.class, () -> view.moveRoot(root, URI.createURI("test://test.aet"))); + } + } + + @Test + @DisplayName("with proper arguments") + public void properArguments() throws Exception { + try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { + Root root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + view.moveRoot(root, URI.createURI("test://test2.aet")); + assertThat(view.getRootObjects().size(), is(1)); + assertThat(view.getRootObjects(), hasItem(root)); + } + } + } + + @Nested + @DisplayName("close") + public class Close { + @Test + @DisplayName("and is closed afterwards") + public void isClosed() throws Exception { + BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection); + view.close(); + assertThat("view should be closed", view.isClosed()); + } + + @Test + @DisplayName("can be called multiple times") + public void callMultipleTimes() throws Exception { + BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection); + view.close(); + view.close(); + assertThat("view should be closed", view.isClosed()); + } + + @Test + @DisplayName("and does not allow further operations") + public void noOperations() throws Exception { + BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection); + view.close(); + assertThrows(IllegalStateException.class, () -> view.getRootObjects()); + assertThrows(IllegalStateException.class, () -> view.getRootObjects(Root.class)); + assertThrows(IllegalStateException.class, () -> view.update()); + assertThrows(IllegalStateException.class, () -> view.registerRoot(null, null)); + assertThrows(IllegalStateException.class, () -> view.moveRoot(null, null)); + } + } +} diff --git a/views/src/test/java/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java b/views/src/test/java/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java new file mode 100644 index 0000000000..6157548b81 --- /dev/null +++ b/views/src/test/java/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java @@ -0,0 +1,410 @@ +package tools.vitruv.framework.views.impl; + +import static org.hamcrest.CoreMatchers.anything; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static tools.vitruv.testutils.matchers.ModelMatchers.equalsDeeply; +import static tools.vitruv.testutils.matchers.ModelMatchers.ignoringFeatures; +import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import allElementTypes.NonRoot; +import allElementTypes.Root; +import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute; +import tools.vitruv.change.atomic.hid.HierarchicalId; +import tools.vitruv.change.atomic.root.InsertRootEObject; +import tools.vitruv.change.atomic.root.RootFactory; +import tools.vitruv.change.atomic.root.RootPackage; +import tools.vitruv.change.composite.description.VitruviusChange; +import tools.vitruv.change.composite.description.VitruviusChangeResolver; +import tools.vitruv.framework.views.ChangeableViewSource; +import tools.vitruv.framework.views.ModifiableViewSelection; +import tools.vitruv.framework.views.changederivation.DefaultStateBasedChangeResolutionStrategy; +import tools.vitruv.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.testutils.TestLogging; + +@ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) +public class ChangeDerivingViewTest { + @Mock + ViewCreatingViewType mockViewType; + @Mock + ChangeableViewSource mockChangeableViewSource; + @Mock + ModifiableViewSelection mockViewSelection; + + @BeforeEach + public void initializeMocks() { + MockitoAnnotations.openMocks(this); + } + + @Nested + @DisplayName("initialize") + public class Initialize { + @Test + @DisplayName("with null view type") + public void withNullViewType() { + assertThrows(IllegalArgumentException.class, () -> new ChangeDerivingView(null, new DefaultStateBasedChangeResolutionStrategy())); + } + + @Test + @DisplayName("with null change resolution strategy") + public void withNullChangeResolutionStrategy() { + assertThrows(IllegalArgumentException.class, + () -> new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection), null)); + } + + @Test + @DisplayName("with proper arguments") + public void withEmptySource() throws Exception { + try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { + assertThat(view.isClosed(), is(false)); + assertThat(view.getRootObjects(), not(hasItem(anything()))); + } + } + } + + @Nested + @DisplayName("retrieve roots") + public class RetrieveRootElements { + @Test + @DisplayName("all of same type") + public void allOfSameType() throws Exception { + try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { + Root root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + Root root2 = aet.Root(); + view.registerRoot(root2, URI.createURI("test://test2.aet")); + assertThat(view.getRootObjects().size(), is(2)); + assertThat(view.getRootObjects(Root.class).size(), is(2)); + assertThat(view.getRootObjects(), hasItem(root)); + assertThat(view.getRootObjects(Root.class), hasItem(root2)); + assertThat(view.getRootObjects(), hasItem(root)); + assertThat(view.getRootObjects(Root.class), hasItem(root2)); + } + } + + @Test + @DisplayName("all of one out of two types") + public void containingAllOfOneType() throws Exception { + try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { + Root root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + NonRoot otherRoot = aet.NonRoot(); + view.registerRoot(otherRoot, URI.createURI("test://test2.aet")); + assertThat(view.getRootObjects().size(), is(2)); + assertThat(view.getRootObjects(Root.class).size(), is(1)); + assertThat(view.getRootObjects(), hasItem(root)); + assertThat(view.getRootObjects(Root.class), hasItem(root)); + assertThat(view.getRootObjects(), hasItem(otherRoot)); + } + } + + @Test + @DisplayName("containing none of a type") + public void containingNoneOfType() throws Exception { + try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { + Root root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + Root otherRoot = aet.Root(); + view.registerRoot(otherRoot, URI.createURI("test://test2.aet")); + assertThat(view.getRootObjects().size(), is(2)); + assertThat(view.getRootObjects(), hasItem(root)); + assertThat(view.getRootObjects(), hasItem(otherRoot)); + assertThat(view.getRootObjects(NonRoot.class), not(hasItem(anything()))); + } + } + } + + @Nested + @DisplayName("update") + public class Update { + @Test + @DisplayName("without previous modification") + public void withoutPreviousModification() throws Exception { + try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { + view.update(); + } + } + + @Test + @DisplayName("with previous modification") + public void withPreviousModification() throws Exception { + try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { + view.modifyContents((resourceSet) -> resourceSet.createResource(URI.createURI("test://test.aet"))); + assertThrows(IllegalStateException.class, () -> view.update()); + } + } + } + + @Nested + @DisplayName("add root") + public class AddRoot { + @Test + @DisplayName("being null") + public void nullElement() throws Exception { + try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { + assertThrows(IllegalArgumentException.class, + () -> view.registerRoot(null, URI.createURI("test://test.aet"))); + } + } + + @Test + @DisplayName("with null URI") + public void nullUri() throws Exception { + try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { + Root root = aet.Root(); + assertThrows(IllegalArgumentException.class, () -> view.registerRoot(root, null)); + } + } + + @Test + @DisplayName("with proper arguments") + public void properArguments() throws Exception { + try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { + Root root = aet.Root(); + String testResourceUriString = "test://test.aet"; + view.registerRoot(root, URI.createURI(testResourceUriString)); + assertThat(view.getRootObjects(), hasItem(root)); + } + } + + @Test + @DisplayName("committing changes") + public void commitChanges() throws Exception { + try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { + Root root = aet.Root(); + root.setId("root"); + String testResourceUriString = "test://test.aet"; + view.registerRoot(root, URI.createURI(testResourceUriString)); + assertThat(view.getRootObjects(), hasItem(root)); + ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeDerivingView.class); + ArgumentCaptor> changeArgument = ArgumentCaptor.forClass(VitruviusChange.class); + view.commitChanges(); + verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); + assertThat(viewArgument.getValue(), is(view)); + VitruviusChange change = VitruviusChangeResolver.forHierarchicalIds(root.eResource().getResourceSet()).resolveAndApply(changeArgument.getValue()); + InsertRootEObject expectedChange = RootFactory.eINSTANCE.createInsertRootEObject(); + expectedChange.setNewValue(root); + expectedChange.setUri(testResourceUriString); + assertThat(change.getEChanges().size(), is(3)); // Create, Insert, ReplaceId + assertThat(change.getEChanges().get(1), + equalsDeeply(expectedChange, + ignoringFeatures(RootPackage.eINSTANCE.getRootEChange_Resource()))); + assertThat(change.getEChanges().get(2), instanceOf(ReplaceSingleValuedEAttribute.class)); + } + } + } + + @Nested + @DisplayName("move root") + public class MoveRoot { + @Test + @DisplayName("being null") + public void nullElement() throws Exception { + try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { + assertThrows(IllegalArgumentException.class, + () -> view.moveRoot(null, URI.createURI("test://test.aet"))); + } + } + + @Test + @DisplayName("with null URI") + public void nullUri() throws Exception { + try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { + Root root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + assertThrows(IllegalArgumentException.class, () -> view.moveRoot(root, null)); + } + } + + @Test + @DisplayName("with element not beeing root") + public void notBeingRoot() throws Exception { + try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { + Root root = aet.Root(); + assertThrows(IllegalStateException.class, () -> view.moveRoot(root, URI.createURI("test://test.aet"))); + } + } + + @Test + @DisplayName("with proper arguments") + public void properArguments() throws Exception { + try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { + Root root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + view.moveRoot(root, URI.createURI("test://test2.aet")); + assertThat(view.getRootObjects().size(), is(1)); + assertThat(view.getRootObjects(), hasItem(root)); + } + } + + @Test + @DisplayName("committing changes") + public void commitChanges() throws Exception { + try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { + Root root = aet.Root(); + String movedResourceUriString = "test://test2.aet"; + view.registerRoot(root, URI.createURI("test://test.aet")); + view.commitChanges(); + reset(mockChangeableViewSource, mockViewType); + view.moveRoot(root, URI.createURI(movedResourceUriString)); + assertThat(view.getRootObjects(), hasItem(root)); + ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeDerivingView.class); + ArgumentCaptor> changeArgument = ArgumentCaptor.forClass(VitruviusChange.class); + view.commitChangesAndUpdate(); + verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); + assertThat(viewArgument.getValue(), is(view)); + assertTrue(changeArgument.getValue().containsConcreteChange(), "change must contain some concrete change"); + assertThat(view.getRootObjects().size(), is(1)); + Root updatedRoot = (Root)view.getRootObjects().iterator().next(); + assertThat(updatedRoot.eResource().getURI(), is(URI.createURI(movedResourceUriString))); + } + } + } + + @Nested + @DisplayName("commit") + public class Commit { + ChangeDerivingView view; + Root root; + + @BeforeEach + public void prepareViewWithRootElement() { + view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy()); + root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + view.commitChangesAndUpdate(); + reset(mockChangeableViewSource, mockViewType); + } + + @AfterEach + public void closeView() throws Exception { + view.close(); + } + + @Test + @DisplayName("once") + public void once() { + NonRoot nonRoot = aet.NonRoot(); + nonRoot.setId("nonRoot"); + root.setSingleValuedContainmentEReference(nonRoot); + ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeDerivingView.class); + ArgumentCaptor> changeArgument = ArgumentCaptor.forClass(VitruviusChange.class); + view.commitChangesAndUpdate(); + verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); + assertThat(viewArgument.getValue(), is(view)); + assertTrue(changeArgument.getValue().containsConcreteChange(), "change must contain some concrete change"); + assertThat(view.getRootObjects().size(), is(1)); + Root root = (Root)view.getRootObjects().iterator().next(); + assertThat(root.eContents().size(), is(1)); + assertThat(root.eContents(), hasItem(nonRoot)); + } + + @Test + @DisplayName("twice") + public void twice() { + NonRoot firstNonRoot = aet.NonRoot(); + firstNonRoot.setId("first"); + root.setSingleValuedContainmentEReference(firstNonRoot); + view.commitChanges(); + reset(mockChangeableViewSource, mockViewType); + NonRoot secondNonRoot = aet.NonRoot(); + secondNonRoot.setId("second"); + root.setSingleValuedContainmentEReference(secondNonRoot); + ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeDerivingView.class); + ArgumentCaptor> changeArgument = ArgumentCaptor.forClass(VitruviusChange.class); + view.commitChangesAndUpdate(); + verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); + assertThat(viewArgument.getValue(), is(view)); + assertTrue(changeArgument.getValue().containsConcreteChange(), "change must contain some concrete change"); + assertThat(view.getRootObjects().size(), is(1)); + Root root = (Root)view.getRootObjects().iterator().next(); + assertThat(root.eContents().size(), is(1)); + assertThat(root.eContents(), hasItem(secondNonRoot)); + } + + @Test + @DisplayName("without changes") + public void withoutChanges() { + ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeDerivingView.class); + ArgumentCaptor> changeArgument = ArgumentCaptor.forClass(VitruviusChange.class); + view.commitChangesAndUpdate(); + verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); + assertThat(viewArgument.getValue(), is(view)); + assertFalse(changeArgument.getValue().containsConcreteChange(), "change must be empty"); + assertThat(view.getRootObjects().size(), is(1)); + } + } + + @Nested + @DisplayName("close") + public class Close { + @Test + @DisplayName("and is closed afterwards") + public void isClosed() throws Exception { + ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy()); + view.close(); + assertThat("view should be closed", view.isClosed()); + } + + @Test + @DisplayName("can be called multiple times") + public void callMultipleTimes() throws Exception { + ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy()); + view.close(); + view.close(); + assertThat("view should be closed", view.isClosed()); + } + + @Test + @DisplayName("and does not allow further operations") + public void noOperations() throws Exception { + ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, + mockViewSelection), new DefaultStateBasedChangeResolutionStrategy()); + view.close(); + assertThrows(IllegalStateException.class, () -> view.getRootObjects()); + assertThrows(IllegalStateException.class, () -> view.getRootObjects(Root.class)); + assertThrows(IllegalStateException.class, () -> view.update()); + assertThrows(IllegalStateException.class, () -> view.commitChanges()); + assertThrows(IllegalStateException.class, () -> view.registerRoot(null, null)); + assertThrows(IllegalStateException.class, () -> view.moveRoot(null, null)); + } + } +} diff --git a/views/src/test/java/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java b/views/src/test/java/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java new file mode 100644 index 0000000000..c08fa2557a --- /dev/null +++ b/views/src/test/java/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java @@ -0,0 +1,442 @@ +package tools.vitruv.framework.views.impl; + +import static org.hamcrest.CoreMatchers.anything; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static tools.vitruv.testutils.matchers.ModelMatchers.equalsDeeply; +import static tools.vitruv.testutils.matchers.ModelMatchers.ignoringFeatures; +import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; + +import java.util.List; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import allElementTypes.AllElementTypesPackage; +import allElementTypes.NonRoot; +import allElementTypes.Root; +import tools.vitruv.change.atomic.EChange; +import tools.vitruv.change.atomic.eobject.CreateEObject; +import tools.vitruv.change.atomic.eobject.DeleteEObject; +import tools.vitruv.change.atomic.feature.attribute.AttributeFactory; +import tools.vitruv.change.atomic.feature.attribute.AttributePackage; +import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute; +import tools.vitruv.change.atomic.feature.reference.ReplaceSingleValuedEReference; +import tools.vitruv.change.atomic.hid.HierarchicalId; +import tools.vitruv.change.atomic.root.InsertRootEObject; +import tools.vitruv.change.atomic.root.RootFactory; +import tools.vitruv.change.atomic.root.RootPackage; +import tools.vitruv.change.composite.description.VitruviusChange; +import tools.vitruv.change.composite.description.VitruviusChangeResolver; +import tools.vitruv.framework.views.ChangeableViewSource; +import tools.vitruv.framework.views.ModifiableViewSelection; +import tools.vitruv.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.testutils.TestLogging;; + +@ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) +public class ChangeRecordingViewTest { + @Mock + ViewCreatingViewType mockViewType; + @Mock + ChangeableViewSource mockChangeableViewSource; + @Mock + ModifiableViewSelection mockViewSelection; + + @BeforeEach + public void initializeMocks() { + MockitoAnnotations.openMocks(this); + } + + @Nested + @DisplayName("initialize") + public class Initialize { + @Test + @DisplayName("with null view") + public void withNullViewType() { + assertThrows(IllegalArgumentException.class, () -> new ChangeRecordingView(null)); + } + + @Test + @DisplayName("with proper arguments") + public void withEmptySource() throws Exception { + try (ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { + assertThat(view.isClosed(), is(false)); + assertThat(view.getRootObjects(), not(hasItem(anything()))); + } + } + } + + @Nested + @DisplayName("retrieve roots") + public class RetrieveRootElements { + @Test + @DisplayName("all of same type") + public void allOfSameType() throws Exception { + try (ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { + Root root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + Root root2 = aet.Root(); + view.registerRoot(root2, URI.createURI("test://test2.aet")); + assertThat(view.getRootObjects().size(), is(2)); + assertThat(view.getRootObjects(Root.class).size(), is(2)); + assertThat(view.getRootObjects(), hasItem(root)); + assertThat(view.getRootObjects(Root.class), hasItem(root2)); + assertThat(view.getRootObjects(), hasItem(root)); + assertThat(view.getRootObjects(Root.class), hasItem(root2)); + } + } + + @Test + @DisplayName("all of one out of two types") + public void containingAllOfOneType() throws Exception { + try (ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { + Root root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + NonRoot otherRoot = aet.NonRoot(); + view.registerRoot(otherRoot, URI.createURI("test://test2.aet")); + assertThat(view.getRootObjects().size(), is(2)); + assertThat(view.getRootObjects(Root.class).size(), is(1)); + assertThat(view.getRootObjects(), hasItem(root)); + assertThat(view.getRootObjects(Root.class), hasItem(root)); + assertThat(view.getRootObjects(), hasItem(otherRoot)); + } + } + + @Test + @DisplayName("containing none of a type") + public void containingNoneOfType() throws Exception { + try (ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { + Root root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + Root otherRoot = aet.Root(); + view.registerRoot(otherRoot, URI.createURI("test://test2.aet")); + assertThat(view.getRootObjects().size(), is(2)); + assertThat(view.getRootObjects(), hasItem(root)); + assertThat(view.getRootObjects(), hasItem(otherRoot)); + assertThat(view.getRootObjects(NonRoot.class), not(hasItem(anything()))); + } + } + } + + @Nested + @DisplayName("update") + public class Update { + @Test + @DisplayName("without previous modification") + public void withoutPreviousModification() throws Exception { + try (ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { + view.update(); + } + } + + @Test + @DisplayName("with previous modification") + public void withPreviousModification() throws Exception { + try (ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { + view.modifyContents((resourceSet) -> resourceSet.createResource(URI.createURI("test://test.aet"))); + assertThrows(IllegalStateException.class, () -> view.update()); + } + } + } + + @Nested + @DisplayName("add root") + public class AddRoot { + @Test + @DisplayName("being null") + public void nullElement() throws Exception { + try (ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { + assertThrows(IllegalArgumentException.class, + () -> view.registerRoot(null, URI.createURI("test://test.aet"))); + } + } + + @Test + @DisplayName("with null URI") + public void nullUri() throws Exception { + try (ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { + Root root = aet.Root(); + assertThrows(IllegalArgumentException.class, () -> view.registerRoot(root, null)); + } + } + + @Test + @DisplayName("with proper arguments") + public void properArguments() throws Exception { + try (ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { + Root root = aet.Root(); + String testResourceUriString = "test://test.aet"; + view.registerRoot(root, URI.createURI(testResourceUriString)); + assertThat(view.getRootObjects(), hasItem(root)); + } + } + + @Test + @DisplayName("committing changes") + public void commitChanges() throws Exception { + try (ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { + Root root = aet.Root(); + root.setId("root"); + String testResourceUriString = "test://test.aet"; + view.registerRoot(root, URI.createURI(testResourceUriString)); + assertThat(view.getRootObjects(), hasItem(root)); + ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeRecordingView.class); + ArgumentCaptor> changeArgument = ArgumentCaptor + .forClass(VitruviusChange.class); + view.commitChanges(); + verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); + + assertThat(viewArgument.getValue(), is(view)); + ResourceSet resolveInResourceSet = new ResourceSetImpl(); + VitruviusChange resolvedChange = VitruviusChangeResolver + .forHierarchicalIds(resolveInResourceSet).resolveAndApply(changeArgument.getValue()); + InsertRootEObject expectedChange = RootFactory.eINSTANCE.createInsertRootEObject(); + expectedChange.setNewValue(root); + expectedChange.setUri(testResourceUriString); + assertThat(resolvedChange.getEChanges().size(), is(3)); // Create, Insert, ReplaceId + assertThat(resolvedChange.getEChanges().get(1), equalsDeeply(expectedChange, + ignoringFeatures(RootPackage.eINSTANCE.getRootEChange_Resource()))); + assertThat(resolvedChange.getEChanges().get(2), instanceOf(ReplaceSingleValuedEAttribute.class)); + } + } + } + + @Nested + @DisplayName("move root") + public class MoveRoot { + @Test + @DisplayName("being null") + public void nullElement() throws Exception { + try (ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { + assertThrows(IllegalArgumentException.class, + () -> view.moveRoot(null, URI.createURI("test://test.aet"))); + } + } + + @Test + @DisplayName("with null URI") + public void nullUri() throws Exception { + try (ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { + Root root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + assertThrows(IllegalArgumentException.class, () -> view.moveRoot(root, null)); + } + } + + @Test + @DisplayName("with element not beeing root") + public void notBeingRoot() throws Exception { + try (ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { + Root root = aet.Root(); + assertThrows(IllegalStateException.class, () -> view.moveRoot(root, URI.createURI("test://test.aet"))); + } + } + + @Test + @DisplayName("with proper arguments") + public void properArguments() throws Exception { + try (ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { + Root root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + view.moveRoot(root, URI.createURI("test://test2.aet")); + assertThat(view.getRootObjects().size(), is(1)); + assertThat(view.getRootObjects(), hasItem(root)); + } + } + + @Test + @DisplayName("committing changes") + public void commitChanges() throws Exception { + try (ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { + Root root = aet.Root(); + String movedResourceUriString = "test://test2.aet"; + view.registerRoot(root, URI.createURI("test://test.aet")); + view.commitChanges(); + reset(mockChangeableViewSource, mockViewType); + + ResourceSet resolveInResourceSet = new ResourceSetImpl(); + resolveInResourceSet.createResource(root.eResource().getURI()); + resolveInResourceSet.getResource(root.eResource().getURI(), false).getContents() + .add(EcoreUtil.copy(root)); + + view.moveRoot(root, URI.createURI(movedResourceUriString)); + assertThat(view.getRootObjects(), hasItem(root)); + ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeRecordingView.class); + ArgumentCaptor> changeArgument = ArgumentCaptor + .forClass(VitruviusChange.class); + view.commitChanges(); + verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); + assertThat(viewArgument.getValue(), is(view)); + VitruviusChange resolvedChange = VitruviusChangeResolver + .forHierarchicalIds(resolveInResourceSet).resolveAndApply(changeArgument.getValue()); + List> capturedEChanges = resolvedChange.getEChanges(); + InsertRootEObject expectedChange = RootFactory.eINSTANCE.createInsertRootEObject(); + expectedChange.setNewValue(root); + expectedChange.setUri(movedResourceUriString); + assertThat(capturedEChanges.size(), is(2)); // Remove, Insert + assertThat(capturedEChanges.get(1), equalsDeeply(expectedChange, + ignoringFeatures(RootPackage.eINSTANCE.getRootEChange_Resource()))); + } + } + } + + @Nested + @DisplayName("commit") + public class Commit { + ChangeRecordingView view; + Root root; + + @BeforeEach + public void prepareViewWithRootElement() { + view = new ChangeRecordingView(new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)); + root = aet.Root(); + view.registerRoot(root, URI.createURI("test://test.aet")); + view.commitChanges(); + reset(mockChangeableViewSource, mockViewType); + } + + @AfterEach + public void closeView() throws Exception { + view.close(); + } + + @Test + @DisplayName("once") + public void once() { + NonRoot nonRoot = aet.NonRoot(); + nonRoot.setId("nonRoot"); + root.setSingleValuedContainmentEReference(nonRoot); + ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeRecordingView.class); + ArgumentCaptor> changeArgument = ArgumentCaptor + .forClass(VitruviusChange.class); + view.commitChanges(); + verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); + assertThat(viewArgument.getValue(), is(view)); + List> capturedEChanges = changeArgument.getValue().getEChanges(); + assertThat(capturedEChanges.size(), is(3)); // Create, Insert, ReplaceId + assertThat(capturedEChanges.get(0), instanceOf(CreateEObject.class)); + assertThat(capturedEChanges.get(1), instanceOf(ReplaceSingleValuedEReference.class)); + assertThat(capturedEChanges.get(2), instanceOf(ReplaceSingleValuedEAttribute.class)); + } + + @Test + @DisplayName("twice") + public void twice() { + NonRoot firstNonRoot = aet.NonRoot(); + firstNonRoot.setId("first"); + root.setSingleValuedContainmentEReference(firstNonRoot); + view.commitChanges(); + + ResourceSet resolveInResourceSet = new ResourceSetImpl(); + resolveInResourceSet.createResource(root.eResource().getURI()); + resolveInResourceSet.getResource(root.eResource().getURI(), false).getContents().add(EcoreUtil.copy(root)); + + reset(mockChangeableViewSource, mockViewType); + NonRoot secondNonRoot = aet.NonRoot(); + secondNonRoot.setId("second"); + root.setSingleValuedContainmentEReference(secondNonRoot); + ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeRecordingView.class); + ArgumentCaptor> changeArgument = ArgumentCaptor + .forClass(VitruviusChange.class); + view.commitChanges(); + verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); + + VitruviusChange resolvedChange = VitruviusChangeResolver.forHierarchicalIds(resolveInResourceSet) + .resolveAndApply(changeArgument.getValue()); + List> capturedEChanges = resolvedChange.getEChanges(); + assertThat(capturedEChanges.size(), is(4)); // Create, Insert, ReplaceValue, Delete + assertThat(capturedEChanges.get(0), instanceOf(CreateEObject.class)); + assertThat(capturedEChanges.get(1), instanceOf(ReplaceSingleValuedEReference.class)); + ReplaceSingleValuedEAttribute replaceIdChange = AttributeFactory.eINSTANCE + .createReplaceSingleValuedEAttribute(); + replaceIdChange.setAffectedElement(secondNonRoot); + replaceIdChange.setAffectedFeature(AllElementTypesPackage.eINSTANCE.getIdentified_Id()); + replaceIdChange.setNewValue("second"); + assertThat(capturedEChanges.get(2), equalsDeeply(replaceIdChange, + ignoringFeatures(AttributePackage.eINSTANCE.getSubtractiveAttributeEChange_OldValue()))); + assertThat(capturedEChanges.get(3), instanceOf(DeleteEObject.class)); + } + + @Test + @DisplayName("without changes") + public void withoutChanges() { + ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeRecordingView.class); + ArgumentCaptor> changeArgument = ArgumentCaptor + .forClass(VitruviusChange.class); + view.commitChanges(); + verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); + assertThat(viewArgument.getValue(), is(view)); + assertThat(changeArgument.getValue().getEChanges(), not(hasItem(anything()))); + } + } + + @Nested + @DisplayName("close") + public class Close { + @Test + @DisplayName("and is closed afterwards") + public void isClosed() throws Exception { + ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)); + view.close(); + assertThat("view should be closed", view.isClosed()); + } + + @Test + @DisplayName("can be called multiple times") + public void callMultipleTimes() throws Exception { + ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)); + view.close(); + view.close(); + assertThat("view should be closed", view.isClosed()); + } + + @Test + @DisplayName("and does not allow further operations") + public void noOperations() throws Exception { + ChangeRecordingView view = new ChangeRecordingView( + new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)); + view.close(); + assertThrows(IllegalStateException.class, () -> view.getRootObjects()); + assertThrows(IllegalStateException.class, () -> view.getRootObjects(Root.class)); + assertThrows(IllegalStateException.class, () -> view.update()); + assertThrows(IllegalStateException.class, () -> view.commitChanges()); + assertThrows(IllegalStateException.class, () -> view.registerRoot(null, null)); + assertThrows(IllegalStateException.class, () -> view.moveRoot(null, null)); + } + } +} diff --git a/views/src/test/java/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java b/views/src/test/java/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java new file mode 100644 index 0000000000..fd65a9f53a --- /dev/null +++ b/views/src/test/java/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java @@ -0,0 +1,464 @@ +package tools.vitruv.framework.views.impl; + +import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories; +import static java.util.Collections.emptySet; +import static org.hamcrest.CoreMatchers.anything; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static tools.vitruv.testutils.matchers.ModelMatchers.equalsDeeply; +import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; + +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; + +import com.google.common.collect.FluentIterable; + +import allElementTypes.Root; +import tools.vitruv.change.atomic.EChange; +import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute; +import tools.vitruv.change.atomic.hid.HierarchicalId; +import tools.vitruv.change.atomic.uuid.Uuid; +import tools.vitruv.change.atomic.uuid.UuidResolver; +import tools.vitruv.change.composite.description.VitruviusChange; +import tools.vitruv.change.composite.description.VitruviusChangeFactory; +import tools.vitruv.change.composite.description.VitruviusChangeResolver; +import tools.vitruv.change.composite.recording.ChangeRecorder; +import tools.vitruv.framework.views.ChangeableViewSource; +import tools.vitruv.framework.views.View; +import tools.vitruv.framework.views.ViewSelection; +import tools.vitruv.framework.views.ViewType; +import tools.vitruv.framework.views.selectors.DirectViewElementSelector; +import tools.vitruv.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.testutils.TestLogging; + +@ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) +public class IdentityMappingViewTypeTest { + @Nested + @DisplayName("initialize") + class Initialize { + @Test + @DisplayName("with proper name") + public void withName() { + ViewType viewType = new IdentityMappingViewType("name"); + assertThat(viewType.getName(), is("name")); + } + + @Test + @DisplayName("with empty name") + public void withEmptyName() { + ViewType viewType = new IdentityMappingViewType(""); + assertThat(viewType.getName(), is("")); + } + + @Test + @DisplayName("with null name") + public void withNullName() { + assertThrows(IllegalArgumentException.class, () -> new IdentityMappingViewType(null)); + } + } + + @Nested + @DisplayName("create selector") + class CreateSelector { + private IdentityMappingViewType basicViewType; + + @BeforeEach + public void initializeViewType() { + this.basicViewType = new IdentityMappingViewType("name"); + } + + @Test + @DisplayName("for empty source") + public void forEmptySource() { + DirectViewElementSelector selector = basicViewType + .createSelector(mock(ChangeableViewSource.class)); + assertThat(selector.getSelectableElements(), is(emptySet())); + } + + @Test + @DisplayName("for source containing single element") + public void forSourceContainingElement() { + Resource resource = withGlobalFactories(new ResourceSetImpl()) + .createResource(URI.createURI("test:///test.aet")); + Root rootElement = aet.Root(); + resource.getContents().add(rootElement); + ChangeableViewSource viewSource = mock(ChangeableViewSource.class); + when(viewSource.getViewSourceModels()).thenReturn(Set.of(resource)); + DirectViewElementSelector selector = basicViewType.createSelector(viewSource); + assertThat(selector.getSelectableElements(), is(Set.of(rootElement))); + } + + @Test + @DisplayName("for source containing non-root elements") + public void forSourceContainingNonRootElements() { + Resource resource = withGlobalFactories(new ResourceSetImpl()) + .createResource(URI.createURI("test:///test.aet")); + Root rootElement = aet.Root(); + rootElement.setSingleValuedContainmentEReference(aet.NonRoot()); + resource.getContents().add(rootElement); + ChangeableViewSource viewSource = mock(ChangeableViewSource.class); + when(viewSource.getViewSourceModels()).thenReturn(Set.of(resource)); + DirectViewElementSelector selector = basicViewType.createSelector(viewSource); + assertThat(selector.getSelectableElements(), is(Set.of(rootElement))); + } + } + + @Nested + @DisplayName("create view") + public class CreateView { + private IdentityMappingViewType basicViewType; + private ResourceSet testResourceSet; + + @BeforeEach + public void initializeViewTypeAndResourceSet() { + this.basicViewType = new IdentityMappingViewType("name"); + this.testResourceSet = withGlobalFactories(new ResourceSetImpl()); + } + + @Test + @DisplayName("with empty source") + public void withNoElements() throws Exception { + ChangeableViewSource viewSource = mock(ChangeableViewSource.class); + DirectViewElementSelector selector = basicViewType.createSelector(viewSource); + try (View view = basicViewType.createView(selector)) { + assertThat(view.getRootObjects(), not(hasItem(anything()))); + } + } + + @Test + @DisplayName("with selector from other view type") + public void withSelectorFromOtherViewtype() { + ChangeableViewSource viewSource = mock(ChangeableViewSource.class); + DirectViewElementSelector selector = new IdentityMappingViewType("other") + .createSelector(viewSource); + assertThrows(IllegalArgumentException.class, () -> basicViewType.createView(selector)); + } + + @Test + @DisplayName("with no selected element") + public void withNoSelectedElement() throws Exception { + Resource resource = testResourceSet.createResource(URI.createURI("test://test.aet")); + Root rootElement = aet.Root(); + rootElement.setId("testid"); + resource.getContents().add(rootElement); + ChangeableViewSource viewSource = mock(ChangeableViewSource.class); + when(viewSource.getViewSourceModels()).thenReturn(Set.of(resource)); + DirectViewElementSelector selector = basicViewType.createSelector(viewSource); + try (View view = basicViewType.createView(selector)) { + assertThat(view.getRootObjects(), not(hasItem(anything()))); + } + } + + @Test + @DisplayName("with single selected element") + public void withSingleSelectedElement() throws Exception { + Resource resource = testResourceSet.createResource(URI.createURI("test://test.aet")); + Root rootElement = aet.Root(); + rootElement.setId("testid"); + resource.getContents().add(rootElement); + ChangeableViewSource viewSource = mock(ChangeableViewSource.class); + when(viewSource.getViewSourceModels()).thenReturn(Set.of(resource)); + DirectViewElementSelector selector = basicViewType.createSelector(viewSource); + selector.setSelected(rootElement, true); + try (View view = basicViewType.createView(selector)) { + assertThat(view.getRootObjects().size(), is(1)); + assertThat(view.getRootObjects(), hasItem(equalsDeeply(rootElement))); + } + } + + @Test + @DisplayName("with one of two elements selected") + public void withOneOfTwoElementsSelected() throws Exception { + Resource firstResource = testResourceSet.createResource(URI.createURI("test://test.aet")); + Resource secondResource = testResourceSet.createResource(URI.createURI("test://test2.aet")); + Root firstRootElement = aet.Root(); + firstRootElement.setId("firstElementId"); + Root secondRootElement = aet.Root(); + secondRootElement.setId("secondElementId"); + firstResource.getContents().add(firstRootElement); + secondResource.getContents().add(secondRootElement); + ChangeableViewSource viewSource = mock(ChangeableViewSource.class); + when(viewSource.getViewSourceModels()).thenReturn(Set.of(firstResource, secondResource)); + DirectViewElementSelector selector = basicViewType.createSelector(viewSource); + selector.setSelected(firstRootElement, true); + try (View view = basicViewType.createView(selector)) { + assertThat(view.getRootObjects().size(), is(1)); + assertThat(view.getRootObjects(), hasItem(equalsDeeply(firstRootElement))); + } + } + + @Test + @DisplayName("with both of two elements selected") + public void withBothOfTwoElementsSelected() throws Exception { + Resource firstResource = testResourceSet.createResource(URI.createURI("test://test.aet")); + Resource secondResource = testResourceSet.createResource(URI.createURI("test://test2.aet")); + Root firstRootElement = aet.Root(); + firstRootElement.setId("firstElementId"); + Root secondRootElement = aet.Root(); + secondRootElement.setId("secondElementId"); + firstResource.getContents().add(firstRootElement); + secondResource.getContents().add(secondRootElement); + ChangeableViewSource viewSource = mock(ChangeableViewSource.class); + when(viewSource.getViewSourceModels()).thenReturn(Set.of(firstResource, secondResource)); + DirectViewElementSelector selector = basicViewType.createSelector(viewSource); + selector.setSelected(firstRootElement, true); + selector.setSelected(secondRootElement, true); + try (View view = basicViewType.createView(selector)) { + assertThat(view.getRootObjects().size(), is(2)); + assertThat(view.getRootObjects(), hasItem(equalsDeeply(firstRootElement))); + assertThat(view.getRootObjects(), hasItem(equalsDeeply(secondRootElement))); + } + } + + /** + * This test validates that if there are two resources with the root of one of + * them being contained in the other resource (like a Java compilation unit + * being root of a resource but also contained in its packages represented in a + * further resource), the root element that is also contained in the other + * resource is not duplicated but copied properly. + * + * @throws Exception + */ + @Test + @DisplayName("for two resources with containment in between") + public void forTwoResourcesWithContainmentInBetween() throws Exception { + Resource firstResource = testResourceSet.createResource(URI.createURI("test://test.aet")); + Resource secondResource = testResourceSet.createResource(URI.createURI("test://test2.aet")); + Root firstRootElement = aet.Root(); + firstRootElement.setId("firstElementId"); + Root secondRootElement = aet.Root(); + secondRootElement.setId("secondElementId"); + firstRootElement.setRecursiveRoot(secondRootElement); + firstResource.getContents().add(firstRootElement); + secondResource.getContents().add(secondRootElement); + ChangeableViewSource viewSource = mock(ChangeableViewSource.class); + when(viewSource.getViewSourceModels()).thenReturn(Set.of(firstResource, secondResource)); + DirectViewElementSelector selector = basicViewType.createSelector(viewSource); + selector.setSelected(firstRootElement, true); + selector.setSelected(secondRootElement, true); + try (View view = basicViewType.createView(selector)) { + assertThat(view.getRootObjects().size(), is(2)); + assertThat(view.getRootObjects(), hasItem(equalsDeeply(firstRootElement))); + assertThat(view.getRootObjects(), hasItem(equalsDeeply(secondRootElement))); + Root rootWithContainment = FluentIterable.from(view.getRootObjects(Root.class)) + .filter((root) -> root.getRecursiveRoot() != null).first().get(); + Root rootWithoutContainment = FluentIterable.from(view.getRootObjects(Root.class)) + .filter((root) -> root.getRecursiveRoot() == null).first().get(); + assertThat(rootWithContainment.getRecursiveRoot(), is(rootWithoutContainment)); + } + } + + } + + @Nested + @DisplayName("update view") + class UpdateView { + private IdentityMappingViewType basicViewType; + private ResourceSet testResourceSet; + private ChangeableViewSource viewSource; + + @BeforeEach + public void initializeViewTypeAndResourceSetAndViewSource() { + this.basicViewType = new IdentityMappingViewType("name"); + this.testResourceSet = withGlobalFactories(new ResourceSetImpl()); + this.viewSource = mock(ChangeableViewSource.class); + when(viewSource.getViewSourceModels()).thenReturn(testResourceSet.getResources()); + } + + private Root createResourceWithSingleRoot(URI uri) { + Resource resource = testResourceSet.createResource(uri); + Root rootElement = aet.Root(); + rootElement.setId("testid"); + resource.getContents().add(rootElement); + return rootElement; + } + + @Test + @DisplayName("adding a non-root element") + public void addingANonRootElement() throws Exception { + Root root = createResourceWithSingleRoot(URI.createURI("test://test.aet")); + DirectViewElementSelector selector = basicViewType.createSelector(viewSource); + selector.getSelectableElements().forEach((element) -> selector.setSelected(element, true)); + try (ModifiableView view = basicViewType.createView(selector)) { + root.setSingleValuedContainmentEReference(aet.NonRoot()); + assertThat((view.getRootObjects(Root.class).iterator().next()).getSingleValuedContainmentEReference(), + equalTo(null)); + basicViewType.updateView(view); + assertThat(view.getRootObjects().size(), is(1)); + assertThat((view.getRootObjects(Root.class).iterator().next()).getSingleValuedContainmentEReference(), + is(anything())); + } + } + + @Test + @DisplayName("adding a root element") + public void addingARootElement() throws Exception { + Root root = createResourceWithSingleRoot(URI.createURI("test://test.aet")); + DirectViewElementSelector selector = basicViewType.createSelector(viewSource); + selector.getSelectableElements().forEach((element) -> selector.setSelected(element, true)); + try (ModifiableView view = basicViewType.createView(selector)) { + root.setId("secondId"); + Root secondRoot = createResourceWithSingleRoot(URI.createURI("test://test2.aet")); + basicViewType.updateView(view); + assertThat(view.getRootObjects().size(), is(1)); + assertThat(view.getRootObjects(), not(hasItem(equalsDeeply(secondRoot)))); + } + } + + @Test + @DisplayName("removing a selected root element") + public void removingSelectedRoot() throws Exception { + Root root = createResourceWithSingleRoot(URI.createURI("test://test.aet")); + DirectViewElementSelector selector = basicViewType.createSelector(viewSource); + selector.getSelectableElements().forEach((element) -> selector.setSelected(element, true)); + try (ModifiableView view = basicViewType.createView(selector)) { + EcoreUtil.delete(root); + assertThat(view.getRootObjects().size(), is(1)); + basicViewType.updateView(view); + assertThat(view.getRootObjects().size(), is(0)); + } + } + + @Test + @DisplayName("removing an unselected root element") + public void removingUnselectedRoot() throws Exception { + Root firstRoot = createResourceWithSingleRoot(URI.createURI("test://test.aet")); + Root secondRoot = createResourceWithSingleRoot(URI.createURI("test://test2.aet")); + DirectViewElementSelector selector = basicViewType.createSelector(viewSource); + selector.setSelected(firstRoot, true); + try (ModifiableView view = basicViewType.createView(selector)) { + EcoreUtil.delete(secondRoot); + assertThat(view.getRootObjects().size(), is(1)); + basicViewType.updateView(view); + assertThat(view.getRootObjects().size(), is(1)); + assertThat(view.getRootObjects(), hasItem(equalsDeeply(firstRoot))); + } + } + } + + @Nested + @DisplayName("commit view changes") + class CommitViewChanges { + private IdentityMappingViewType basicViewType; + private ResourceSet viewSourceResourceSet; + private ResourceSet viewResourceSet; + private ModifiableView view; + private ViewSelection viewSelection; + private ChangeableViewSource viewSource; + private UuidResolver uuidResolver; + + @BeforeEach + void initializeViewTypeAndResourceSetAndViewSource() { + this.basicViewType = new IdentityMappingViewType("name"); + this.viewSourceResourceSet = withGlobalFactories(new ResourceSetImpl()); + this.viewResourceSet = withGlobalFactories(new ResourceSetImpl()); + this.view = mock(ModifiableView.class); + this.viewSource = mock(ChangeableViewSource.class); + this.viewSelection = mock(ViewSelection.class); + this.uuidResolver = UuidResolver.create(viewSourceResourceSet); + when(view.getViewSource()).thenReturn(viewSource); + when(view.getSelection()).thenReturn(viewSelection); + when(viewSource.getViewSourceModels()).thenReturn(viewSourceResourceSet.getResources()); + when(viewSource.getUuidResolver()).thenReturn(uuidResolver); + when(viewSelection.isViewObjectSelected(any(EObject.class))).thenReturn(true); + } + + private Root createResourceWithSingleRoot(URI uri) { + Resource resource = viewSourceResourceSet.createResource(uri); + Root rootElement = aet.Root(); + uuidResolver.registerEObject(rootElement); + rootElement.setId("testid"); + resource.getContents().add(rootElement); + + Resource viewResource = viewResourceSet.createResource(uri); + Root viewRootElement = EcoreUtil.copy(rootElement); + viewResource.getContents().add(viewRootElement); + return viewRootElement; + } + + @Test + @DisplayName("with null changes") + void withNull() { + assertThrows(NullPointerException.class, () -> basicViewType.commitViewChanges(view, null)); + } + + @Test + @DisplayName("with null view") + void withNullView() { + VitruviusChange someChange = VitruviusChangeFactory.getInstance() + .createTransactionalChange(Set.of()); + assertThrows(NullPointerException.class, () -> basicViewType.commitViewChanges(null, someChange)); + } + + @ParameterizedTest + @MethodSource("testEmptyChanges") + @DisplayName("with empty changes") + void withEmptyChanges(VitruviusChange change) { + ArgumentCaptor> changeArgument = ArgumentCaptor.forClass(VitruviusChange.class); + basicViewType.commitViewChanges(view, change); + verify(viewSource).propagateChange(changeArgument.capture()); + assertEquals(changeArgument.getValue(), change); + } + + private static Stream> testEmptyChanges() { + VitruviusChangeFactory factory = VitruviusChangeFactory.getInstance(); + return Stream.of(factory.createTransactionalChange(Set.of()), factory.createCompositeChange(Set.of())); + } + + @Test + @DisplayName("with non-empty change") + void withNonEmptyChange() { + Root root = createResourceWithSingleRoot(URI.createURI("test://test.aet")); + try (ChangeRecorder changeRecorder = new ChangeRecorder(viewResourceSet)) { + changeRecorder.addToRecording(root); + changeRecorder.beginRecording(); + root.setId("testid2"); + changeRecorder.endRecording(); + VitruviusChange change = changeRecorder.getChange(); + VitruviusChange idAssignedChange = assignIds(change); + + ArgumentCaptor> changeArgument = ArgumentCaptor.forClass(VitruviusChange.class); + basicViewType.commitViewChanges(view, idAssignedChange); + verify(viewSource).propagateChange(changeArgument.capture()); + List> eChanges = changeArgument.getValue().getEChanges(); + assertThat(eChanges.size(), is(1)); + assertThat(eChanges.get(0), instanceOf(ReplaceSingleValuedEAttribute.class)); + var attributeChange = (ReplaceSingleValuedEAttribute) eChanges.get(0); + assertThat(attributeChange.getOldValue(), is("testid")); + assertThat(attributeChange.getNewValue(), is("testid2")); + } + } + + private VitruviusChange assignIds(VitruviusChange change) { + VitruviusChangeResolver idChangeResolver = VitruviusChangeResolver + .forHierarchicalIds(viewResourceSet); + return idChangeResolver.assignIds(change); + } + } +} diff --git a/views/src/test/java/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java b/views/src/test/java/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java new file mode 100644 index 0000000000..d6c4e06fd1 --- /dev/null +++ b/views/src/test/java/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java @@ -0,0 +1,279 @@ +package tools.vitruv.framework.views.selection; + +import static java.util.Collections.emptySet; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.eclipse.emf.ecore.EObject; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import allElementTypes.Root; +import tools.vitruv.framework.views.ModifiableViewSelection; +import tools.vitruv.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.testutils.TestLogging; + +@ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) +public class ElementViewSelectionTest { + @Nested + @DisplayName("inialize") + class Initialize { + @Test + @DisplayName("empty") + public void empty() { + ModifiableViewSelection selection = new ElementViewSelection(emptySet()); + assertThat(selection.getSelectableElements(), is(emptySet())); + } + + @Test + @DisplayName("with single element") + public void withSingleElement() { + Root root = aet.Root(); + ModifiableViewSelection selection = new ElementViewSelection(Set.of(root)); + assertThat(selection.getSelectableElements(), is(Set.of(root))); + } + + @Test + @DisplayName("with multiple elements") + public void withMultipleElements() { + Root firstRoot = aet.Root(); + Root secondRoot = aet.Root(); + ModifiableViewSelection selection = new ElementViewSelection(Set.of(firstRoot, secondRoot)); + assertThat(selection.getSelectableElements(), is(Set.of(firstRoot, secondRoot))); + } + } + + @Nested + @DisplayName("selectable") + class Selectable { + ModifiableViewSelection selection; + List selectableElements; + + @BeforeEach + public void setupSelectionWithTwoElements() { + selectableElements = new ArrayList<>(); + selectableElements.add(aet.Root()); + selectableElements.add(aet.Root()); + selection = new ElementViewSelection(selectableElements); + } + + @Test + @DisplayName("element in selection") + public void elementInSelection() { + assertThat("all added elements must be selectable", + selectableElements.stream().allMatch(element -> selection.isSelectable(element))); + } + + @Test + @DisplayName("element not in selection") + public void elementNotInSelection() { + assertThat("elements not added to selection must not be selectable", !selection.isSelectable(aet.Root())); + } + + @Test + @DisplayName("null element") + public void nullElement() { + assertThat("null elements must not be selectable", !selection.isSelectable(null)); + } + + } + + @Nested + @DisplayName("select") + class Select { + ModifiableViewSelection selection; + List selectableElements; + + @BeforeEach + public void setupSelectionWithTwoElements() { + selectableElements = new ArrayList<>(); + selectableElements.add(aet.Root()); + selectableElements.add(aet.Root()); + selection = new ElementViewSelection(selectableElements); + } + + @Test + @DisplayName("no element") + public void noElement() { + assertThat("unselected elements must not be selected by default", + !selection.isSelected(selectableElements.get(0))); + assertThat("unselected elements must not be selected by default", + !selection.isSelected(selectableElements.get(1))); + } + + @Test + @DisplayName("first element") + public void firstElement() { + selection.setSelected(selectableElements.get(0), true); + assertThat("element must be selected after selection", selection.isSelected(selectableElements.get(0))); + assertThat("unselected elements must not be selected by default", + !selection.isSelected(selectableElements.get(1))); + } + + @Test + @DisplayName("second element") + public void secondElement() { + selection.setSelected(selectableElements.get(1), true); + assertThat("unselected elements must not be selected by default", + !selection.isSelected(selectableElements.get(0))); + assertThat("element must be selected after selection", selection.isSelected(selectableElements.get(1))); + } + + @Test + @DisplayName("all elements") + public void allElements() { + selection.setSelected(selectableElements.get(0), true); + selection.setSelected(selectableElements.get(1), true); + assertThat("element must be selected after selection", selection.isSelected(selectableElements.get(0))); + assertThat("element must be selected after selection", selection.isSelected(selectableElements.get(1))); + } + + @Test + @DisplayName("element that is not selectable") + public void unselectableElement() { + assertThrows(IllegalStateException.class, () -> selection.setSelected(aet.Root(), true)); + } + + } + + @Nested + @DisplayName("unselect") + class Unselect { + ModifiableViewSelection selection; + List selectableElements; + + @BeforeEach + public void setupSelectionWithTwoSelectedElements() { + selectableElements = new ArrayList<>(); + selectableElements.add(aet.Root()); + selectableElements.add(aet.Root()); + selection = new ElementViewSelection(selectableElements); + selectableElements.forEach(element -> selection.setSelected(element, true)); + } + + @Test + @DisplayName("first element") + public void firstElement() { + selection.setSelected(selectableElements.get(0), false); + assertThat("element must be unselected after deselection", + !selection.isSelected(selectableElements.get(0))); + assertThat("element must be selected without deselection", selection.isSelected(selectableElements.get(1))); + } + + @Test + @DisplayName("second element") + public void secondElement() { + selection.setSelected(selectableElements.get(1), false); + assertThat("element must be selected without deselection", selection.isSelected(selectableElements.get(0))); + assertThat("element must be unselected after deselection", + !selection.isSelected(selectableElements.get(1))); + } + + @Test + @DisplayName("all elements") + public void allElements() { + selection.setSelected(selectableElements.get(0), false); + selection.setSelected(selectableElements.get(1), false); + assertThat("element must be unselected after deselection", + !selection.isSelected(selectableElements.get(0))); + assertThat("element must be unselected after deselection", + !selection.isSelected(selectableElements.get(1))); + } + + @Test + @DisplayName("element that is not selectable") + public void unselectableElement() { + assertThrows(IllegalStateException.class, () -> selection.setSelected(aet.Root(), false)); + } + + } + + @Nested + @DisplayName("validate view element selection") + class ViewElementSelection { + ModifiableViewSelection selection; + List selectableElements; + + @BeforeEach + public void setupSelectionWithFirstOfTwoElementsSelected() { + selectableElements = new ArrayList<>(); + selectableElements.add(aet.Root()); + selectableElements.add(aet.Root()); + selection = new ElementViewSelection(selectableElements); + selection.setSelected(selectableElements.get(0), true); + } + + @Test + @DisplayName("matching selected element") + public void matchingSelectedElement() { + assertThat("view element must be validated as selected after selecting it", + selection.isViewObjectSelected(selectableElements.get(0))); + } + + @Test + @DisplayName("matching unselected element") + public void matchingUnselectedElement() { + assertThat("view element must be validated as unselected when not selecting it", + !selection.isViewObjectSelected(selectableElements.get(1))); + } + + @Test + @DisplayName("matching no element") + public void matchingNoElement() { + assertThat("view element must be validated as unselected when it cannot be selected", + !selection.isViewObjectSelected(aet.Root())); + } + + } + + @Nested + @DisplayName("copy") + class Copy { + List selectableElements; + + @BeforeEach + public void setupSelectionWithFirstOfTwoElementsSelected() { + selectableElements = new ArrayList<>(); + selectableElements.add(aet.Root()); + selectableElements.add(aet.Root()); + } + + @Test + @DisplayName("empty") + public void empty() { + ModifiableViewSelection originalSelection = new ElementViewSelection(emptySet()); + ModifiableViewSelection copy = new ElementViewSelection(originalSelection); + assertThat(copy.getSelectableElements(), is(emptySet())); + } + + @Test + @DisplayName("with single element") + public void withSingleElement() { + Root root = aet.Root(); + ModifiableViewSelection originalSelection = new ElementViewSelection(Set.of(root)); + ModifiableViewSelection copy = new ElementViewSelection(originalSelection); + assertThat(copy.getSelectableElements(), is(Set.of(root))); + } + + @Test + @DisplayName("with multiple elements") + public void withMultipleElements() { + Root firstRoot = aet.Root(); + Root secondRoot = aet.Root(); + ModifiableViewSelection originalSelection = new ElementViewSelection(Set.of(firstRoot, secondRoot)); + ModifiableViewSelection copy = new ElementViewSelection(originalSelection); + assertThat(copy.getSelectableElements(), is(Set.of(firstRoot, secondRoot))); + } + + } + +} diff --git a/views/src/test/java/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java b/views/src/test/java/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java new file mode 100644 index 0000000000..a099cef99d --- /dev/null +++ b/views/src/test/java/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java @@ -0,0 +1,165 @@ +package tools.vitruv.framework.views.selectors; + +import static java.util.Collections.emptySet; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.eclipse.emf.ecore.EObject; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import allElementTypes.Root; +import tools.vitruv.change.atomic.hid.HierarchicalId; +import tools.vitruv.framework.views.ChangeableViewSource; +import tools.vitruv.framework.views.ViewSelector; +import tools.vitruv.framework.views.impl.ModifiableView; +import tools.vitruv.framework.views.impl.ViewCreatingViewType; +import tools.vitruv.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.testutils.TestLogging; + +@ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) +public class DirectViewElementSelectorTest { + @Mock + ViewCreatingViewType, HierarchicalId> mockViewType; + + @Mock + ChangeableViewSource mockViewSource; + + @BeforeEach + public void initializeMocks() { + MockitoAnnotations.openMocks(this); + } + + @Nested + @DisplayName("initalize") + class Initialize { + @Nested + @DisplayName("with null elements") + class WithNullElements { + @Test + @DisplayName("with null view type") + public void nullViewType() { + assertThrows(IllegalArgumentException.class, + () -> new DirectViewElementSelector<>(null, mockViewSource, emptySet())); + } + + @Test + @DisplayName("with null view source") + public void nullViewSource() { + assertThrows(IllegalArgumentException.class, + () -> new DirectViewElementSelector<>(mockViewType, null, emptySet())); + } + + @Test + @DisplayName("with null selectable elements") + public void nullElements() { + assertThrows(IllegalArgumentException.class, + () -> new DirectViewElementSelector<>(mockViewType, mockViewSource, null)); + } + } + + @Test + @DisplayName("with no selectable elements") + public void empty() { + ViewSelector selector = new DirectViewElementSelector<>(mockViewType, mockViewSource, emptySet()); + assertThat(selector.getSelectableElements(), is(emptySet())); + assertThat("BasicViewElementSelectors must always be valid", selector.isValid()); + } + + @Test + @DisplayName("with single selectable element") + public void withSingleSelectableElement() { + Root root = aet.Root(); + ViewSelector selector = new DirectViewElementSelector<>(mockViewType, mockViewSource, Set.of(root)); + assertThat(selector.getSelectableElements(), is(Set.of(root))); + assertThat("BasicViewElementSelectors must always be valid", selector.isValid()); + } + + @Test + @DisplayName("with multiple selectable elements") + public void withMultipleSelectableElements() { + Root firstRoot = aet.Root(); + Root secondRoot = aet.Root(); + ViewSelector selector = new DirectViewElementSelector<>(mockViewType, mockViewSource, + Set.of(firstRoot, secondRoot)); + assertThat(selector.getSelectableElements(), is(Set.of(firstRoot, secondRoot))); + assertThat("BasicViewElementSelectors must always be valid", selector.isValid()); + } + + } + + @Nested + @DisplayName("provides view selection") + class ViewSelection { + ViewSelector selector; + List selectableElements; + + @BeforeEach + public void setupSelectionWithFirstOfTwoElementsSelected() { + selectableElements = new ArrayList<>(); + selectableElements.add(aet.Root()); + selectableElements.add(aet.Root()); + selector = new DirectViewElementSelector<>(mockViewType, mockViewSource, selectableElements); + selector.setSelected(selectableElements.get(0), true); + assertThat("BasicViewElementSelectors must always be valid", selector.isValid()); + } + + @Test + @DisplayName("matching selected element") + public void matchingSelectedElement() { + assertThat("view element must be validated as selected after selecting it", + selector.getSelection().isViewObjectSelected(selectableElements.get(0))); + } + + @Test + @DisplayName("matching unselected element") + public void matchingUnselectedElement() { + assertThat("view element must be validated as unselected when not selecting it", + !selector.getSelection().isViewObjectSelected(selectableElements.get(1))); + } + + @Test + @DisplayName("matching no element") + public void matchingNoElement() { + assertThat("view element must be validated as unselected when it cannot be selected", + !selector.getSelection().isViewObjectSelected(aet.Root())); + } + } + + @Nested + @DisplayName("returns") + class Returns { + @Test + @DisplayName("view created from view type") + public void createView() { + DirectViewElementSelector selector = new DirectViewElementSelector<>(mockViewType, + mockViewSource, emptySet()); + ModifiableView view = mock(ModifiableView.class); + when(mockViewType.createView(selector)).thenReturn(view); + assertThat(selector.createView(), is(view)); + } + + @Test + @DisplayName("view source") + public void getViewSource() { + DirectViewElementSelector selector = new DirectViewElementSelector<>(mockViewType, + mockViewSource, emptySet()); + assertThat(selector.getViewSource(), is(mockViewSource)); + } + + } + +} diff --git a/views/src/test/java/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java b/views/src/test/java/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java new file mode 100644 index 0000000000..b5ea0cf865 --- /dev/null +++ b/views/src/test/java/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java @@ -0,0 +1,111 @@ +package tools.vitruv.framework.views.util; + +import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.common.util.URIUtil.createFileURI; +import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static tools.vitruv.testutils.metamodels.UmlMockupCreators.uml; + +import java.io.File; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.ecore.xmi.XMLResource; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import edu.kit.ipd.sdq.commons.util.java.Pair; +import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceCopier; +import tools.vitruv.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.testutils.TestLogging; +import tools.vitruv.testutils.TestProject; +import tools.vitruv.testutils.TestProjectManager; +import uml_mockup.Identified; +import uml_mockup.UClass; +import uml_mockup.UPackage; + +@ExtendWith({ TestProjectManager.class, TestLogging.class, RegisterMetamodelsInStandalone.class }) +public class XmiIdEdgeCaseTest { + private ResourceSet resourceSet; + private Path testProjectFolder; + + @BeforeEach + void setup(@TestProject Path testProjectFolder) { + this.testProjectFolder = testProjectFolder; + resourceSet = withGlobalFactories(new ResourceSetImpl()); + } + + @Test + public void testSingleResourceCopy() { + Pair> umlResourcePair = createPopulatedUmlResourceAndIdMapping("my"); + ResourceSet copyResourceSet = new ResourceSetImpl(); + XMLResource copiedModel = (XMLResource) ResourceCopier.copyViewResource(umlResourcePair.get0(), + copyResourceSet); + validateIds(copiedModel, umlResourcePair.get1()); + } + + @Test + public void testMultiResourceCopy() { + Pair> umlResourcePair = createPopulatedUmlResourceAndIdMapping("my1"); + Pair> umlResourcePair2 = createPopulatedUmlResourceAndIdMapping("my2"); + Pair> umlResourcePair3 = createPopulatedUmlResourceAndIdMapping("my3"); + + ResourceSet copyResourceSet = new ResourceSetImpl(); + Map copiedModels = ResourceCopier.copyViewResources( + List.of(umlResourcePair.get0(), umlResourcePair2.get0(), umlResourcePair3.get0()), copyResourceSet); + XMLResource copiedModel = (XMLResource) copiedModels.get(umlResourcePair.get0()); + XMLResource copiedModel2 = (XMLResource) copiedModels.get(umlResourcePair2.get0()); + XMLResource copiedModel3 = (XMLResource) copiedModels.get(umlResourcePair3.get0()); + assertNotNull(copiedModel, "copy for uml model is missing"); + assertNotNull(copiedModel2, "copy for uml model 2 is missing"); + assertNotNull(copiedModel3, "copy for uml model 3 is missing"); + validateIds(copiedModel, umlResourcePair.get1()); + validateIds(copiedModel2, umlResourcePair2.get1()); + validateIds(copiedModel3, umlResourcePair3.get1()); + } + + private Pair> createPopulatedUmlResourceAndIdMapping(String name) { + XMLResource umlResource = (XMLResource) resourceSet.createResource(getModelURI(name + ".uml_mockup")); + UPackage uPackage1 = uml.Package(); + umlResource.getContents().add(uPackage1); + uPackage1.setName("Package1"); + UClass uClass1 = uml.Class(); + uPackage1.getClasses().add(uClass1); + + UPackage uPackage2 = uml.Package(); + umlResource.getContents().add(uPackage2); + uPackage2.setName("Package2"); + UClass uClass2 = uml.Class(); + uPackage2.getClasses().add(uClass2); + + Map expectedIdMapping = Map.of( // + name + "_package-1", uPackage1, // + name + "_package-2", uPackage2, // + name + "_class-1", uClass1, // + name + "_class-2", uClass2 // + ); + expectedIdMapping.forEach((id, obj) -> umlResource.setID(obj, id)); + return new Pair>(umlResource, expectedIdMapping); + } + + private void validateIds(Resource copiedResource, Map expectedIdMapping) { + expectedIdMapping.forEach((id, object) -> { + EObject copiedObject = copiedResource.getEObject(id); + assertNotNull(copiedObject, "could not find element with id " + id); + assertEquals(((Identified) object).getId(), ((Identified) copiedObject).getId(), + "retrieved incorrect element for id " + id + "\nexpected: " + object + ", actual: " + copiedObject); + }); + } + + protected URI getModelURI(String modelFileName) { + File file = testProjectFolder.resolve("model").resolve(modelFileName).toFile(); + return createFileURI(file); + } +} diff --git a/views/src/test/xtend/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend new file mode 100644 index 0000000000..3175d61706 --- /dev/null +++ b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend @@ -0,0 +1,355 @@ +package tools.vitruv.framework.views.changederivation + +import allElementTypes.Root +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import tools.vitruv.change.atomic.eobject.CreateEObject +import tools.vitruv.change.atomic.eobject.DeleteEObject +import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute +import tools.vitruv.change.atomic.root.InsertRootEObject +import tools.vitruv.change.atomic.root.RemoveRootEObject +import tools.vitruv.change.composite.description.VitruviusChangeResolver +import tools.vitruv.testutils.Capture + +import static org.hamcrest.CoreMatchers.instanceOf +import static org.hamcrest.MatcherAssert.assertThat +import static org.junit.jupiter.api.Assertions.assertEquals +import static org.junit.jupiter.api.Assertions.assertTrue +import static tools.vitruv.testutils.matchers.ModelMatchers.containsModelOf +import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet + +import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories +import static extension tools.vitruv.testutils.Capture.operator_doubleGreaterThan + +class BasicStateChangePropagationTest extends StateChangePropagationTest { + private def getTestUri() { + getModelURI("Test.allElementTypes") + } + + @ParameterizedTest() + @DisplayName("create new resource and calculate state-based difference") + @MethodSource("strategiesToTest") + def void createNewResource(StateBasedChangeResolutionStrategy strategyToTest) { + val modelResource = new ResourceSetImpl().createResource(testUri) => [ + contents += aet.Root => [ + id = "Root" + ] + ] + + val changes = strategyToTest.getChangeSequenceForCreated(modelResource) + assertEquals(3, changes.EChanges.size) + assertEquals(1, changes.EChanges.filter(InsertRootEObject).size) + assertEquals(1, changes.EChanges.filter(CreateEObject).size) + assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) + + // Create empty resource to apply generated changes to + val validationResourceSet = new ResourceSetImpl() + VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(changes) + + modelResource.save(null) + assertEquals(1, validationResourceSet.resources.size) + assertThat(validationResourceSet.resources.get(0), containsModelOf(modelResource)) + } + + @ParameterizedTest() + @DisplayName("delete existing resource and calculate state-based difference") + @MethodSource("strategiesToTest") + def void deleteResource(StateBasedChangeResolutionStrategy strategyToTest) { + val modelResource = new Capture + resourceSet.record [ + createResource(testUri) => [ + contents += aet.Root => [ + id = "Root" + ] + ] >> modelResource + ] + (-modelResource).save(null) + val changes = strategyToTest.getChangeSequenceForDeleted(-modelResource) + assertEquals(2, changes.EChanges.size) + assertEquals(1, changes.EChanges.filter(RemoveRootEObject).size) + assertEquals(1, changes.EChanges.filter(DeleteEObject).size) + + // Load resource to apply generated changes to + val validationResourceSet = new ResourceSetImpl() + validationResourceSet.getResource(testUri, true) + VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(changes) + + assertEquals(1, validationResourceSet.resources.size) + assertTrue(validationResourceSet.resources.get(0).contents.empty) + } + + @ParameterizedTest() + @DisplayName("replace root element and calculate state-based difference") + @MethodSource("strategiesToTest") + def void replaceRootElement(StateBasedChangeResolutionStrategy strategyToTest) { + val modelResource = new Capture + resourceSet.record [ + createResource(testUri) => [ + contents += aet.Root => [ + id = "Root" + ] + ] >> modelResource + ] + (-modelResource).save(null) + + (-modelResource).record [ + contents.clear() + contents += aet.Root => [ + id = "Root2" + ] + ] + + val validationResourceSet = new ResourceSetImpl().withGlobalFactories() + val oldState = validationResourceSet.getResource(testUri, true) + val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) + + VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(changes) + + assertEquals(1, validationResourceSet.resources.size) + assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) + } + + @ParameterizedTest() + @DisplayName("change a root element property and calculate state-based difference") + @MethodSource("strategiesToTest") + def void changeRootElementFeature(StateBasedChangeResolutionStrategy strategyToTest) { + val modelResource = new Capture + val root = aet.Root + resourceSet.record [ + createResource(testUri) => [ + contents += root => [ + id = "Root" + ] + ] >> modelResource + ] + (-modelResource).save(null) + + resourceSet.record [ + root.singleValuedEAttribute = 2 + ] + + val validationResourceSet = new ResourceSetImpl().withGlobalFactories() + val oldState = validationResourceSet.getResource(testUri, true) + val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) + assertEquals(1, changes.EChanges.size) + assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) + + VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(changes) + + assertEquals(1, validationResourceSet.resources.size) + assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) + } + + @ParameterizedTest() + @DisplayName("change a root element's id and calculate state-based difference") + @MethodSource("strategiesToTest") + def void changeRootElementId(DefaultStateBasedChangeResolutionStrategy strategyToTest) { + val modelResource = new Capture + val root = aet.Root + resourceSet.record [ + createResource(testUri) => [ + contents += root => [ + id = "Root" + ] + ] >> modelResource + ] + (-modelResource).save(null) + + resourceSet.record [ + root.id = "Root2" + ] + + val validationResourceSet = new ResourceSetImpl().withGlobalFactories() + val oldState = validationResourceSet.getResource(testUri, true) + val unresolvedChanges = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) + val changes = VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(unresolvedChanges) + switch (strategyToTest.useIdentifiers) { + case ONLY, + case WHEN_AVAILABLE: { + val Iterable> deleteChanges = newArrayList(changes.EChanges.filter(DeleteEObject)) + assertEquals(1, deleteChanges.size) + assertThat(deleteChanges.head.affectedElement, instanceOf(Root)) + assertEquals("Root", (deleteChanges.head.affectedElement as Root).id) + + val Iterable> createChanges = newArrayList(changes.EChanges.filter(CreateEObject)) + assertEquals(1, createChanges.size) + assertThat(createChanges.head.affectedElement, instanceOf(Root)) + assertEquals("Root2", (createChanges.head.affectedElement as Root).id) + } + case NEVER: { + assertEquals(1, changes.EChanges.size) + assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) + } + } + + assertEquals(1, validationResourceSet.resources.size) + assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) + } + + @ParameterizedTest() + @DisplayName("change a non-root element property and calculate state-based difference") + @MethodSource("strategiesToTest") + def void changeNonRootElementFeature(StateBasedChangeResolutionStrategy strategyToTest) { + val modelResource = new Capture + val root = aet.Root + val containedRoot = aet.Root + resourceSet.record [ + createResource(testUri) => [ + contents += root => [ + id = "Root" + recursiveRoot = containedRoot => [ + id = "ContainedRoot" + singleValuedEAttribute = 0 + ] + ] + ] >> modelResource + ] + (-modelResource).save(null) + + resourceSet.record [ + containedRoot.singleValuedEAttribute = 1 + ] + + val validationResourceSet = new ResourceSetImpl().withGlobalFactories() + val oldState = validationResourceSet.getResource(testUri, true) + val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) + assertEquals(1, changes.EChanges.size) + assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) + + VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(changes) + + assertEquals(1, validationResourceSet.resources.size) + assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) + } + + @ParameterizedTest() + @DisplayName("change a non-root element's id and calculate state-based difference") + @MethodSource("strategiesToTest") + def void changeNonRootElementId(DefaultStateBasedChangeResolutionStrategy strategyToTest) { + val modelResource = new Capture + val root = aet.Root + val containedRoot = aet.Root + resourceSet.record [ + createResource(testUri) => [ + contents += root => [ + id = "Root" + recursiveRoot = containedRoot => [ + id = "ContainedRoot" + singleValuedEAttribute = 0 + ] + ] + ] >> modelResource + ] + (-modelResource).save(null) + + resourceSet.record [ + containedRoot.id = "ContainedRoot2" + ] + + val validationResourceSet = new ResourceSetImpl().withGlobalFactories() + val oldState = validationResourceSet.getResource(testUri, true) + val unresolvedChanges = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) + val changes = VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(unresolvedChanges) + switch (strategyToTest.useIdentifiers) { + case ONLY, + case WHEN_AVAILABLE: { + val Iterable> deleteChanges = newArrayList(changes.EChanges.filter(DeleteEObject)) + assertEquals(1, deleteChanges.size) + assertThat(deleteChanges.head.affectedElement, instanceOf(Root)) + assertEquals("ContainedRoot", (deleteChanges.head.affectedElement as Root).id) + + val Iterable> createChanges = newArrayList(changes.EChanges.filter(CreateEObject)) + assertEquals(1, createChanges.size) + assertThat(createChanges.head.affectedElement, instanceOf(Root)) + assertEquals("ContainedRoot2", (createChanges.head.affectedElement as Root).id) + } + case NEVER: { + assertEquals(1, changes.EChanges.size) + assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) + } + } + + assertEquals(1, validationResourceSet.resources.size) + assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) + } + + @ParameterizedTest() + @DisplayName("move a resource to new location and calculate state-based difference") + @MethodSource("strategiesToTest") + def void moveResource(StateBasedChangeResolutionStrategy strategyToTest) { + val modelResource = new Capture + val root = aet.Root + resourceSet.record [ + createResource(testUri) => [ + contents += root => [ + id = "Root" + ] + ] >> modelResource + ] + (-modelResource).save(null) + + val validationResourceSet = new ResourceSetImpl().withGlobalFactories() + val oldState = validationResourceSet.getResource(testUri, true) + + val movedResourceUri = getModelURI("moved.allElementTypes") + resourceSet.record [ + createResource(movedResourceUri) => [ + contents += root + ] >> modelResource + ] + + val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) + assertEquals(2, changes.EChanges.size) + assertEquals(1, changes.EChanges.filter(RemoveRootEObject).size) + assertEquals(1, changes.EChanges.filter(InsertRootEObject).size) + + VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(changes) + + (-modelResource).save(null) + assertEquals(2, validationResourceSet.resources.size) + assertThat(validationResourceSet.getResource(movedResourceUri, false), containsModelOf(-modelResource)) + } + + @ParameterizedTest() + @DisplayName("move a resource to new location changing root feature and calculate state-based difference") + @MethodSource("strategiesToTest") + def void moveResourceAndChangeRootFeature(StateBasedChangeResolutionStrategy strategyToTest) { + val modelResource = new Capture + val root = aet.Root + resourceSet.record [ + createResource(testUri) => [ + contents += root => [ + id = "Root" + ] + ] >> modelResource + ] + (-modelResource).save(null) + + val validationResourceSet = new ResourceSetImpl().withGlobalFactories() + val oldState = validationResourceSet.getResource(testUri, true) + + val movedResourceUri = getModelURI("moved.allElementTypes") + resourceSet.record [ + (-modelResource).contents -= root + root.singleValuedEAttribute = 2 + createResource(movedResourceUri) => [ + contents += root + ] >> modelResource + ] + + val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) + assertEquals(3, changes.EChanges.size) + assertEquals(1, changes.EChanges.filter(RemoveRootEObject).size) + assertEquals(1, changes.EChanges.filter(InsertRootEObject).size) + assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) + + VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(changes) + + (-modelResource).save(null) + assertEquals(2, validationResourceSet.resources.size) + assertThat(validationResourceSet.getResource(movedResourceUri, false), containsModelOf(-modelResource)) + } +} diff --git a/views/src/test/xtend/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend new file mode 100644 index 0000000000..e5d30778eb --- /dev/null +++ b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend @@ -0,0 +1,41 @@ +package tools.vitruv.framework.views.changederivation + +import org.eclipse.emf.ecore.resource.Resource +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource + +import static org.junit.jupiter.api.Assertions.assertThrows + +class EdgeCaseStateChangeTest extends StateChangePropagationTest { + + /** + * Tests the comparison of two states with no changes for the uml mockup model. + */ + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testNoUmlChange(StateBasedChangeResolutionStrategy strategyToTest) { + compareChanges(umlModel, umlCheckpoint, strategyToTest) + } + + /** + * Tests the comparison of two states with no changes for the pcm mockup model. + */ + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testNoPcmChange(StateBasedChangeResolutionStrategy strategyToTest) { + compareChanges(umlModel, umlCheckpoint, strategyToTest) + } + + /** + * Tests invalid input: null instead of state resources. + */ + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testNullResources(StateBasedChangeResolutionStrategy strategyToTest) { + val Resource nullResource = null + assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceForCreated(nullResource)] + assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceBetween(nullResource, nullResource)] + assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceForDeleted(nullResource)] + } + +} diff --git a/views/src/test/xtend/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend new file mode 100644 index 0000000000..64ac2d08d9 --- /dev/null +++ b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend @@ -0,0 +1,74 @@ +package tools.vitruv.framework.views.changederivation + +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource + +import static tools.vitruv.testutils.metamodels.PcmMockupCreators.pcm + +class PcmStateChangeTest extends StateChangePropagationTest { + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testAddComponent(StateBasedChangeResolutionStrategy strategyToTest) { + pcmRoot.components += pcm.Component => [name = "NewlyAddedComponent"] + compareChanges(pcmModel, pcmCheckpoint, strategyToTest) + } + + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testRenameComponent(StateBasedChangeResolutionStrategy strategyToTest) { + pcmRoot.components.get(0).name = "RenamedComponent" + compareChanges(pcmModel, pcmCheckpoint, strategyToTest) + } + + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testDeleteComponent(StateBasedChangeResolutionStrategy strategyToTest) { + pcmRoot.components.remove(0) + compareChanges(pcmModel, pcmCheckpoint, strategyToTest) + } + + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testAddProvidedInterface(StateBasedChangeResolutionStrategy strategyToTest) { + val newInterface = pcm.Interface => [name = "NewlyAddedInterface"] + pcmRoot.interfaces += pcm.Interface + newInterface.methods += pcm.Method => [name = "newMethod"] + pcmRoot.components.get(0).providedInterface = newInterface + compareChanges(pcmModel, pcmCheckpoint, strategyToTest) + } + + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testInterfaceWithMultipleMethods(StateBasedChangeResolutionStrategy strategyToTest) { + val newInterface = pcm.Interface => [ + name = "NewlyAddedInterface" + ] + pcmRoot.interfaces += newInterface + newInterface.methods += (0 .. 5).map [ index | + pcm.Method => [name = '''newMethod«index»'''] + ] + pcmRoot.components.get(0).providedInterface = newInterface + compareChanges(pcmModel, pcmCheckpoint, strategyToTest) + } + + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testAddDifferentProvidedInterface(StateBasedChangeResolutionStrategy strategyToTest) { + val firstInterface = pcm.Interface => [name = "NewlyAddedInterface"] + val secondInterface = pcm.Interface => [name = "NewlyAddedInterface2"] + pcmRoot.interfaces += #[firstInterface, secondInterface] + pcmRoot.components.get(0).providedInterface = firstInterface + pcmRoot.components.get(0).providedInterface = secondInterface + compareChanges(pcmModel, pcmCheckpoint, strategyToTest) + } + + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testAddMultipleInterfaces(StateBasedChangeResolutionStrategy strategyToTest) { + pcmRoot.interfaces += (1 .. 3).map [ index | + pcm.Interface => [name = '''NewlyAddedInterface«index»'''] + ] + pcmRoot.interfaces.forEach[methods += pcm.Method => [name = "newMethod"]] + compareChanges(pcmModel, pcmCheckpoint, strategyToTest) + } +} diff --git a/views/src/test/xtend/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend new file mode 100644 index 0000000000..a30d64e748 --- /dev/null +++ b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend @@ -0,0 +1,175 @@ +package tools.vitruv.framework.views.changederivation + +import java.nio.file.Path +import java.util.stream.Stream +import org.eclipse.emf.common.notify.Notifier +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.compare.utils.UseIdentifiers +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.emf.ecore.resource.ResourceSet +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl +import org.eclipse.emf.ecore.util.EcoreUtil +import org.eclipse.xtend.lib.annotations.Accessors +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Named +import org.junit.jupiter.api.^extension.ExtendWith +import pcm_mockup.Repository +import tools.vitruv.change.composite.description.VitruviusChange +import tools.vitruv.change.composite.description.VitruviusChangeResolver +import tools.vitruv.change.composite.recording.ChangeRecorder +import tools.vitruv.testutils.RegisterMetamodelsInStandalone +import tools.vitruv.testutils.TestLogging +import tools.vitruv.testutils.TestProject +import tools.vitruv.testutils.TestProjectManager +import uml_mockup.UPackage + +import static org.junit.jupiter.api.Assertions.* +import static tools.vitruv.testutils.metamodels.PcmMockupCreators.pcm +import static tools.vitruv.testutils.metamodels.UmlMockupCreators.uml + +import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.common.util.URIUtil.createFileURI +import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories + +@ExtendWith(TestProjectManager, TestLogging, RegisterMetamodelsInStandalone) +abstract class StateChangePropagationTest { + protected static final String PCM_FILE_EXT = "pcm_mockup" + protected static final String UML_FILE_EXT = "uml_mockup" + var Path testProjectFolder + @Accessors(PROTECTED_GETTER) + var Resource umlCheckpoint + @Accessors(PROTECTED_GETTER) + var Resource pcmCheckpoint + @Accessors(PROTECTED_GETTER) + var Resource umlModel + @Accessors(PROTECTED_GETTER) + var Resource pcmModel + @Accessors(PROTECTED_GETTER) + var Repository pcmRoot + @Accessors(PROTECTED_GETTER) + var UPackage umlRoot + var ChangeRecorder changeRecorder + @Accessors(PROTECTED_GETTER) + var ResourceSet resourceSet + var ResourceSet checkpointResourceSet + + /** + * Creates the strategy, sets up the test model and prepares everything for determining changes. + */ + @BeforeEach + def void setup(@TestProject Path testProjectFolder) { + this.testProjectFolder = testProjectFolder + // Setup: + resourceSet = new ResourceSetImpl().withGlobalFactories() + checkpointResourceSet = new ResourceSetImpl().withGlobalFactories() + changeRecorder = new ChangeRecorder(resourceSet) + // Create mockup models: + resourceSet.record [ + createPcmMockupModel() + createUmlMockupModel() + ] + // create model checkpoints and start recording: + umlCheckpoint = umlModel.createCheckpoint + pcmCheckpoint = pcmModel.createCheckpoint + umlModel.startRecording + pcmModel.startRecording + } + + static def strategiesToTest() { + Stream.of( + Named.of("identifiers when available", new DefaultStateBasedChangeResolutionStrategy(UseIdentifiers.WHEN_AVAILABLE)), + Named.of("only identifiers", new DefaultStateBasedChangeResolutionStrategy(UseIdentifiers.ONLY)), + Named.of("never identifiers", new DefaultStateBasedChangeResolutionStrategy(UseIdentifiers.NEVER)) + ) + } + + /** + * Stops recording in case the test does not call getRecordedChanges() or getChangeFromComparisonWithCheckpoint(). + */ + @AfterEach + def stopRecording() { + changeRecorder.close() + } + + /** + * USE THIS METHOD TO COMPARE RESULTS! + * Compares two changes: The recorded change sequence and the resolved changes by the state delta based strategy. + */ + protected def compareChanges(Resource model, Resource checkpoint, StateBasedChangeResolutionStrategy strategyToTest) { + model.save(null) + val deltaBasedChange = resourceSet.endRecording + val unresolvedStateBasedChange = strategyToTest.getChangeSequenceBetween(model, checkpoint) + assertNotNull(unresolvedStateBasedChange) + val stateBasedChange = VitruviusChangeResolver.forHierarchicalIds(checkpoint.resourceSet).resolveAndApply(unresolvedStateBasedChange) + val message = getTextualRepresentation(stateBasedChange, deltaBasedChange) + val stateBasedChangedObjects = stateBasedChange.affectedAndReferencedEObjects + val deltaBasedChangedObjects = deltaBasedChange.affectedAndReferencedEObjects + assertEquals(stateBasedChangedObjects.size, deltaBasedChangedObjects.size, ''' + Got a different number of changed objects: + «message»''') + stateBasedChangedObjects.forEach [ stateBasedChangedObject | + assertTrue(deltaBasedChangedObjects.exists [EcoreUtil.equals(it, stateBasedChangedObject)], ''' + Could not find this changed object in the delta based change: + «stateBasedChangedObject» + + «message»''') + ] + } + + /** + * Returns the recorded change sequences (the "original" changes) for a specific model instance. + */ + private def VitruviusChange endRecording(Notifier notifier) { + changeRecorder.removeFromRecording(notifier) + return changeRecorder.endRecording + } + + private def String getTextualRepresentation(VitruviusChange stateBasedChange, VitruviusChange deltaBasedChange) ''' + State-based «stateBasedChange» + Delta-based «deltaBasedChange» + ''' + + private def createPcmMockupModel() { + pcmModel = resourceSet.createResource(getModelURI("My.pcm_mockup")) => [ + contents += (pcmRoot = pcm.Repository => [ + name = "RootRepository" + interfaces += pcm.Interface + components += pcm.Component + ]) + ] + pcmModel.save(null) + } + + private def createUmlMockupModel() { + umlModel = resourceSet.createResource(getModelURI("My.uml_mockup")) => [ + contents += (umlRoot = uml.Package => [ + name = "RootPackage" + interfaces += uml.Interface + classes += uml.Class + ]) + ] + umlModel.save(null) + } + + private def startRecording(Notifier notifier) { + changeRecorder.addToRecording(notifier) + if (!changeRecorder.isRecording) { + changeRecorder.beginRecording + } + } + + protected def record(T notifier, (T) => void function) { + notifier.startRecording + function.apply(notifier) + return notifier.endRecording + } + + private def Resource createCheckpoint(Resource original) { + return checkpointResourceSet.getResource(original.URI, true) + } + + protected def URI getModelURI(String modelFileName) { + return testProjectFolder.resolve("model").resolve(modelFileName).toFile().createFileURI() + } +} diff --git a/views/src/test/xtend/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend new file mode 100644 index 0000000000..c21e11d991 --- /dev/null +++ b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend @@ -0,0 +1,77 @@ +package tools.vitruv.framework.views.changederivation + +import org.eclipse.emf.compare.utils.UseIdentifiers +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource +import org.junit.jupiter.params.provider.MethodSource + +import static tools.vitruv.testutils.metamodels.UmlMockupCreators.uml + +class UmlStateChangeTest extends StateChangePropagationTest { + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testRenameTypes(StateBasedChangeResolutionStrategy strategyToTest) { + umlRoot.classes.get(0) => [name = "RenamedClass"] + umlRoot.interfaces.get(0) => [name = "RenamedInterface"] + compareChanges(umlModel, umlCheckpoint, strategyToTest) + } + + @ParameterizedTest() + @EnumSource( + value = UseIdentifiers, + //TODO: should be re-enabled when state comparison is used instead of change comparison + names = #["ONLY"], + mode = EnumSource.Mode.EXCLUDE + ) + def void testNewAttributes(UseIdentifiers useIdentifiers) { + val strategyToTest = new DefaultStateBasedChangeResolutionStrategy(useIdentifiers) + umlRoot.classes.get(0) => [ + attributes += uml.Attribute => [attributeName = "NewlyAddedAttribute"] + ] + compareChanges(umlModel, umlCheckpoint, strategyToTest) + } + + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testNewMethod(StateBasedChangeResolutionStrategy strategyToTest) { + umlRoot.interfaces.get(0) => [ + methods += uml.Method => [name = "NewlyAddedMethod"] + ] + compareChanges(umlModel, umlCheckpoint, strategyToTest) + } + + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testNewClass(StateBasedChangeResolutionStrategy strategyToTest) { + umlRoot.classes += uml.Class => [name = "NewlyAddedClass"] + compareChanges(umlModel, umlCheckpoint, strategyToTest) + } + + @ParameterizedTest() + @EnumSource( + value = UseIdentifiers, + //TODO: should be re-enabled when state comparison is used instead of change comparison + names = #["NEVER"], + mode = EnumSource.Mode.EXCLUDE + ) + def void testReplaceClass(UseIdentifiers useIdentifiers) { + val strategyToTest = new DefaultStateBasedChangeResolutionStrategy(useIdentifiers) + umlRoot.classes.remove(0) + umlRoot.classes += uml.Class => [name = "NewlyAddedClass"] + compareChanges(umlModel, umlCheckpoint, strategyToTest) + } + + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testDeleteClass(StateBasedChangeResolutionStrategy strategyToTest) { + umlRoot.classes.remove(0) + compareChanges(umlModel, umlCheckpoint, strategyToTest) + } + + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testNewInterface(StateBasedChangeResolutionStrategy strategyToTest) { + umlRoot.interfaces += uml.Interface => [name = "NewlyAddedInterface"] + compareChanges(umlModel, umlCheckpoint, strategyToTest) + } +} From 00e58612d87a0c623e4500cb9a90105f84deabfe Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Mon, 12 Aug 2024 10:21:35 +0200 Subject: [PATCH 02/11] Add missing projects --- {testutils/vsum => applications}/pom.xml | 40 +- .../applications/VitruvApplication.xtend | 9 + .../VitruvApplicationsRegistry.xtend | 64 +++ pom.xml | 24 +- testutils/deprecated/pom.xml | 91 ++++ .../vsum/LegacyCorrespondenceRetriever.xtend | 2 +- .../vsum/LegacyVitruvApplicationTest.xtend | 8 +- testutils/integration/pom.xml | 108 ++++ .../integration}/TestViewFactory.java | 2 +- .../DefaultVirtualModelBasedTestView.xtend | 9 +- .../vsum/ViewBasedVitruvApplicationTest.xtend | 8 +- .../vsum/VirtualModelBasedTestView.xtend | 4 +- .../vsum/VitruvApplicationTest.xtend | 10 +- testutils/pom.xml | 6 +- views/pom.xml | 46 +- .../framework/views/ChangeableViewSource.java | 11 + .../framework/views/CommittableView.java | 41 ++ .../views/ModifiableViewSelection.java | 49 ++ .../tools/vitruv/framework/views/View.java | 138 ++++++ .../vitruv/framework/views/ViewProvider.java | 18 + .../vitruv/framework/views/ViewSelection.java | 15 + .../vitruv/framework/views/ViewSelector.java | 34 ++ .../vitruv/framework/views/ViewSource.java | 28 ++ .../vitruv/framework/views/ViewType.java | 24 + .../framework/views/ViewTypeProvider.java | 15 + .../views/ViewTypeRepositoryTest.java | 107 ---- .../StateBasedChangeResolutionStrategy.java | 54 ++ .../framework/views/impl/BasicViewTest.java | 262 ---------- .../views/impl/ChangeDerivingViewTest.java | 410 ---------------- .../views/impl/ChangeRecordingViewTest.java | 442 ----------------- .../views/impl/IdentityMappingViewType.java | 89 ++++ .../impl/IdentityMappingViewTypeTest.java | 464 ------------------ .../views/impl/ViewCreatingViewType.java | 45 ++ .../selection/AbstractViewSelection.java | 55 +++ .../views/selection/ElementViewSelection.java | 24 + .../selection/ElementViewSelectionTest.java | 279 ----------- .../DirectViewElementSelectorTest.java | 165 ------- .../views/util/XmiIdEdgeCaseTest.java | 111 ----- .../framework/views/ViewTypeFactory.xtend | 15 + .../framework/views/ViewTypeRepository.xtend | 36 ++ .../BasicStateChangePropagationTest.xtend | 355 -------------- ...ltStateBasedChangeResolutionStrategy.xtend | 118 +++++ .../EdgeCaseStateChangeTest.xtend | 41 -- .../changederivation/PcmStateChangeTest.xtend | 74 --- .../StateChangePropagationTest.xtend | 175 ------- .../changederivation/UmlStateChangeTest.xtend | 77 --- .../views/impl/AbstractViewType.xtend | 16 + .../framework/views/impl/BasicView.xtend | 151 ++++++ .../views/impl/ChangeDerivingView.xtend | 105 ++++ .../views/impl/ChangeRecordingView.xtend | 76 +++ .../framework/views/impl/ModifiableView.xtend | 14 + .../selectors/DirectViewElementSelector.xtend | 74 +++ .../views/ViewTypeRepositoryTest.java | 4 +- .../framework/views/impl/BasicViewTest.java | 6 +- .../views/impl/ChangeDerivingViewTest.java | 10 +- .../views/impl/ChangeRecordingViewTest.java | 10 +- .../impl/IdentityMappingViewTypeTest.java | 8 +- .../selection/ElementViewSelectionTest.java | 6 +- .../DirectViewElementSelectorTest.java | 6 +- .../views/util/XmiIdEdgeCaseTest.java | 10 +- .../BasicStateChangePropagationTest.xtend | 8 +- .../changederivation/PcmStateChangeTest.xtend | 2 +- .../StateChangePropagationTest.xtend | 12 +- .../changederivation/UmlStateChangeTest.xtend | 2 +- vsum/pom.xml | 137 ++++++ .../vsum/internal/ResourceRepositoryImpl.java | 215 ++++++++ .../vsum/internal/VirtualModelImpl.java | 183 +++++++ .../vsum/internal/VirtualModelRegistry.java | 30 ++ .../vitruv/framework/vsum/VirtualModel.xtend | 18 + .../framework/vsum/VirtualModelBuilder.xtend | 107 ++++ .../framework/vsum/VirtualModelManager.xtend | 32 ++ .../vsum/helper/VsumFileSystemLayout.xtend | 103 ++++ .../vsum/internal/InternalVirtualModel.xtend | 15 + .../vsum/internal/ModelInstance.xtend | 65 +++ .../vsum/internal/ModelRepository.xtend | 20 + .../framework/vsum/VirtualModelTest.xtend | 396 +++++++++++++++ .../framework/vsum/VirtualModelTestUtil.xtend | 128 +++++ 77 files changed, 3104 insertions(+), 3067 deletions(-) rename {testutils/vsum => applications}/pom.xml (50%) create mode 100644 applications/src/main/xtend/tools/vitruv/framework/applications/VitruvApplication.xtend create mode 100644 applications/src/main/xtend/tools/vitruv/framework/applications/VitruvApplicationsRegistry.xtend create mode 100644 testutils/deprecated/pom.xml rename testutils/{vsum => deprecated}/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyCorrespondenceRetriever.xtend (94%) rename testutils/{vsum => deprecated}/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyVitruvApplicationTest.xtend (87%) create mode 100644 testutils/integration/pom.xml rename testutils/{vsum/src/main/java/tools/vitruv/framework/testutils/vsum => integration/src/main/java/tools/vitruv/framework/testutils/integration}/TestViewFactory.java (98%) rename testutils/{vsum => integration}/src/main/xtend/tools/vitruv/framework/testutils/vsum/DefaultVirtualModelBasedTestView.xtend (86%) rename testutils/{vsum => integration}/src/main/xtend/tools/vitruv/framework/testutils/vsum/ViewBasedVitruvApplicationTest.xtend (91%) rename testutils/{vsum => integration}/src/main/xtend/tools/vitruv/framework/testutils/vsum/VirtualModelBasedTestView.xtend (71%) rename testutils/{vsum => integration}/src/main/xtend/tools/vitruv/framework/testutils/vsum/VitruvApplicationTest.xtend (77%) create mode 100644 views/src/main/java/tools/vitruv/framework/views/ChangeableViewSource.java create mode 100644 views/src/main/java/tools/vitruv/framework/views/CommittableView.java create mode 100644 views/src/main/java/tools/vitruv/framework/views/ModifiableViewSelection.java create mode 100644 views/src/main/java/tools/vitruv/framework/views/View.java create mode 100644 views/src/main/java/tools/vitruv/framework/views/ViewProvider.java create mode 100644 views/src/main/java/tools/vitruv/framework/views/ViewSelection.java create mode 100644 views/src/main/java/tools/vitruv/framework/views/ViewSelector.java create mode 100644 views/src/main/java/tools/vitruv/framework/views/ViewSource.java create mode 100644 views/src/main/java/tools/vitruv/framework/views/ViewType.java create mode 100644 views/src/main/java/tools/vitruv/framework/views/ViewTypeProvider.java delete mode 100644 views/src/main/java/tools/vitruv/framework/views/ViewTypeRepositoryTest.java create mode 100644 views/src/main/java/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.java delete mode 100644 views/src/main/java/tools/vitruv/framework/views/impl/BasicViewTest.java delete mode 100644 views/src/main/java/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java delete mode 100644 views/src/main/java/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java create mode 100644 views/src/main/java/tools/vitruv/framework/views/impl/IdentityMappingViewType.java delete mode 100644 views/src/main/java/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java create mode 100644 views/src/main/java/tools/vitruv/framework/views/impl/ViewCreatingViewType.java create mode 100644 views/src/main/java/tools/vitruv/framework/views/selection/AbstractViewSelection.java create mode 100644 views/src/main/java/tools/vitruv/framework/views/selection/ElementViewSelection.java delete mode 100644 views/src/main/java/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java delete mode 100644 views/src/main/java/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java delete mode 100644 views/src/main/java/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java create mode 100644 views/src/main/xtend/tools/vitruv/framework/views/ViewTypeFactory.xtend create mode 100644 views/src/main/xtend/tools/vitruv/framework/views/ViewTypeRepository.xtend delete mode 100644 views/src/main/xtend/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend create mode 100644 views/src/main/xtend/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend delete mode 100644 views/src/main/xtend/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend delete mode 100644 views/src/main/xtend/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend delete mode 100644 views/src/main/xtend/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend delete mode 100644 views/src/main/xtend/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend create mode 100644 views/src/main/xtend/tools/vitruv/framework/views/impl/AbstractViewType.xtend create mode 100644 views/src/main/xtend/tools/vitruv/framework/views/impl/BasicView.xtend create mode 100644 views/src/main/xtend/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend create mode 100644 views/src/main/xtend/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend create mode 100644 views/src/main/xtend/tools/vitruv/framework/views/impl/ModifiableView.xtend create mode 100644 views/src/main/xtend/tools/vitruv/framework/views/selectors/DirectViewElementSelector.xtend create mode 100644 vsum/pom.xml create mode 100644 vsum/src/main/java/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java create mode 100644 vsum/src/main/java/tools/vitruv/framework/vsum/internal/VirtualModelImpl.java create mode 100644 vsum/src/main/java/tools/vitruv/framework/vsum/internal/VirtualModelRegistry.java create mode 100644 vsum/src/main/xtend/tools/vitruv/framework/vsum/VirtualModel.xtend create mode 100644 vsum/src/main/xtend/tools/vitruv/framework/vsum/VirtualModelBuilder.xtend create mode 100644 vsum/src/main/xtend/tools/vitruv/framework/vsum/VirtualModelManager.xtend create mode 100644 vsum/src/main/xtend/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend create mode 100644 vsum/src/main/xtend/tools/vitruv/framework/vsum/internal/InternalVirtualModel.xtend create mode 100644 vsum/src/main/xtend/tools/vitruv/framework/vsum/internal/ModelInstance.xtend create mode 100644 vsum/src/main/xtend/tools/vitruv/framework/vsum/internal/ModelRepository.xtend create mode 100644 vsum/src/test/xtend/tools/vitruv/framework/vsum/VirtualModelTest.xtend create mode 100644 vsum/src/test/xtend/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend diff --git a/testutils/vsum/pom.xml b/applications/pom.xml similarity index 50% rename from testutils/vsum/pom.xml rename to applications/pom.xml index 170d96ab60..ad8aff8b20 100644 --- a/testutils/vsum/pom.xml +++ b/applications/pom.xml @@ -1,19 +1,18 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 tools.vitruv - tools.vitruv.framework.testutils + tools.vitruv.framework 3.0.1-SNAPSHOT - tools.vitruv.framework.testutils.vsum + tools.vitruv.framework.applications - Vitruv Framework Test Utilities + Vitruv Framework Applications Specification @@ -33,34 +32,43 @@ ${project.groupId} - tools.vitruv.change.utils + tools.vitruv.change.propagation ${project.version} + com.google.guava guava - org.eclipse.emf - org.eclipse.emf.common + org.eclipse.xtend + org.eclipse.xtend.lib + compile - org.eclipse.emf - org.eclipse.emf.ecore + org.eclipse.xtext + org.eclipse.xtext.xbase.lib + compile - org.junit.jupiter - junit-jupiter-api + log4j + log4j + compile - org.junit.jupiter - junit-jupiter-params + org.eclipse.platform + org.eclipse.equinox.registry + compile - xannotations - edu.kit.ipd.sdq.activextendannotations + org.eclipse.platform + org.eclipse.core.runtime + compile + + + \ No newline at end of file diff --git a/applications/src/main/xtend/tools/vitruv/framework/applications/VitruvApplication.xtend b/applications/src/main/xtend/tools/vitruv/framework/applications/VitruvApplication.xtend new file mode 100644 index 0000000000..944d2c9d5b --- /dev/null +++ b/applications/src/main/xtend/tools/vitruv/framework/applications/VitruvApplication.xtend @@ -0,0 +1,9 @@ +package tools.vitruv.framework.applications + +import tools.vitruv.change.propagation.ChangePropagationSpecification +import java.util.Set + +interface VitruvApplication { + def Set getChangePropagationSpecifications(); + def String getName(); +} diff --git a/applications/src/main/xtend/tools/vitruv/framework/applications/VitruvApplicationsRegistry.xtend b/applications/src/main/xtend/tools/vitruv/framework/applications/VitruvApplicationsRegistry.xtend new file mode 100644 index 0000000000..05bfb82e1a --- /dev/null +++ b/applications/src/main/xtend/tools/vitruv/framework/applications/VitruvApplicationsRegistry.xtend @@ -0,0 +1,64 @@ +package tools.vitruv.framework.applications + +import org.eclipse.xtend.lib.annotations.Accessors +import java.util.Collection +import java.util.HashSet +import java.util.Collections +import java.util.List +import org.eclipse.core.runtime.Platform +import org.apache.log4j.Logger + +class VitruvApplicationsRegistry { + public static String EXTENSION_POINT_ID = "tools.vitruv.framework.applications.application" + static Logger LOGGER = Logger.getLogger(VitruvApplicationsRegistry) + + @Accessors(PUBLIC_GETTER) + static VitruvApplicationsRegistry instance = new VitruvApplicationsRegistry + + Collection applications; + boolean initialized; + + private new() { + applications = new HashSet + initialized = false; + } + + /** + * Adds the given application to the registry + * + * @param application - the {@link VitruvApplication} to add, must not be null + */ + def addApplication(VitruvApplication application) { + if (application === null) { + throw new IllegalArgumentException("Application must not be null") + } + applications += application; + } + + def getApplications() { + if (!initialized) { + applications += allApplicationsFromExtensionPoint + initialized = true + } + Collections.unmodifiableCollection(applications); + } + + /** + * Retrieves all applications from the extensions registered to the VitruvApplication + * extension point. + */ + private def static Iterable getAllApplicationsFromExtensionPoint() { + val List applications = newArrayList(); + if (Platform.running) { + Platform.getExtensionRegistry().getConfigurationElementsFor(EXTENSION_POINT_ID).map [ + try { + it.createExecutableExtension("class") + } catch (Exception e) { + LOGGER.warn("Error when loading application for extension " + it) + null; + } + ].filter(VitruvApplication).forEach[applications.add(it)]; + } + return applications; + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index e42fda98e9..d3498b3ff1 100644 --- a/pom.xml +++ b/pom.xml @@ -27,8 +27,10 @@ - testutils views + vsum + testutils + applications @@ -109,6 +111,21 @@ tools.vitruv.change.changederivation 3.0.1-SNAPSHOT + + tools.vitruv + tools.vitruv.change.testutils.core + 3.0.1-SNAPSHOT + + + tools.vitruv + tools.vitruv.change.testutils.integration + 3.0.1-SNAPSHOT + + + tools.vitruv + tools.vitruv.change.testutils + 3.0.1-SNAPSHOT + ch.qos.logback @@ -175,6 +192,11 @@ org.eclipse.equinox.common 3.18.100 + + org.eclipse.platform + org.eclipse.equinox.registry + 3.12.100 + org.eclipse.platform org.eclipse.jface diff --git a/testutils/deprecated/pom.xml b/testutils/deprecated/pom.xml new file mode 100644 index 0000000000..4ee9f58aef --- /dev/null +++ b/testutils/deprecated/pom.xml @@ -0,0 +1,91 @@ + + + + 4.0.0 + + + tools.vitruv + tools.vitruv.framework.testutils + 3.0.1-SNAPSHOT + + + tools.vitruv.framework.testutils.deprecated + + DEPRECATED Vitruv Framework Test Utilities + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + org.eclipse.xtend + xtend-maven-plugin + + + + + + + + ${project.groupId} + tools.vitruv.change.propagation + ${project.version} + + + ${project.groupId} + tools.vitruv.change.composite + ${project.version} + + + ${project.groupId} + tools.vitruv.change.correspondence + ${project.version} + + + ${project.groupId} + tools.vitruv.change.atomic + ${project.version} + + + ${project.groupId} + tools.vitruv.change.testutils.integration + ${project.version} + + + ${project.groupId} + tools.vitruv.framework.vsum + ${project.version} + + + ${project.groupId} + tools.vitruv.framework.testutils.integration + ${project.version} + + + + + com.google.guava + guava + + + org.eclipse.emf + org.eclipse.emf.common + + + org.eclipse.emf + org.eclipse.emf.ecore + + + xannotations + edu.kit.ipd.sdq.activextendannotations + + + org.eclipse.xtext + org.eclipse.xtext.xbase.lib + + + \ No newline at end of file diff --git a/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyCorrespondenceRetriever.xtend b/testutils/deprecated/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyCorrespondenceRetriever.xtend similarity index 94% rename from testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyCorrespondenceRetriever.xtend rename to testutils/deprecated/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyCorrespondenceRetriever.xtend index 2723b358e0..aacf4e2eac 100644 --- a/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyCorrespondenceRetriever.xtend +++ b/testutils/deprecated/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyCorrespondenceRetriever.xtend @@ -1,4 +1,4 @@ -package tools.vitruv.testutils.framework.vsum +package tools.vitruv.framework.testutils.deprecated import org.eclipse.emf.ecore.EObject diff --git a/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyVitruvApplicationTest.xtend b/testutils/deprecated/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyVitruvApplicationTest.xtend similarity index 87% rename from testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyVitruvApplicationTest.xtend rename to testutils/deprecated/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyVitruvApplicationTest.xtend index 637f4132fe..6e146187fe 100644 --- a/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyVitruvApplicationTest.xtend +++ b/testutils/deprecated/src/main/xtend/tools/vitruv/framework/testutils/vsum/LegacyVitruvApplicationTest.xtend @@ -1,12 +1,14 @@ -package tools.vitruv.testutils.framework.vsum +package tools.vitruv.framework.testutils.deprecated import edu.kit.ipd.sdq.activextendannotations.DelegateExcept import java.nio.file.Path import org.eclipse.emf.ecore.EClass import org.eclipse.emf.ecore.EObject import tools.vitruv.framework.vsum.internal.InternalVirtualModel -import tools.vitruv.testutils.views.NonTransactionalTestView -import tools.vitruv.testutils.views.TestView +import tools.vitruv.change.testutils.views.NonTransactionalTestView +import tools.vitruv.change.testutils.views.TestView +import tools.vitruv.framework.testutils.integration.VitruvApplicationTest +import tools.vitruv.framework.testutils.integration.DefaultVirtualModelBasedTestView import static com.google.common.base.Preconditions.checkArgument diff --git a/testutils/integration/pom.xml b/testutils/integration/pom.xml new file mode 100644 index 0000000000..e6c1dd61ce --- /dev/null +++ b/testutils/integration/pom.xml @@ -0,0 +1,108 @@ + + + + 4.0.0 + + + tools.vitruv + tools.vitruv.framework.testutils + 3.0.1-SNAPSHOT + + + tools.vitruv.framework.testutils.integration + + Vitruv Framework Integration Test Utilities for Views and V-SUMs + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + org.eclipse.xtend + xtend-maven-plugin + + + + + + + + ${project.groupId} + tools.vitruv.change.testutils.core + ${project.version} + + + ${project.groupId} + tools.vitruv.change.testutils.integration + ${project.version} + + + ${project.groupId} + tools.vitruv.change.interaction + ${project.version} + + + ${project.groupId} + tools.vitruv.change.atomic + ${project.version} + + + ${project.groupId} + tools.vitruv.change.propagation + ${project.version} + + + ${project.groupId} + tools.vitruv.change.composite + ${project.version} + + + ${project.groupId} + tools.vitruv.framework.vsum + ${project.version} + + + ${project.groupId} + tools.vitruv.framework.views + ${project.version} + + + + + com.google.guava + guava + + + org.eclipse.emf + org.eclipse.emf.common + + + org.eclipse.emf + org.eclipse.emf.ecore + + + org.junit.jupiter + junit-jupiter-api + + + org.eclipse.xtend + org.eclipse.xtend.lib + + + org.hamcrest + hamcrest + + + sdq-commons + edu.kit.ipd.sdq.commons.util.emf + + + org.eclipse.xtext + org.eclipse.xtext.xbase.lib + + + \ No newline at end of file diff --git a/testutils/vsum/src/main/java/tools/vitruv/framework/testutils/vsum/TestViewFactory.java b/testutils/integration/src/main/java/tools/vitruv/framework/testutils/integration/TestViewFactory.java similarity index 98% rename from testutils/vsum/src/main/java/tools/vitruv/framework/testutils/vsum/TestViewFactory.java rename to testutils/integration/src/main/java/tools/vitruv/framework/testutils/integration/TestViewFactory.java index 09ce25de40..4d28131f4e 100644 --- a/testutils/vsum/src/main/java/tools/vitruv/framework/testutils/vsum/TestViewFactory.java +++ b/testutils/integration/src/main/java/tools/vitruv/framework/testutils/integration/TestViewFactory.java @@ -1,4 +1,4 @@ -package tools.vitruv.framework.testutils.vsum; +package tools.vitruv.framework.testutils.view; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.not; diff --git a/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/DefaultVirtualModelBasedTestView.xtend b/testutils/integration/src/main/xtend/tools/vitruv/framework/testutils/vsum/DefaultVirtualModelBasedTestView.xtend similarity index 86% rename from testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/DefaultVirtualModelBasedTestView.xtend rename to testutils/integration/src/main/xtend/tools/vitruv/framework/testutils/vsum/DefaultVirtualModelBasedTestView.xtend index 62c75d5ba9..7d30967b7c 100644 --- a/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/DefaultVirtualModelBasedTestView.xtend +++ b/testutils/integration/src/main/xtend/tools/vitruv/framework/testutils/vsum/DefaultVirtualModelBasedTestView.xtend @@ -1,13 +1,14 @@ -package tools.vitruv.testutils.framework.vsum +package tools.vitruv.framework.testutils.integration import java.nio.file.Path import org.eclipse.xtend.lib.annotations.Delegate import tools.vitruv.change.propagation.ChangePropagationSpecification import tools.vitruv.framework.vsum.VirtualModelBuilder import tools.vitruv.framework.vsum.internal.InternalVirtualModel -import tools.vitruv.testutils.views.ChangePublishingTestView -import tools.vitruv.testutils.views.NonTransactionalTestView -import tools.vitruv.testutils.views.UriMode +import tools.vitruv.change.testutils.views.ChangePublishingTestView +import tools.vitruv.change.testutils.views.NonTransactionalTestView +import tools.vitruv.change.testutils.views.UriMode +import tools.vitruv.change.testutils.TestUserInteraction class DefaultVirtualModelBasedTestView implements VirtualModelBasedTestView, NonTransactionalTestView { InternalVirtualModel virtualModel diff --git a/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/ViewBasedVitruvApplicationTest.xtend b/testutils/integration/src/main/xtend/tools/vitruv/framework/testutils/vsum/ViewBasedVitruvApplicationTest.xtend similarity index 91% rename from testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/ViewBasedVitruvApplicationTest.xtend rename to testutils/integration/src/main/xtend/tools/vitruv/framework/testutils/vsum/ViewBasedVitruvApplicationTest.xtend index 7021e5748d..2bc34d2569 100644 --- a/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/ViewBasedVitruvApplicationTest.xtend +++ b/testutils/integration/src/main/xtend/tools/vitruv/framework/testutils/vsum/ViewBasedVitruvApplicationTest.xtend @@ -1,4 +1,4 @@ -package tools.vitruv.testutils.framework.vsum +package tools.vitruv.framework.testutils.integration import org.junit.jupiter.api.^extension.ExtendWith import tools.vitruv.framework.vsum.VirtualModel @@ -14,8 +14,12 @@ import static com.google.common.base.Preconditions.checkArgument import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.common.util.URIUtil.createFileURI import static org.eclipse.emf.common.util.URI.createPlatformResourceURI import org.eclipse.xtend.lib.annotations.Accessors -import tools.vitruv.testutils.views.UriMode import tools.vitruv.change.propagation.ChangePropagationMode +import tools.vitruv.change.testutils.views.UriMode +import tools.vitruv.change.testutils.TestUserInteraction +import tools.vitruv.change.testutils.TestLogging +import tools.vitruv.change.testutils.TestProjectManager +import tools.vitruv.change.testutils.TestProject @ExtendWith(TestLogging, TestProjectManager) abstract class ViewBasedVitruvApplicationTest { diff --git a/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/VirtualModelBasedTestView.xtend b/testutils/integration/src/main/xtend/tools/vitruv/framework/testutils/vsum/VirtualModelBasedTestView.xtend similarity index 71% rename from testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/VirtualModelBasedTestView.xtend rename to testutils/integration/src/main/xtend/tools/vitruv/framework/testutils/vsum/VirtualModelBasedTestView.xtend index a75b12fce4..39dd5398f5 100644 --- a/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/VirtualModelBasedTestView.xtend +++ b/testutils/integration/src/main/xtend/tools/vitruv/framework/testutils/vsum/VirtualModelBasedTestView.xtend @@ -1,7 +1,7 @@ -package tools.vitruv.testutils.framework.vsum +package tools.vitruv.framework.testutils.integration import tools.vitruv.framework.vsum.VirtualModel -import tools.vitruv.testutils.views.TestView +import tools.vitruv.change.testutils.views.TestView /** * A {@link TestView} that uses a {@link VirtualModel} for model management diff --git a/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/VitruvApplicationTest.xtend b/testutils/integration/src/main/xtend/tools/vitruv/framework/testutils/vsum/VitruvApplicationTest.xtend similarity index 77% rename from testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/VitruvApplicationTest.xtend rename to testutils/integration/src/main/xtend/tools/vitruv/framework/testutils/vsum/VitruvApplicationTest.xtend index 01da26f2ee..964070ea97 100644 --- a/testutils/vsum/src/main/xtend/tools/vitruv/framework/testutils/vsum/VitruvApplicationTest.xtend +++ b/testutils/integration/src/main/xtend/tools/vitruv/framework/testutils/vsum/VitruvApplicationTest.xtend @@ -1,4 +1,4 @@ -package tools.vitruv.testutils.framework.vsum +package tools.vitruv.framework.testutils.integration import java.nio.file.Path import org.eclipse.xtend.lib.annotations.Delegate @@ -6,8 +6,10 @@ import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.^extension.ExtendWith import tools.vitruv.change.propagation.ChangePropagationSpecification - -import tools.vitruv.testutils.views.UriMode +import tools.vitruv.change.testutils.views.UriMode +import tools.vitruv.change.testutils.TestLogging +import tools.vitruv.change.testutils.TestProjectManager +import tools.vitruv.change.testutils.TestProject @ExtendWith(TestLogging, TestProjectManager) abstract class VitruvApplicationTest implements VirtualModelBasedTestView { @@ -29,7 +31,7 @@ abstract class VitruvApplicationTest implements VirtualModelBasedTestView { testView = generateTestView(testProjectPath, vsumPath) } - def package VirtualModelBasedTestView generateTestView(Path testProjectPath, Path vsumPath) { + def VirtualModelBasedTestView generateTestView(Path testProjectPath, Path vsumPath) { new DefaultVirtualModelBasedTestView(testProjectPath, vsumPath, changePropagationSpecifications, uriMode) } diff --git a/testutils/pom.xml b/testutils/pom.xml index 440fee65c4..7488f3660f 100644 --- a/testutils/pom.xml +++ b/testutils/pom.xml @@ -1,8 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 @@ -18,6 +17,7 @@ Utilities for defining tests using the Vitruv Framework - vsum + integration + deprecated \ No newline at end of file diff --git a/views/pom.xml b/views/pom.xml index e26f6e229f..7d4a9f2efb 100644 --- a/views/pom.xml +++ b/views/pom.xml @@ -37,7 +37,7 @@ ${project.groupId} - tools.vitruv.change.changederivation + tools.vitruv.change.atomic ${project.version} @@ -55,53 +55,41 @@ test - - - - log4j - log4j - - - com.google.guava - guava + sdq-commons + edu.kit.ipd.sdq.commons.util.emf - org.eclipse.emf - org.eclipse.emf.common + xannotations + edu.kit.ipd.sdq.activextendannotations - org.eclipse.emf - org.eclipse.emf.ecore + emf-compare + org.eclipse.emf.compare org.eclipse.emf - org.eclipse.emf.edit + org.eclipse.emf.ecore org.eclipse.xtext - org.eclipse.xtext.xbase.lib + org.eclipse.xtext.xbase.lib - sdq-commons - edu.kit.ipd.sdq.commons.util.emf + com.google.guava + guava - xannotations - edu.kit.ipd.sdq.activextendannotations + org.eclipse.emf + org.eclipse.emf.common org.eclipse.xtend org.eclipse.xtend.lib - test + compile org.hamcrest @@ -128,5 +116,11 @@ mockito-core test + + org.eclipse.emf + org.eclipse.emf.ecore.xmi + test + + \ No newline at end of file diff --git a/views/src/main/java/tools/vitruv/framework/views/ChangeableViewSource.java b/views/src/main/java/tools/vitruv/framework/views/ChangeableViewSource.java new file mode 100644 index 0000000000..50b775d192 --- /dev/null +++ b/views/src/main/java/tools/vitruv/framework/views/ChangeableViewSource.java @@ -0,0 +1,11 @@ +package tools.vitruv.framework.views; + +import tools.vitruv.change.composite.propagation.ChangeableModelRepository; + +/** + * A specific {@link ViewSource} that can be changed, such that views are able + * to perform modifications to the underlying models. + */ +public interface ChangeableViewSource extends ViewSource, ChangeableModelRepository { + +} diff --git a/views/src/main/java/tools/vitruv/framework/views/CommittableView.java b/views/src/main/java/tools/vitruv/framework/views/CommittableView.java new file mode 100644 index 0000000000..e77aa4c771 --- /dev/null +++ b/views/src/main/java/tools/vitruv/framework/views/CommittableView.java @@ -0,0 +1,41 @@ +package tools.vitruv.framework.views; + +/** + * A {@link View} that allows to commit its changes back to the underlying {@link ChangeableViewSource}. + */ +public interface CommittableView extends View { + /** + * Commits the changes made to the view and its containing elements to the + * underlying {@link ChangeableViewSource}. This explicitly includes all changes + * that have been made before calling this method. Whether changes will + * effectively be recorded depends on this view. It is permissible for a view + * not to record any changes if it deems them irrelevant. + * + * To ensure that the view is not modified ({@link #isModified()}) afterwards + * because further changes may be performed to the underlying sources during + * commit, consider using {@link #commitChangesAndUpdate()} instead to perform + * an update of the view afterwards. + * + * @throws IllegalStateException if called on a closed view + * @see #isClosed() + * @see #commitChangesAndUpdate() + */ + void commitChanges(); + + /** + * Convenience method for subsequent execution of {@link #commitChanges()} and + * {@link #update()}. Commits the changes made to the view and its containing + * elements to the underlying {@link ChangeableViewSource} and updates the view + * elements from the {@link ChangeableViewSource} afterwards to reflect + * potential further changes made during commit. + * + * @throws IllegalStateException if called on a closed view + * @see #commitChanges() + * @see #update() + * @see #isClosed() + */ + default void commitChangesAndUpdate() { + commitChanges(); + update(); + } +} diff --git a/views/src/main/java/tools/vitruv/framework/views/ModifiableViewSelection.java b/views/src/main/java/tools/vitruv/framework/views/ModifiableViewSelection.java new file mode 100644 index 0000000000..7d343be0ab --- /dev/null +++ b/views/src/main/java/tools/vitruv/framework/views/ModifiableViewSelection.java @@ -0,0 +1,49 @@ +package tools.vitruv.framework.views; + +import java.util.Collection; + +import org.eclipse.emf.ecore.EObject; + +/** + * A {@link ViewSelection} that can be changed by performing selections of the + * elements. + */ +public interface ModifiableViewSelection extends ViewSelection { + /** + * Returns the elements that can be selected, i.e., those that may be passed to + * {@link #isSelected(EObject)} and {@link #setSelected(EObject, boolean)}. + * + * @return the selectable {@link EObject}s + */ + Collection getSelectableElements(); + + /** + * Returns whether the given {@link EObject} is selected. Also returns + * false when the given object is null or not + * selectable at all. + * + * @param eObject the {@link EObject} to check the selection state for + * @return whether the given {@link EObject} is selected + */ + boolean isSelected(EObject eObject); + + /** + * Returns whether the given {@link EObject} can be selected. May only be called + * for objects that are selectable (i.e., contained in + * {@link #getSelectableElements()} and throws an exception otherwise. + * + * @param eObject the {@link EObject} to check the selection state for + * @return whether the given {@link EObject} is selected + */ + boolean isSelectable(EObject eObject); + + /** + * Sets the selection state of the given {@link EObject}. May only be called for + * objects that are selectable (i.e., contained in + * {@link #getSelectableElements()} and throws an exception otherwise. + * + * @param eObject the {@link EObject} to set the selection state for + * @param selected whether the given {@link EObject} should be selected or not + */ + void setSelected(EObject eObject, boolean selected); +} diff --git a/views/src/main/java/tools/vitruv/framework/views/View.java b/views/src/main/java/tools/vitruv/framework/views/View.java new file mode 100644 index 0000000000..b9807cf392 --- /dev/null +++ b/views/src/main/java/tools/vitruv/framework/views/View.java @@ -0,0 +1,138 @@ +package tools.vitruv.framework.views; + +import java.util.Collection; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; + +import com.google.common.annotations.Beta; +import com.google.common.collect.FluentIterable; + +import tools.vitruv.framework.views.changederivation.DefaultStateBasedChangeResolutionStrategy; +import tools.vitruv.framework.views.changederivation.StateBasedChangeResolutionStrategy; + +/** + * A Vitruv view on an underlying {@link ChangeableViewSource}. + */ +public interface View extends AutoCloseable { + + /** + * Provides the root model elements of this view. + * + * @throws IllegalStateException if called on a closed view. + * @see View#isClosed() + */ + Collection getRootObjects(); + + /** + * Provides all root model elements of this view that conform to a certain type. + * + * @param clazz is requested root element type. + * @throws IllegalStateException if called on a closed view. + * @see View#isClosed() + */ + default Collection getRootObjects(Class clazz) { + return FluentIterable.from(getRootObjects()).filter(clazz).toList(); + }; + + /** + * Returns whether the view was modified. + */ + boolean isModified(); + + /** + * Returns whether the view is outdated, i.e., whether the underlying view + * sources have changed. + */ + boolean isOutdated(); + + /** + * Updates the view from the underlying {@link ViewSource}, thus invalidating + * its previous state and now providing an updated view on the + * {@code ViewSource}. It reuses the {@link ViewSelection} with whom the view + * has been created. This can only be done for an unmodified view. + * + * @throws UnsupportedOperationException if called on a modified view + * @throws IllegalStateException if called on a closed view + * @see #isClosed() + * @see #isModified() + */ + void update(); + + /** + * Checks whether the view was closed. Closed views cannot be used further. All + * methods may thrown an {@link IllegalStateException}. + */ + boolean isClosed(); + + /** + * Persists the given object at the given {@link URI} and adds it as view root. + * The newly registered root will not be present in the view, you have to commit + * the view and checkout a new one (you probably also have to adapt the selection) + * in order to be able to see and modify the new root. + * + * This method is in beta state, as it is still under evaluation whether it is + * sufficient and appropriate for registering root objects in views. + */ + @Beta + void registerRoot(EObject object, URI persistAt); + + /** + * Moves the given object to the given {@link URI}. The given {@link EObject} + * must already be a root object of the view, otherwise an + * {@link IllegalStateException} is thrown. + * + * This method is in beta state, as it is still under evaluation whether it is + * sufficient and appropriate for registering root objects in views. + */ + @Beta + void moveRoot(EObject object, URI newLocation); + + /** + * Returns the {@link ViewSelection} with which this view has been created. + */ + ViewSelection getSelection(); + + /** + * Returns the {@link ViewType} this view conforms to. + */ + ViewType getViewType(); + + /** + * Returns a {@link CommittableView} based on the view's configuration. + * Changes to commit are identified by recording any changes made to the view. + * + * @throws UnsupportedOperationException if called on a modified view + * @throws IllegalStateException if called on a closed view + * @see #isClosed() + * @see #isModified() + */ + CommittableView withChangeRecordingTrait(); + + /** + * Returns a {@link CommittableView} based on the view's configuration. + * Changes to commit are identified by comparing the current view state with its state from the last update. + * + * @param changeResolutionStrategy The change resolution strategy to use for view state comparison. Must not be null. + * @throws UnsupportedOperationException if called on a modified view + * @throws IllegalStateException if called on a closed view + * @see #isClosed() + * @see #isModified() + */ + CommittableView withChangeDerivingTrait(StateBasedChangeResolutionStrategy changeResolutionStrategy); + + + /** + * Returns a {@link CommittableView} based on the view's configuration. + * Changes to commit are identified by comparing the current view state with its state from the last update. + * To compare states the {@link DefaultStateBasedChangeResolutionStrategy} is applied. + * + * @throws UnsupportedOperationException if called on a modified view + * @throws IllegalStateException if called on a closed view + * @see #isClosed() + * @see #isModified() + */ + default CommittableView withChangeDerivingTrait() { + return withChangeDerivingTrait(new DefaultStateBasedChangeResolutionStrategy()); + } +} diff --git a/views/src/main/java/tools/vitruv/framework/views/ViewProvider.java b/views/src/main/java/tools/vitruv/framework/views/ViewProvider.java new file mode 100644 index 0000000000..cc970895b3 --- /dev/null +++ b/views/src/main/java/tools/vitruv/framework/views/ViewProvider.java @@ -0,0 +1,18 @@ +package tools.vitruv.framework.views; + +/** + * A provider for views, i.e. this object contains the source models to create a + * view from and provides according selectors for given view types to + * instantiate a view from. + */ +public interface ViewProvider { + /** + * Returns a view selector for the given {@link ViewType} based on the source + * models covered by this object. + * + * @param viewType the {@link ViewType} to create a selector for + * @returns a {@link ViewSelector} for given view type and the source models + * covered by this object + */ + S createSelector(ViewType viewType); +} diff --git a/views/src/main/java/tools/vitruv/framework/views/ViewSelection.java b/views/src/main/java/tools/vitruv/framework/views/ViewSelection.java new file mode 100644 index 0000000000..b5e443bc25 --- /dev/null +++ b/views/src/main/java/tools/vitruv/framework/views/ViewSelection.java @@ -0,0 +1,15 @@ +package tools.vitruv.framework.views; + +import org.eclipse.emf.ecore.EObject; + +/** + * A representation of the elements selected to be represented in a view. + */ +public interface ViewSelection { + /** + * Returns whether the given element is selected to be represented in a view. + * + * @return whether the given {@link EObject} is to be represented in the view + */ + boolean isViewObjectSelected(EObject eObject); +} diff --git a/views/src/main/java/tools/vitruv/framework/views/ViewSelector.java b/views/src/main/java/tools/vitruv/framework/views/ViewSelector.java new file mode 100644 index 0000000000..dcdc5b0bf3 --- /dev/null +++ b/views/src/main/java/tools/vitruv/framework/views/ViewSelector.java @@ -0,0 +1,34 @@ +package tools.vitruv.framework.views; + +/** + * A selector for selecting the elements to be represented in a view. It + * encapsulates a modifiable {@link ViewSelection}, which it is able to validate + * and which is then passed to a created view. It is capable of acting as a + * builder for a view by providing an appropriate creation method. + */ +public interface ViewSelector extends ModifiableViewSelection { + /** + * Creates a view for the underlying source models and the view type this + * selector has been created for as well as for the selection performed in this + * selector. May only be called if the current selection is valid as returned by + * {@link #isValid()}. + * + * @return the created {@link View} + */ + View createView(); + + /** + * Checks whether the current selection is valid and thus calling + * {@link #createView()} is possible. + * + * @return whether the current selection is valid + */ + boolean isValid(); + + /** + * Returns an immutable copy of the selection resulting from this selector. + * + * @return an immutable copy of the selection in this selector + */ + ViewSelection getSelection(); +} diff --git a/views/src/main/java/tools/vitruv/framework/views/ViewSource.java b/views/src/main/java/tools/vitruv/framework/views/ViewSource.java new file mode 100644 index 0000000000..ba3a389131 --- /dev/null +++ b/views/src/main/java/tools/vitruv/framework/views/ViewSource.java @@ -0,0 +1,28 @@ +package tools.vitruv.framework.views; + +import java.util.Collection; + +import org.eclipse.emf.ecore.resource.Resource; + +import tools.vitruv.change.atomic.uuid.UuidResolver; + +/** + * A view source giving access to the underlying source models of the view. + */ +public interface ViewSource { + /** + * Returns {@link Resource}s representing the source models of a view to be + * instantiated. + * + * @return {@link Resource}s as the sources of a view + */ + Collection getViewSourceModels(); + + /** + * Returns the {@link UuidResolver} associated with the resources in this view + * source. + * + * @return the {@link UuidResolver} of this view source. + */ + public UuidResolver getUuidResolver(); +} diff --git a/views/src/main/java/tools/vitruv/framework/views/ViewType.java b/views/src/main/java/tools/vitruv/framework/views/ViewType.java new file mode 100644 index 0000000000..8ed8df58a4 --- /dev/null +++ b/views/src/main/java/tools/vitruv/framework/views/ViewType.java @@ -0,0 +1,24 @@ +package tools.vitruv.framework.views; + +/** + * A Vitruv view type on the virtual model, providing a view selector and allows + * creating views. + * + * @param the type of view selector this view type uses + */ +public interface ViewType { + /** + * Returns the name of the view type. + */ + String getName(); + + /** + * Returns the view selector of the view type, which allows configuring views + * based on the given {@link ChangeableViewSource}. + * + * @param viewSource the {@link ChangeableViewSource} from which views shall be + * derived. + * @returns a {@link ViewSelector} for this view type + */ + S createSelector(ChangeableViewSource viewSource); +} diff --git a/views/src/main/java/tools/vitruv/framework/views/ViewTypeProvider.java b/views/src/main/java/tools/vitruv/framework/views/ViewTypeProvider.java new file mode 100644 index 0000000000..7bcc6b34d5 --- /dev/null +++ b/views/src/main/java/tools/vitruv/framework/views/ViewTypeProvider.java @@ -0,0 +1,15 @@ +package tools.vitruv.framework.views; + +import java.util.Collection; + +/** + * A provider for view types. + */ +public interface ViewTypeProvider { + /** + * Returns the view types covered by this provider. + * + * @return a collection of {@link ViewType}s + */ + public Collection> getViewTypes(); +} diff --git a/views/src/main/java/tools/vitruv/framework/views/ViewTypeRepositoryTest.java b/views/src/main/java/tools/vitruv/framework/views/ViewTypeRepositoryTest.java deleted file mode 100644 index 86a6f1c7e1..0000000000 --- a/views/src/main/java/tools/vitruv/framework/views/ViewTypeRepositoryTest.java +++ /dev/null @@ -1,107 +0,0 @@ -package tools.vitruv.framework.views; - -import static java.util.Collections.emptySet; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static tools.vitruv.framework.views.ViewTypeFactory.createIdentityMappingViewType; - -import java.util.HashSet; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging; - -@ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) -public class ViewTypeRepositoryTest { - @Nested - @DisplayName("register") - public class Register { - @Test - @DisplayName("proper view type") - public void properViewType() { - ViewTypeRepository repository = new ViewTypeRepository(); - ViewType viewType = createIdentityMappingViewType("type"); - repository.register(viewType); - assertThat(repository.getViewTypes().size(), is(1)); - assertThat(repository.getViewTypes(), hasItem(viewType)); - } - - @Test - @DisplayName("null view type") - public void nullViewType() { - ViewTypeRepository repository = new ViewTypeRepository(); - assertThrows(IllegalArgumentException.class, () -> repository.register(null)); - } - - @Test - @DisplayName("same view type twice") - public void sameViewTypeTwice() { - ViewTypeRepository repository = new ViewTypeRepository(); - ViewType viewType = createIdentityMappingViewType("type"); - repository.register(viewType); - assertThrows(IllegalStateException.class, () -> repository.register(viewType)); - } - - @Test - @DisplayName("another view type with same name") - public void viewTypeWithSameName() { - ViewTypeRepository repository = new ViewTypeRepository(); - ViewType firstViewType = createIdentityMappingViewType("type"); - ViewType secondViewType = createIdentityMappingViewType("type"); - repository.register(firstViewType); - assertThrows(IllegalStateException.class, () -> repository.register(secondViewType)); - } - } - - @Nested - @DisplayName("retrieve") - public class Retrieve { - @Test - @DisplayName("provides only a copy of view type list") - public void providesOnlyCopy() { - ViewTypeRepository repository = new ViewTypeRepository(); - ViewType viewType = createIdentityMappingViewType("type"); - repository.getViewTypes().add(viewType); - assertThat(new HashSet<>(repository.getViewTypes()), is(emptySet())); - } - } - - @Nested - @DisplayName("find") - public class Find { - @Test - @DisplayName("registered view type by name") - public void registeredViewType() { - ViewTypeRepository repository = new ViewTypeRepository(); - ViewType viewType = createIdentityMappingViewType("type"); - repository.register(viewType); - assertThat(repository.findViewType("type"), is(viewType)); - } - - @Test - @DisplayName("non-registered view type by name") - public void nonRegisteredViewType() { - ViewTypeRepository repository = new ViewTypeRepository(); - ViewType viewType = createIdentityMappingViewType("type"); - repository.register(viewType); - assertThat(repository.findViewType("other"), equalTo(null)); - } - - @Test - @DisplayName("null name") - public void nullName() { - ViewTypeRepository repository = new ViewTypeRepository(); - ViewType viewType = createIdentityMappingViewType("type"); - repository.register(viewType); - assertThrows(IllegalArgumentException.class, () -> repository.findViewType(null)); - } - - } -} diff --git a/views/src/main/java/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.java b/views/src/main/java/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.java new file mode 100644 index 0000000000..fa5d130cd5 --- /dev/null +++ b/views/src/main/java/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.java @@ -0,0 +1,54 @@ +package tools.vitruv.framework.views.changederivation; + +import org.eclipse.emf.ecore.resource.Resource; + +import tools.vitruv.change.atomic.hid.HierarchicalId; +import tools.vitruv.change.composite.description.VitruviusChange; + +/** + * Strategy for resolving state-based changes to individual change sequences. + * This strategy is used by the domains when there are no change sequences + * available and changes need to be propagated based on the difference between + * the old and new state. + */ +public interface StateBasedChangeResolutionStrategy { + + /** + * Resolves the state-based delta of two resources and returns the correlating + * change sequences. Changes must use hierarchical IDs and are returned + * unresolved. The {@code oldState} remains unmodified from calling this method. + * + * @param newState is the new state of the resource, must not be + * null and must not contain proxies. + * @param oldState is the current or old state of the resource, must not be + * null and must not contain proxies. + * @return a unresolved {@link VitruviusChange} that contains the individual + * change sequence. + */ + VitruviusChange getChangeSequenceBetween(Resource newState, Resource oldState); + + /** + * Resolves the state-based delta for creating the given resource and returns + * the correlating change sequences. Changes must use hierarchical IDs and are + * returned unresolved. + * + * @param newState is the new state of the resource, must not be + * null and must not contain proxies. + * @return a unresolved {@link VitruviusChange} that contains the individual + * change sequence. + */ + VitruviusChange getChangeSequenceForCreated(Resource newState); + + /** + * Resolves the state-based delta for deleting the given resource and returns + * the correlating change sequences. Changes must use hierarchical IDs and are + * returned unresolved. The {@code oldState} remains unmodified from calling + * this method. + * + * @param oldState is the new state of the resource, must not be + * null and must not contain proxies. + * @return a unresolved {@link VitruviusChange} that contains the individual + * change sequence. + */ + VitruviusChange getChangeSequenceForDeleted(Resource oldState); +} diff --git a/views/src/main/java/tools/vitruv/framework/views/impl/BasicViewTest.java b/views/src/main/java/tools/vitruv/framework/views/impl/BasicViewTest.java deleted file mode 100644 index 97616f2b03..0000000000 --- a/views/src/main/java/tools/vitruv/framework/views/impl/BasicViewTest.java +++ /dev/null @@ -1,262 +0,0 @@ -package tools.vitruv.framework.views.impl; - -import static org.hamcrest.CoreMatchers.anything; -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; - -import org.eclipse.emf.common.util.URI; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import allElementTypes.NonRoot; -import allElementTypes.Root; -import tools.vitruv.change.atomic.hid.HierarchicalId; -import tools.vitruv.framework.views.ChangeableViewSource; -import tools.vitruv.framework.views.ModifiableViewSelection; -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging; - -@ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) -public class BasicViewTest { - @Mock - ViewCreatingViewType mockViewType; - @Mock - ChangeableViewSource mockChangeableViewSource; - @Mock - ModifiableViewSelection mockViewSelection; - - @BeforeEach - public void initializeMocks() { - MockitoAnnotations.openMocks(this); - } - - @Nested - @DisplayName("initialize") - public class Initialize { - @Test - @DisplayName("with null view type") - public void withNullViewType() { - assertThrows(IllegalArgumentException.class, - () -> new BasicView(null, mockChangeableViewSource, mockViewSelection)); - } - - @Test - @DisplayName("with null view source") - public void withNullViewSource() { - assertThrows(IllegalArgumentException.class, - () -> new BasicView(mockViewType, null, mockViewSelection)); - } - - @Test - @DisplayName("with null view selection") - public void withNullViewSelection() { - assertThrows(IllegalArgumentException.class, - () -> new BasicView(mockViewType, mockChangeableViewSource, null)); - } - - @Test - @DisplayName("with proper arguments") - public void withEmptySource() throws Exception { - try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { - verify(mockViewType).updateView(view); - assertThat(view.isClosed(), is(false)); - assertThat(view.getRootObjects(), not(hasItem(anything()))); - } - } - } - - @Nested - @DisplayName("retrieve roots") - public class RetrieveRootElements { - @Test - @DisplayName("all of same type") - public void allOfSameType() throws Exception { - try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { - Root root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - Root root2 = aet.Root(); - view.registerRoot(root2, URI.createURI("test://test2.aet")); - assertThat(view.getRootObjects().size(), is(2)); - assertThat(view.getRootObjects(Root.class).size(), is(2)); - assertThat(view.getRootObjects(), hasItem(root)); - assertThat(view.getRootObjects(Root.class), hasItem(root2)); - assertThat(view.getRootObjects(), hasItem(root)); - assertThat(view.getRootObjects(Root.class), hasItem(root2)); - } - } - - @Test - @DisplayName("all of one out of two types") - public void containingAllOfOneType() throws Exception { - try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { - Root root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - NonRoot otherRoot = aet.NonRoot(); - view.registerRoot(otherRoot, URI.createURI("test://test2.aet")); - assertThat(view.getRootObjects().size(), is(2)); - assertThat(view.getRootObjects(Root.class).size(), is(1)); - assertThat(view.getRootObjects(), hasItem(root)); - assertThat(view.getRootObjects(Root.class), hasItem(root)); - assertThat(view.getRootObjects(), hasItem(otherRoot)); - } - } - - @Test - @DisplayName("containing none of a type") - public void containingNoneOfType() throws Exception { - try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { - Root root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - Root otherRoot = aet.Root(); - view.registerRoot(otherRoot, URI.createURI("test://test2.aet")); - assertThat(view.getRootObjects().size(), is(2)); - assertThat(view.getRootObjects(), hasItem(root)); - assertThat(view.getRootObjects(), hasItem(otherRoot)); - assertThat(view.getRootObjects(NonRoot.class), not(hasItem(anything()))); - } - } - } - - @Nested - @DisplayName("update") - public class Update { - @Test - @DisplayName("without previous modification") - public void withoutPreviousModification() throws Exception { - try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { - view.update(); - verify(mockViewType, times(2)).updateView(view); - } - } - - @Test - @DisplayName("with previous modification") - public void withPreviousModification() throws Exception { - try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { - view.modifyContents((resourceSet) -> resourceSet.createResource(URI.createURI("test://test.aet"))); - assertThrows(IllegalStateException.class, () -> view.update()); - } - } - } - - @Nested - @DisplayName("add root") - public class AddRoot { - @Test - @DisplayName("being null") - public void nullElement() throws Exception { - try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { - assertThrows(IllegalArgumentException.class, - () -> view.registerRoot(null, URI.createURI("test://test.aet"))); - } - } - - @Test - @DisplayName("with null URI") - public void nullUri() throws Exception { - try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { - Root root = aet.Root(); - assertThrows(IllegalArgumentException.class, () -> view.registerRoot(root, null)); - } - } - - @Test - @DisplayName("with proper arguments") - public void properArguments() throws Exception { - try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { - Root root = aet.Root(); - String testResourceUriString = "test://test.aet"; - view.registerRoot(root, URI.createURI(testResourceUriString)); - assertThat(view.getRootObjects(), hasItem(root)); - } - } - } - - @Nested - @DisplayName("move root") - public class MoveRoot { - @Test - @DisplayName("being null") - public void nullElement() throws Exception { - try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { - assertThrows(IllegalArgumentException.class, - () -> view.moveRoot(null, URI.createURI("test://test.aet"))); - } - } - - @Test - @DisplayName("with null URI") - public void nullUri() throws Exception { - try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { - Root root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - assertThrows(IllegalArgumentException.class, () -> view.moveRoot(root, null)); - } - } - - @Test - @DisplayName("with element not beeing root") - public void notBeingRoot() throws Exception { - try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { - Root root = aet.Root(); - assertThrows(IllegalStateException.class, () -> view.moveRoot(root, URI.createURI("test://test.aet"))); - } - } - - @Test - @DisplayName("with proper arguments") - public void properArguments() throws Exception { - try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { - Root root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - view.moveRoot(root, URI.createURI("test://test2.aet")); - assertThat(view.getRootObjects().size(), is(1)); - assertThat(view.getRootObjects(), hasItem(root)); - } - } - } - - @Nested - @DisplayName("close") - public class Close { - @Test - @DisplayName("and is closed afterwards") - public void isClosed() throws Exception { - BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection); - view.close(); - assertThat("view should be closed", view.isClosed()); - } - - @Test - @DisplayName("can be called multiple times") - public void callMultipleTimes() throws Exception { - BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection); - view.close(); - view.close(); - assertThat("view should be closed", view.isClosed()); - } - - @Test - @DisplayName("and does not allow further operations") - public void noOperations() throws Exception { - BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection); - view.close(); - assertThrows(IllegalStateException.class, () -> view.getRootObjects()); - assertThrows(IllegalStateException.class, () -> view.getRootObjects(Root.class)); - assertThrows(IllegalStateException.class, () -> view.update()); - assertThrows(IllegalStateException.class, () -> view.registerRoot(null, null)); - assertThrows(IllegalStateException.class, () -> view.moveRoot(null, null)); - } - } -} diff --git a/views/src/main/java/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java b/views/src/main/java/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java deleted file mode 100644 index 6157548b81..0000000000 --- a/views/src/main/java/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java +++ /dev/null @@ -1,410 +0,0 @@ -package tools.vitruv.framework.views.impl; - -import static org.hamcrest.CoreMatchers.anything; -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static tools.vitruv.testutils.matchers.ModelMatchers.equalsDeeply; -import static tools.vitruv.testutils.matchers.ModelMatchers.ignoringFeatures; -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; - -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EObject; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import allElementTypes.NonRoot; -import allElementTypes.Root; -import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute; -import tools.vitruv.change.atomic.hid.HierarchicalId; -import tools.vitruv.change.atomic.root.InsertRootEObject; -import tools.vitruv.change.atomic.root.RootFactory; -import tools.vitruv.change.atomic.root.RootPackage; -import tools.vitruv.change.composite.description.VitruviusChange; -import tools.vitruv.change.composite.description.VitruviusChangeResolver; -import tools.vitruv.framework.views.ChangeableViewSource; -import tools.vitruv.framework.views.ModifiableViewSelection; -import tools.vitruv.framework.views.changederivation.DefaultStateBasedChangeResolutionStrategy; -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging; - -@ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) -public class ChangeDerivingViewTest { - @Mock - ViewCreatingViewType mockViewType; - @Mock - ChangeableViewSource mockChangeableViewSource; - @Mock - ModifiableViewSelection mockViewSelection; - - @BeforeEach - public void initializeMocks() { - MockitoAnnotations.openMocks(this); - } - - @Nested - @DisplayName("initialize") - public class Initialize { - @Test - @DisplayName("with null view type") - public void withNullViewType() { - assertThrows(IllegalArgumentException.class, () -> new ChangeDerivingView(null, new DefaultStateBasedChangeResolutionStrategy())); - } - - @Test - @DisplayName("with null change resolution strategy") - public void withNullChangeResolutionStrategy() { - assertThrows(IllegalArgumentException.class, - () -> new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection), null)); - } - - @Test - @DisplayName("with proper arguments") - public void withEmptySource() throws Exception { - try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - assertThat(view.isClosed(), is(false)); - assertThat(view.getRootObjects(), not(hasItem(anything()))); - } - } - } - - @Nested - @DisplayName("retrieve roots") - public class RetrieveRootElements { - @Test - @DisplayName("all of same type") - public void allOfSameType() throws Exception { - try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - Root root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - Root root2 = aet.Root(); - view.registerRoot(root2, URI.createURI("test://test2.aet")); - assertThat(view.getRootObjects().size(), is(2)); - assertThat(view.getRootObjects(Root.class).size(), is(2)); - assertThat(view.getRootObjects(), hasItem(root)); - assertThat(view.getRootObjects(Root.class), hasItem(root2)); - assertThat(view.getRootObjects(), hasItem(root)); - assertThat(view.getRootObjects(Root.class), hasItem(root2)); - } - } - - @Test - @DisplayName("all of one out of two types") - public void containingAllOfOneType() throws Exception { - try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - Root root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - NonRoot otherRoot = aet.NonRoot(); - view.registerRoot(otherRoot, URI.createURI("test://test2.aet")); - assertThat(view.getRootObjects().size(), is(2)); - assertThat(view.getRootObjects(Root.class).size(), is(1)); - assertThat(view.getRootObjects(), hasItem(root)); - assertThat(view.getRootObjects(Root.class), hasItem(root)); - assertThat(view.getRootObjects(), hasItem(otherRoot)); - } - } - - @Test - @DisplayName("containing none of a type") - public void containingNoneOfType() throws Exception { - try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - Root root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - Root otherRoot = aet.Root(); - view.registerRoot(otherRoot, URI.createURI("test://test2.aet")); - assertThat(view.getRootObjects().size(), is(2)); - assertThat(view.getRootObjects(), hasItem(root)); - assertThat(view.getRootObjects(), hasItem(otherRoot)); - assertThat(view.getRootObjects(NonRoot.class), not(hasItem(anything()))); - } - } - } - - @Nested - @DisplayName("update") - public class Update { - @Test - @DisplayName("without previous modification") - public void withoutPreviousModification() throws Exception { - try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - view.update(); - } - } - - @Test - @DisplayName("with previous modification") - public void withPreviousModification() throws Exception { - try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - view.modifyContents((resourceSet) -> resourceSet.createResource(URI.createURI("test://test.aet"))); - assertThrows(IllegalStateException.class, () -> view.update()); - } - } - } - - @Nested - @DisplayName("add root") - public class AddRoot { - @Test - @DisplayName("being null") - public void nullElement() throws Exception { - try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - assertThrows(IllegalArgumentException.class, - () -> view.registerRoot(null, URI.createURI("test://test.aet"))); - } - } - - @Test - @DisplayName("with null URI") - public void nullUri() throws Exception { - try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - Root root = aet.Root(); - assertThrows(IllegalArgumentException.class, () -> view.registerRoot(root, null)); - } - } - - @Test - @DisplayName("with proper arguments") - public void properArguments() throws Exception { - try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - Root root = aet.Root(); - String testResourceUriString = "test://test.aet"; - view.registerRoot(root, URI.createURI(testResourceUriString)); - assertThat(view.getRootObjects(), hasItem(root)); - } - } - - @Test - @DisplayName("committing changes") - public void commitChanges() throws Exception { - try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - Root root = aet.Root(); - root.setId("root"); - String testResourceUriString = "test://test.aet"; - view.registerRoot(root, URI.createURI(testResourceUriString)); - assertThat(view.getRootObjects(), hasItem(root)); - ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeDerivingView.class); - ArgumentCaptor> changeArgument = ArgumentCaptor.forClass(VitruviusChange.class); - view.commitChanges(); - verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); - assertThat(viewArgument.getValue(), is(view)); - VitruviusChange change = VitruviusChangeResolver.forHierarchicalIds(root.eResource().getResourceSet()).resolveAndApply(changeArgument.getValue()); - InsertRootEObject expectedChange = RootFactory.eINSTANCE.createInsertRootEObject(); - expectedChange.setNewValue(root); - expectedChange.setUri(testResourceUriString); - assertThat(change.getEChanges().size(), is(3)); // Create, Insert, ReplaceId - assertThat(change.getEChanges().get(1), - equalsDeeply(expectedChange, - ignoringFeatures(RootPackage.eINSTANCE.getRootEChange_Resource()))); - assertThat(change.getEChanges().get(2), instanceOf(ReplaceSingleValuedEAttribute.class)); - } - } - } - - @Nested - @DisplayName("move root") - public class MoveRoot { - @Test - @DisplayName("being null") - public void nullElement() throws Exception { - try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - assertThrows(IllegalArgumentException.class, - () -> view.moveRoot(null, URI.createURI("test://test.aet"))); - } - } - - @Test - @DisplayName("with null URI") - public void nullUri() throws Exception { - try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - Root root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - assertThrows(IllegalArgumentException.class, () -> view.moveRoot(root, null)); - } - } - - @Test - @DisplayName("with element not beeing root") - public void notBeingRoot() throws Exception { - try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - Root root = aet.Root(); - assertThrows(IllegalStateException.class, () -> view.moveRoot(root, URI.createURI("test://test.aet"))); - } - } - - @Test - @DisplayName("with proper arguments") - public void properArguments() throws Exception { - try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - Root root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - view.moveRoot(root, URI.createURI("test://test2.aet")); - assertThat(view.getRootObjects().size(), is(1)); - assertThat(view.getRootObjects(), hasItem(root)); - } - } - - @Test - @DisplayName("committing changes") - public void commitChanges() throws Exception { - try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - Root root = aet.Root(); - String movedResourceUriString = "test://test2.aet"; - view.registerRoot(root, URI.createURI("test://test.aet")); - view.commitChanges(); - reset(mockChangeableViewSource, mockViewType); - view.moveRoot(root, URI.createURI(movedResourceUriString)); - assertThat(view.getRootObjects(), hasItem(root)); - ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeDerivingView.class); - ArgumentCaptor> changeArgument = ArgumentCaptor.forClass(VitruviusChange.class); - view.commitChangesAndUpdate(); - verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); - assertThat(viewArgument.getValue(), is(view)); - assertTrue(changeArgument.getValue().containsConcreteChange(), "change must contain some concrete change"); - assertThat(view.getRootObjects().size(), is(1)); - Root updatedRoot = (Root)view.getRootObjects().iterator().next(); - assertThat(updatedRoot.eResource().getURI(), is(URI.createURI(movedResourceUriString))); - } - } - } - - @Nested - @DisplayName("commit") - public class Commit { - ChangeDerivingView view; - Root root; - - @BeforeEach - public void prepareViewWithRootElement() { - view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy()); - root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - view.commitChangesAndUpdate(); - reset(mockChangeableViewSource, mockViewType); - } - - @AfterEach - public void closeView() throws Exception { - view.close(); - } - - @Test - @DisplayName("once") - public void once() { - NonRoot nonRoot = aet.NonRoot(); - nonRoot.setId("nonRoot"); - root.setSingleValuedContainmentEReference(nonRoot); - ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeDerivingView.class); - ArgumentCaptor> changeArgument = ArgumentCaptor.forClass(VitruviusChange.class); - view.commitChangesAndUpdate(); - verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); - assertThat(viewArgument.getValue(), is(view)); - assertTrue(changeArgument.getValue().containsConcreteChange(), "change must contain some concrete change"); - assertThat(view.getRootObjects().size(), is(1)); - Root root = (Root)view.getRootObjects().iterator().next(); - assertThat(root.eContents().size(), is(1)); - assertThat(root.eContents(), hasItem(nonRoot)); - } - - @Test - @DisplayName("twice") - public void twice() { - NonRoot firstNonRoot = aet.NonRoot(); - firstNonRoot.setId("first"); - root.setSingleValuedContainmentEReference(firstNonRoot); - view.commitChanges(); - reset(mockChangeableViewSource, mockViewType); - NonRoot secondNonRoot = aet.NonRoot(); - secondNonRoot.setId("second"); - root.setSingleValuedContainmentEReference(secondNonRoot); - ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeDerivingView.class); - ArgumentCaptor> changeArgument = ArgumentCaptor.forClass(VitruviusChange.class); - view.commitChangesAndUpdate(); - verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); - assertThat(viewArgument.getValue(), is(view)); - assertTrue(changeArgument.getValue().containsConcreteChange(), "change must contain some concrete change"); - assertThat(view.getRootObjects().size(), is(1)); - Root root = (Root)view.getRootObjects().iterator().next(); - assertThat(root.eContents().size(), is(1)); - assertThat(root.eContents(), hasItem(secondNonRoot)); - } - - @Test - @DisplayName("without changes") - public void withoutChanges() { - ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeDerivingView.class); - ArgumentCaptor> changeArgument = ArgumentCaptor.forClass(VitruviusChange.class); - view.commitChangesAndUpdate(); - verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); - assertThat(viewArgument.getValue(), is(view)); - assertFalse(changeArgument.getValue().containsConcreteChange(), "change must be empty"); - assertThat(view.getRootObjects().size(), is(1)); - } - } - - @Nested - @DisplayName("close") - public class Close { - @Test - @DisplayName("and is closed afterwards") - public void isClosed() throws Exception { - ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy()); - view.close(); - assertThat("view should be closed", view.isClosed()); - } - - @Test - @DisplayName("can be called multiple times") - public void callMultipleTimes() throws Exception { - ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy()); - view.close(); - view.close(); - assertThat("view should be closed", view.isClosed()); - } - - @Test - @DisplayName("and does not allow further operations") - public void noOperations() throws Exception { - ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, - mockViewSelection), new DefaultStateBasedChangeResolutionStrategy()); - view.close(); - assertThrows(IllegalStateException.class, () -> view.getRootObjects()); - assertThrows(IllegalStateException.class, () -> view.getRootObjects(Root.class)); - assertThrows(IllegalStateException.class, () -> view.update()); - assertThrows(IllegalStateException.class, () -> view.commitChanges()); - assertThrows(IllegalStateException.class, () -> view.registerRoot(null, null)); - assertThrows(IllegalStateException.class, () -> view.moveRoot(null, null)); - } - } -} diff --git a/views/src/main/java/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java b/views/src/main/java/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java deleted file mode 100644 index c08fa2557a..0000000000 --- a/views/src/main/java/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java +++ /dev/null @@ -1,442 +0,0 @@ -package tools.vitruv.framework.views.impl; - -import static org.hamcrest.CoreMatchers.anything; -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static tools.vitruv.testutils.matchers.ModelMatchers.equalsDeeply; -import static tools.vitruv.testutils.matchers.ModelMatchers.ignoringFeatures; -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; - -import java.util.List; - -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.resource.ResourceSet; -import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; -import org.eclipse.emf.ecore.util.EcoreUtil; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import allElementTypes.AllElementTypesPackage; -import allElementTypes.NonRoot; -import allElementTypes.Root; -import tools.vitruv.change.atomic.EChange; -import tools.vitruv.change.atomic.eobject.CreateEObject; -import tools.vitruv.change.atomic.eobject.DeleteEObject; -import tools.vitruv.change.atomic.feature.attribute.AttributeFactory; -import tools.vitruv.change.atomic.feature.attribute.AttributePackage; -import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute; -import tools.vitruv.change.atomic.feature.reference.ReplaceSingleValuedEReference; -import tools.vitruv.change.atomic.hid.HierarchicalId; -import tools.vitruv.change.atomic.root.InsertRootEObject; -import tools.vitruv.change.atomic.root.RootFactory; -import tools.vitruv.change.atomic.root.RootPackage; -import tools.vitruv.change.composite.description.VitruviusChange; -import tools.vitruv.change.composite.description.VitruviusChangeResolver; -import tools.vitruv.framework.views.ChangeableViewSource; -import tools.vitruv.framework.views.ModifiableViewSelection; -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging;; - -@ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) -public class ChangeRecordingViewTest { - @Mock - ViewCreatingViewType mockViewType; - @Mock - ChangeableViewSource mockChangeableViewSource; - @Mock - ModifiableViewSelection mockViewSelection; - - @BeforeEach - public void initializeMocks() { - MockitoAnnotations.openMocks(this); - } - - @Nested - @DisplayName("initialize") - public class Initialize { - @Test - @DisplayName("with null view") - public void withNullViewType() { - assertThrows(IllegalArgumentException.class, () -> new ChangeRecordingView(null)); - } - - @Test - @DisplayName("with proper arguments") - public void withEmptySource() throws Exception { - try (ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - assertThat(view.isClosed(), is(false)); - assertThat(view.getRootObjects(), not(hasItem(anything()))); - } - } - } - - @Nested - @DisplayName("retrieve roots") - public class RetrieveRootElements { - @Test - @DisplayName("all of same type") - public void allOfSameType() throws Exception { - try (ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - Root root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - Root root2 = aet.Root(); - view.registerRoot(root2, URI.createURI("test://test2.aet")); - assertThat(view.getRootObjects().size(), is(2)); - assertThat(view.getRootObjects(Root.class).size(), is(2)); - assertThat(view.getRootObjects(), hasItem(root)); - assertThat(view.getRootObjects(Root.class), hasItem(root2)); - assertThat(view.getRootObjects(), hasItem(root)); - assertThat(view.getRootObjects(Root.class), hasItem(root2)); - } - } - - @Test - @DisplayName("all of one out of two types") - public void containingAllOfOneType() throws Exception { - try (ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - Root root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - NonRoot otherRoot = aet.NonRoot(); - view.registerRoot(otherRoot, URI.createURI("test://test2.aet")); - assertThat(view.getRootObjects().size(), is(2)); - assertThat(view.getRootObjects(Root.class).size(), is(1)); - assertThat(view.getRootObjects(), hasItem(root)); - assertThat(view.getRootObjects(Root.class), hasItem(root)); - assertThat(view.getRootObjects(), hasItem(otherRoot)); - } - } - - @Test - @DisplayName("containing none of a type") - public void containingNoneOfType() throws Exception { - try (ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - Root root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - Root otherRoot = aet.Root(); - view.registerRoot(otherRoot, URI.createURI("test://test2.aet")); - assertThat(view.getRootObjects().size(), is(2)); - assertThat(view.getRootObjects(), hasItem(root)); - assertThat(view.getRootObjects(), hasItem(otherRoot)); - assertThat(view.getRootObjects(NonRoot.class), not(hasItem(anything()))); - } - } - } - - @Nested - @DisplayName("update") - public class Update { - @Test - @DisplayName("without previous modification") - public void withoutPreviousModification() throws Exception { - try (ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - view.update(); - } - } - - @Test - @DisplayName("with previous modification") - public void withPreviousModification() throws Exception { - try (ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - view.modifyContents((resourceSet) -> resourceSet.createResource(URI.createURI("test://test.aet"))); - assertThrows(IllegalStateException.class, () -> view.update()); - } - } - } - - @Nested - @DisplayName("add root") - public class AddRoot { - @Test - @DisplayName("being null") - public void nullElement() throws Exception { - try (ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - assertThrows(IllegalArgumentException.class, - () -> view.registerRoot(null, URI.createURI("test://test.aet"))); - } - } - - @Test - @DisplayName("with null URI") - public void nullUri() throws Exception { - try (ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - Root root = aet.Root(); - assertThrows(IllegalArgumentException.class, () -> view.registerRoot(root, null)); - } - } - - @Test - @DisplayName("with proper arguments") - public void properArguments() throws Exception { - try (ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - Root root = aet.Root(); - String testResourceUriString = "test://test.aet"; - view.registerRoot(root, URI.createURI(testResourceUriString)); - assertThat(view.getRootObjects(), hasItem(root)); - } - } - - @Test - @DisplayName("committing changes") - public void commitChanges() throws Exception { - try (ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - Root root = aet.Root(); - root.setId("root"); - String testResourceUriString = "test://test.aet"; - view.registerRoot(root, URI.createURI(testResourceUriString)); - assertThat(view.getRootObjects(), hasItem(root)); - ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeRecordingView.class); - ArgumentCaptor> changeArgument = ArgumentCaptor - .forClass(VitruviusChange.class); - view.commitChanges(); - verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); - - assertThat(viewArgument.getValue(), is(view)); - ResourceSet resolveInResourceSet = new ResourceSetImpl(); - VitruviusChange resolvedChange = VitruviusChangeResolver - .forHierarchicalIds(resolveInResourceSet).resolveAndApply(changeArgument.getValue()); - InsertRootEObject expectedChange = RootFactory.eINSTANCE.createInsertRootEObject(); - expectedChange.setNewValue(root); - expectedChange.setUri(testResourceUriString); - assertThat(resolvedChange.getEChanges().size(), is(3)); // Create, Insert, ReplaceId - assertThat(resolvedChange.getEChanges().get(1), equalsDeeply(expectedChange, - ignoringFeatures(RootPackage.eINSTANCE.getRootEChange_Resource()))); - assertThat(resolvedChange.getEChanges().get(2), instanceOf(ReplaceSingleValuedEAttribute.class)); - } - } - } - - @Nested - @DisplayName("move root") - public class MoveRoot { - @Test - @DisplayName("being null") - public void nullElement() throws Exception { - try (ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - assertThrows(IllegalArgumentException.class, - () -> view.moveRoot(null, URI.createURI("test://test.aet"))); - } - } - - @Test - @DisplayName("with null URI") - public void nullUri() throws Exception { - try (ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - Root root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - assertThrows(IllegalArgumentException.class, () -> view.moveRoot(root, null)); - } - } - - @Test - @DisplayName("with element not beeing root") - public void notBeingRoot() throws Exception { - try (ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - Root root = aet.Root(); - assertThrows(IllegalStateException.class, () -> view.moveRoot(root, URI.createURI("test://test.aet"))); - } - } - - @Test - @DisplayName("with proper arguments") - public void properArguments() throws Exception { - try (ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - Root root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - view.moveRoot(root, URI.createURI("test://test2.aet")); - assertThat(view.getRootObjects().size(), is(1)); - assertThat(view.getRootObjects(), hasItem(root)); - } - } - - @Test - @DisplayName("committing changes") - public void commitChanges() throws Exception { - try (ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - Root root = aet.Root(); - String movedResourceUriString = "test://test2.aet"; - view.registerRoot(root, URI.createURI("test://test.aet")); - view.commitChanges(); - reset(mockChangeableViewSource, mockViewType); - - ResourceSet resolveInResourceSet = new ResourceSetImpl(); - resolveInResourceSet.createResource(root.eResource().getURI()); - resolveInResourceSet.getResource(root.eResource().getURI(), false).getContents() - .add(EcoreUtil.copy(root)); - - view.moveRoot(root, URI.createURI(movedResourceUriString)); - assertThat(view.getRootObjects(), hasItem(root)); - ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeRecordingView.class); - ArgumentCaptor> changeArgument = ArgumentCaptor - .forClass(VitruviusChange.class); - view.commitChanges(); - verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); - assertThat(viewArgument.getValue(), is(view)); - VitruviusChange resolvedChange = VitruviusChangeResolver - .forHierarchicalIds(resolveInResourceSet).resolveAndApply(changeArgument.getValue()); - List> capturedEChanges = resolvedChange.getEChanges(); - InsertRootEObject expectedChange = RootFactory.eINSTANCE.createInsertRootEObject(); - expectedChange.setNewValue(root); - expectedChange.setUri(movedResourceUriString); - assertThat(capturedEChanges.size(), is(2)); // Remove, Insert - assertThat(capturedEChanges.get(1), equalsDeeply(expectedChange, - ignoringFeatures(RootPackage.eINSTANCE.getRootEChange_Resource()))); - } - } - } - - @Nested - @DisplayName("commit") - public class Commit { - ChangeRecordingView view; - Root root; - - @BeforeEach - public void prepareViewWithRootElement() { - view = new ChangeRecordingView(new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)); - root = aet.Root(); - view.registerRoot(root, URI.createURI("test://test.aet")); - view.commitChanges(); - reset(mockChangeableViewSource, mockViewType); - } - - @AfterEach - public void closeView() throws Exception { - view.close(); - } - - @Test - @DisplayName("once") - public void once() { - NonRoot nonRoot = aet.NonRoot(); - nonRoot.setId("nonRoot"); - root.setSingleValuedContainmentEReference(nonRoot); - ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeRecordingView.class); - ArgumentCaptor> changeArgument = ArgumentCaptor - .forClass(VitruviusChange.class); - view.commitChanges(); - verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); - assertThat(viewArgument.getValue(), is(view)); - List> capturedEChanges = changeArgument.getValue().getEChanges(); - assertThat(capturedEChanges.size(), is(3)); // Create, Insert, ReplaceId - assertThat(capturedEChanges.get(0), instanceOf(CreateEObject.class)); - assertThat(capturedEChanges.get(1), instanceOf(ReplaceSingleValuedEReference.class)); - assertThat(capturedEChanges.get(2), instanceOf(ReplaceSingleValuedEAttribute.class)); - } - - @Test - @DisplayName("twice") - public void twice() { - NonRoot firstNonRoot = aet.NonRoot(); - firstNonRoot.setId("first"); - root.setSingleValuedContainmentEReference(firstNonRoot); - view.commitChanges(); - - ResourceSet resolveInResourceSet = new ResourceSetImpl(); - resolveInResourceSet.createResource(root.eResource().getURI()); - resolveInResourceSet.getResource(root.eResource().getURI(), false).getContents().add(EcoreUtil.copy(root)); - - reset(mockChangeableViewSource, mockViewType); - NonRoot secondNonRoot = aet.NonRoot(); - secondNonRoot.setId("second"); - root.setSingleValuedContainmentEReference(secondNonRoot); - ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeRecordingView.class); - ArgumentCaptor> changeArgument = ArgumentCaptor - .forClass(VitruviusChange.class); - view.commitChanges(); - verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); - - VitruviusChange resolvedChange = VitruviusChangeResolver.forHierarchicalIds(resolveInResourceSet) - .resolveAndApply(changeArgument.getValue()); - List> capturedEChanges = resolvedChange.getEChanges(); - assertThat(capturedEChanges.size(), is(4)); // Create, Insert, ReplaceValue, Delete - assertThat(capturedEChanges.get(0), instanceOf(CreateEObject.class)); - assertThat(capturedEChanges.get(1), instanceOf(ReplaceSingleValuedEReference.class)); - ReplaceSingleValuedEAttribute replaceIdChange = AttributeFactory.eINSTANCE - .createReplaceSingleValuedEAttribute(); - replaceIdChange.setAffectedElement(secondNonRoot); - replaceIdChange.setAffectedFeature(AllElementTypesPackage.eINSTANCE.getIdentified_Id()); - replaceIdChange.setNewValue("second"); - assertThat(capturedEChanges.get(2), equalsDeeply(replaceIdChange, - ignoringFeatures(AttributePackage.eINSTANCE.getSubtractiveAttributeEChange_OldValue()))); - assertThat(capturedEChanges.get(3), instanceOf(DeleteEObject.class)); - } - - @Test - @DisplayName("without changes") - public void withoutChanges() { - ArgumentCaptor viewArgument = ArgumentCaptor.forClass(ChangeRecordingView.class); - ArgumentCaptor> changeArgument = ArgumentCaptor - .forClass(VitruviusChange.class); - view.commitChanges(); - verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); - assertThat(viewArgument.getValue(), is(view)); - assertThat(changeArgument.getValue().getEChanges(), not(hasItem(anything()))); - } - } - - @Nested - @DisplayName("close") - public class Close { - @Test - @DisplayName("and is closed afterwards") - public void isClosed() throws Exception { - ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)); - view.close(); - assertThat("view should be closed", view.isClosed()); - } - - @Test - @DisplayName("can be called multiple times") - public void callMultipleTimes() throws Exception { - ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)); - view.close(); - view.close(); - assertThat("view should be closed", view.isClosed()); - } - - @Test - @DisplayName("and does not allow further operations") - public void noOperations() throws Exception { - ChangeRecordingView view = new ChangeRecordingView( - new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)); - view.close(); - assertThrows(IllegalStateException.class, () -> view.getRootObjects()); - assertThrows(IllegalStateException.class, () -> view.getRootObjects(Root.class)); - assertThrows(IllegalStateException.class, () -> view.update()); - assertThrows(IllegalStateException.class, () -> view.commitChanges()); - assertThrows(IllegalStateException.class, () -> view.registerRoot(null, null)); - assertThrows(IllegalStateException.class, () -> view.moveRoot(null, null)); - } - } -} diff --git a/views/src/main/java/tools/vitruv/framework/views/impl/IdentityMappingViewType.java b/views/src/main/java/tools/vitruv/framework/views/impl/IdentityMappingViewType.java new file mode 100644 index 0000000000..f8d7c7ed17 --- /dev/null +++ b/views/src/main/java/tools/vitruv/framework/views/impl/IdentityMappingViewType.java @@ -0,0 +1,89 @@ +package tools.vitruv.framework.views.impl; + +import static com.google.common.base.Preconditions.checkArgument; +import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Stream; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; + +import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceCopier; +import tools.vitruv.change.atomic.hid.HierarchicalId; +import tools.vitruv.change.atomic.uuid.Uuid; +import tools.vitruv.change.atomic.uuid.UuidResolver; +import tools.vitruv.change.composite.description.VitruviusChange; +import tools.vitruv.change.composite.description.VitruviusChangeResolver; +import tools.vitruv.framework.views.ChangeableViewSource; +import tools.vitruv.framework.views.View; +import tools.vitruv.framework.views.ViewSelection; +import tools.vitruv.framework.views.ViewSource; +import tools.vitruv.framework.views.selectors.DirectViewElementSelector; + +/** + * A view type that allows creating views based on a basic element-wise + * selection mechanism and providing a one-to-one (identity) mapping of elements + * within the {@link ViewSource} to a created {@link View}. + */ +public class IdentityMappingViewType extends AbstractViewType, HierarchicalId> { + public IdentityMappingViewType(String name) { + super(name); + } + + @Override + public DirectViewElementSelector createSelector(ChangeableViewSource viewSource) { + return new DirectViewElementSelector<>(this, viewSource, + viewSource.getViewSourceModels().stream().map(resource -> { + if (!resource.getContents().isEmpty() && ResourceCopier.requiresFullCopy(resource)) { + // Some resources (like UML) can only be copied as a whole, so no option to select + // specific root elements + return Stream.of(resource.getContents().get(0)); + } + return resource.getContents().stream(); + }).flatMap(Function.identity()).filter(it -> it != null).toList()); + } + + @Override + public ModifiableView createView(DirectViewElementSelector selector) { + checkArgument(selector.getViewType() == this, "cannot create view with selector for different view type"); + return new BasicView(selector.getViewType(), selector.getViewSource(), selector.getSelection()); + } + + @Override + public void updateView(ModifiableView view) { + view.modifyContents((viewResourceSet) -> { + viewResourceSet.getResources().forEach(Resource::unload); + viewResourceSet.getResources().clear(); + createViewResources(view, viewResourceSet); + }); + } + + @Override + public void commitViewChanges(ModifiableView view, VitruviusChange viewChange) { + ResourceSet viewSourceCopyResourceSet = withGlobalFactories(new ResourceSetImpl()); + VitruviusChangeResolver idChangeResolver = VitruviusChangeResolver.forHierarchicalIds(viewSourceCopyResourceSet); + UuidResolver viewSourceCopyUuidResolver = UuidResolver.create(viewSourceCopyResourceSet); + VitruviusChangeResolver uuidChangeResolver = VitruviusChangeResolver.forUuids(viewSourceCopyUuidResolver); + Map mapping = createViewResources(view, viewSourceCopyResourceSet); + view.getViewSource().getUuidResolver().resolveResources(mapping, viewSourceCopyUuidResolver); + + VitruviusChange resolvedChange = idChangeResolver.resolveAndApply(viewChange); + VitruviusChange unresolvedChanges = uuidChangeResolver.assignIds(resolvedChange); + view.getViewSource().propagateChange(unresolvedChanges); + } + + private Map createViewResources(ModifiableView view, ResourceSet viewResourceSet) { + Collection viewSources = view.getViewSource().getViewSourceModels(); + ViewSelection selection = view.getSelection(); + List resourcesWithSelectedElements = viewSources.stream() + .filter(resource -> resource.getContents().stream().anyMatch(selection::isViewObjectSelected)).toList(); + return ResourceCopier.copyViewSourceResources(resourcesWithSelectedElements, viewResourceSet, + selection::isViewObjectSelected); + } +} diff --git a/views/src/main/java/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java b/views/src/main/java/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java deleted file mode 100644 index fd65a9f53a..0000000000 --- a/views/src/main/java/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java +++ /dev/null @@ -1,464 +0,0 @@ -package tools.vitruv.framework.views.impl; - -import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories; -import static java.util.Collections.emptySet; -import static org.hamcrest.CoreMatchers.anything; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static tools.vitruv.testutils.matchers.ModelMatchers.equalsDeeply; -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; - -import java.util.List; -import java.util.Set; -import java.util.stream.Stream; - -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.emf.ecore.resource.ResourceSet; -import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; -import org.eclipse.emf.ecore.util.EcoreUtil; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.ArgumentCaptor; - -import com.google.common.collect.FluentIterable; - -import allElementTypes.Root; -import tools.vitruv.change.atomic.EChange; -import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute; -import tools.vitruv.change.atomic.hid.HierarchicalId; -import tools.vitruv.change.atomic.uuid.Uuid; -import tools.vitruv.change.atomic.uuid.UuidResolver; -import tools.vitruv.change.composite.description.VitruviusChange; -import tools.vitruv.change.composite.description.VitruviusChangeFactory; -import tools.vitruv.change.composite.description.VitruviusChangeResolver; -import tools.vitruv.change.composite.recording.ChangeRecorder; -import tools.vitruv.framework.views.ChangeableViewSource; -import tools.vitruv.framework.views.View; -import tools.vitruv.framework.views.ViewSelection; -import tools.vitruv.framework.views.ViewType; -import tools.vitruv.framework.views.selectors.DirectViewElementSelector; -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging; - -@ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) -public class IdentityMappingViewTypeTest { - @Nested - @DisplayName("initialize") - class Initialize { - @Test - @DisplayName("with proper name") - public void withName() { - ViewType viewType = new IdentityMappingViewType("name"); - assertThat(viewType.getName(), is("name")); - } - - @Test - @DisplayName("with empty name") - public void withEmptyName() { - ViewType viewType = new IdentityMappingViewType(""); - assertThat(viewType.getName(), is("")); - } - - @Test - @DisplayName("with null name") - public void withNullName() { - assertThrows(IllegalArgumentException.class, () -> new IdentityMappingViewType(null)); - } - } - - @Nested - @DisplayName("create selector") - class CreateSelector { - private IdentityMappingViewType basicViewType; - - @BeforeEach - public void initializeViewType() { - this.basicViewType = new IdentityMappingViewType("name"); - } - - @Test - @DisplayName("for empty source") - public void forEmptySource() { - DirectViewElementSelector selector = basicViewType - .createSelector(mock(ChangeableViewSource.class)); - assertThat(selector.getSelectableElements(), is(emptySet())); - } - - @Test - @DisplayName("for source containing single element") - public void forSourceContainingElement() { - Resource resource = withGlobalFactories(new ResourceSetImpl()) - .createResource(URI.createURI("test:///test.aet")); - Root rootElement = aet.Root(); - resource.getContents().add(rootElement); - ChangeableViewSource viewSource = mock(ChangeableViewSource.class); - when(viewSource.getViewSourceModels()).thenReturn(Set.of(resource)); - DirectViewElementSelector selector = basicViewType.createSelector(viewSource); - assertThat(selector.getSelectableElements(), is(Set.of(rootElement))); - } - - @Test - @DisplayName("for source containing non-root elements") - public void forSourceContainingNonRootElements() { - Resource resource = withGlobalFactories(new ResourceSetImpl()) - .createResource(URI.createURI("test:///test.aet")); - Root rootElement = aet.Root(); - rootElement.setSingleValuedContainmentEReference(aet.NonRoot()); - resource.getContents().add(rootElement); - ChangeableViewSource viewSource = mock(ChangeableViewSource.class); - when(viewSource.getViewSourceModels()).thenReturn(Set.of(resource)); - DirectViewElementSelector selector = basicViewType.createSelector(viewSource); - assertThat(selector.getSelectableElements(), is(Set.of(rootElement))); - } - } - - @Nested - @DisplayName("create view") - public class CreateView { - private IdentityMappingViewType basicViewType; - private ResourceSet testResourceSet; - - @BeforeEach - public void initializeViewTypeAndResourceSet() { - this.basicViewType = new IdentityMappingViewType("name"); - this.testResourceSet = withGlobalFactories(new ResourceSetImpl()); - } - - @Test - @DisplayName("with empty source") - public void withNoElements() throws Exception { - ChangeableViewSource viewSource = mock(ChangeableViewSource.class); - DirectViewElementSelector selector = basicViewType.createSelector(viewSource); - try (View view = basicViewType.createView(selector)) { - assertThat(view.getRootObjects(), not(hasItem(anything()))); - } - } - - @Test - @DisplayName("with selector from other view type") - public void withSelectorFromOtherViewtype() { - ChangeableViewSource viewSource = mock(ChangeableViewSource.class); - DirectViewElementSelector selector = new IdentityMappingViewType("other") - .createSelector(viewSource); - assertThrows(IllegalArgumentException.class, () -> basicViewType.createView(selector)); - } - - @Test - @DisplayName("with no selected element") - public void withNoSelectedElement() throws Exception { - Resource resource = testResourceSet.createResource(URI.createURI("test://test.aet")); - Root rootElement = aet.Root(); - rootElement.setId("testid"); - resource.getContents().add(rootElement); - ChangeableViewSource viewSource = mock(ChangeableViewSource.class); - when(viewSource.getViewSourceModels()).thenReturn(Set.of(resource)); - DirectViewElementSelector selector = basicViewType.createSelector(viewSource); - try (View view = basicViewType.createView(selector)) { - assertThat(view.getRootObjects(), not(hasItem(anything()))); - } - } - - @Test - @DisplayName("with single selected element") - public void withSingleSelectedElement() throws Exception { - Resource resource = testResourceSet.createResource(URI.createURI("test://test.aet")); - Root rootElement = aet.Root(); - rootElement.setId("testid"); - resource.getContents().add(rootElement); - ChangeableViewSource viewSource = mock(ChangeableViewSource.class); - when(viewSource.getViewSourceModels()).thenReturn(Set.of(resource)); - DirectViewElementSelector selector = basicViewType.createSelector(viewSource); - selector.setSelected(rootElement, true); - try (View view = basicViewType.createView(selector)) { - assertThat(view.getRootObjects().size(), is(1)); - assertThat(view.getRootObjects(), hasItem(equalsDeeply(rootElement))); - } - } - - @Test - @DisplayName("with one of two elements selected") - public void withOneOfTwoElementsSelected() throws Exception { - Resource firstResource = testResourceSet.createResource(URI.createURI("test://test.aet")); - Resource secondResource = testResourceSet.createResource(URI.createURI("test://test2.aet")); - Root firstRootElement = aet.Root(); - firstRootElement.setId("firstElementId"); - Root secondRootElement = aet.Root(); - secondRootElement.setId("secondElementId"); - firstResource.getContents().add(firstRootElement); - secondResource.getContents().add(secondRootElement); - ChangeableViewSource viewSource = mock(ChangeableViewSource.class); - when(viewSource.getViewSourceModels()).thenReturn(Set.of(firstResource, secondResource)); - DirectViewElementSelector selector = basicViewType.createSelector(viewSource); - selector.setSelected(firstRootElement, true); - try (View view = basicViewType.createView(selector)) { - assertThat(view.getRootObjects().size(), is(1)); - assertThat(view.getRootObjects(), hasItem(equalsDeeply(firstRootElement))); - } - } - - @Test - @DisplayName("with both of two elements selected") - public void withBothOfTwoElementsSelected() throws Exception { - Resource firstResource = testResourceSet.createResource(URI.createURI("test://test.aet")); - Resource secondResource = testResourceSet.createResource(URI.createURI("test://test2.aet")); - Root firstRootElement = aet.Root(); - firstRootElement.setId("firstElementId"); - Root secondRootElement = aet.Root(); - secondRootElement.setId("secondElementId"); - firstResource.getContents().add(firstRootElement); - secondResource.getContents().add(secondRootElement); - ChangeableViewSource viewSource = mock(ChangeableViewSource.class); - when(viewSource.getViewSourceModels()).thenReturn(Set.of(firstResource, secondResource)); - DirectViewElementSelector selector = basicViewType.createSelector(viewSource); - selector.setSelected(firstRootElement, true); - selector.setSelected(secondRootElement, true); - try (View view = basicViewType.createView(selector)) { - assertThat(view.getRootObjects().size(), is(2)); - assertThat(view.getRootObjects(), hasItem(equalsDeeply(firstRootElement))); - assertThat(view.getRootObjects(), hasItem(equalsDeeply(secondRootElement))); - } - } - - /** - * This test validates that if there are two resources with the root of one of - * them being contained in the other resource (like a Java compilation unit - * being root of a resource but also contained in its packages represented in a - * further resource), the root element that is also contained in the other - * resource is not duplicated but copied properly. - * - * @throws Exception - */ - @Test - @DisplayName("for two resources with containment in between") - public void forTwoResourcesWithContainmentInBetween() throws Exception { - Resource firstResource = testResourceSet.createResource(URI.createURI("test://test.aet")); - Resource secondResource = testResourceSet.createResource(URI.createURI("test://test2.aet")); - Root firstRootElement = aet.Root(); - firstRootElement.setId("firstElementId"); - Root secondRootElement = aet.Root(); - secondRootElement.setId("secondElementId"); - firstRootElement.setRecursiveRoot(secondRootElement); - firstResource.getContents().add(firstRootElement); - secondResource.getContents().add(secondRootElement); - ChangeableViewSource viewSource = mock(ChangeableViewSource.class); - when(viewSource.getViewSourceModels()).thenReturn(Set.of(firstResource, secondResource)); - DirectViewElementSelector selector = basicViewType.createSelector(viewSource); - selector.setSelected(firstRootElement, true); - selector.setSelected(secondRootElement, true); - try (View view = basicViewType.createView(selector)) { - assertThat(view.getRootObjects().size(), is(2)); - assertThat(view.getRootObjects(), hasItem(equalsDeeply(firstRootElement))); - assertThat(view.getRootObjects(), hasItem(equalsDeeply(secondRootElement))); - Root rootWithContainment = FluentIterable.from(view.getRootObjects(Root.class)) - .filter((root) -> root.getRecursiveRoot() != null).first().get(); - Root rootWithoutContainment = FluentIterable.from(view.getRootObjects(Root.class)) - .filter((root) -> root.getRecursiveRoot() == null).first().get(); - assertThat(rootWithContainment.getRecursiveRoot(), is(rootWithoutContainment)); - } - } - - } - - @Nested - @DisplayName("update view") - class UpdateView { - private IdentityMappingViewType basicViewType; - private ResourceSet testResourceSet; - private ChangeableViewSource viewSource; - - @BeforeEach - public void initializeViewTypeAndResourceSetAndViewSource() { - this.basicViewType = new IdentityMappingViewType("name"); - this.testResourceSet = withGlobalFactories(new ResourceSetImpl()); - this.viewSource = mock(ChangeableViewSource.class); - when(viewSource.getViewSourceModels()).thenReturn(testResourceSet.getResources()); - } - - private Root createResourceWithSingleRoot(URI uri) { - Resource resource = testResourceSet.createResource(uri); - Root rootElement = aet.Root(); - rootElement.setId("testid"); - resource.getContents().add(rootElement); - return rootElement; - } - - @Test - @DisplayName("adding a non-root element") - public void addingANonRootElement() throws Exception { - Root root = createResourceWithSingleRoot(URI.createURI("test://test.aet")); - DirectViewElementSelector selector = basicViewType.createSelector(viewSource); - selector.getSelectableElements().forEach((element) -> selector.setSelected(element, true)); - try (ModifiableView view = basicViewType.createView(selector)) { - root.setSingleValuedContainmentEReference(aet.NonRoot()); - assertThat((view.getRootObjects(Root.class).iterator().next()).getSingleValuedContainmentEReference(), - equalTo(null)); - basicViewType.updateView(view); - assertThat(view.getRootObjects().size(), is(1)); - assertThat((view.getRootObjects(Root.class).iterator().next()).getSingleValuedContainmentEReference(), - is(anything())); - } - } - - @Test - @DisplayName("adding a root element") - public void addingARootElement() throws Exception { - Root root = createResourceWithSingleRoot(URI.createURI("test://test.aet")); - DirectViewElementSelector selector = basicViewType.createSelector(viewSource); - selector.getSelectableElements().forEach((element) -> selector.setSelected(element, true)); - try (ModifiableView view = basicViewType.createView(selector)) { - root.setId("secondId"); - Root secondRoot = createResourceWithSingleRoot(URI.createURI("test://test2.aet")); - basicViewType.updateView(view); - assertThat(view.getRootObjects().size(), is(1)); - assertThat(view.getRootObjects(), not(hasItem(equalsDeeply(secondRoot)))); - } - } - - @Test - @DisplayName("removing a selected root element") - public void removingSelectedRoot() throws Exception { - Root root = createResourceWithSingleRoot(URI.createURI("test://test.aet")); - DirectViewElementSelector selector = basicViewType.createSelector(viewSource); - selector.getSelectableElements().forEach((element) -> selector.setSelected(element, true)); - try (ModifiableView view = basicViewType.createView(selector)) { - EcoreUtil.delete(root); - assertThat(view.getRootObjects().size(), is(1)); - basicViewType.updateView(view); - assertThat(view.getRootObjects().size(), is(0)); - } - } - - @Test - @DisplayName("removing an unselected root element") - public void removingUnselectedRoot() throws Exception { - Root firstRoot = createResourceWithSingleRoot(URI.createURI("test://test.aet")); - Root secondRoot = createResourceWithSingleRoot(URI.createURI("test://test2.aet")); - DirectViewElementSelector selector = basicViewType.createSelector(viewSource); - selector.setSelected(firstRoot, true); - try (ModifiableView view = basicViewType.createView(selector)) { - EcoreUtil.delete(secondRoot); - assertThat(view.getRootObjects().size(), is(1)); - basicViewType.updateView(view); - assertThat(view.getRootObjects().size(), is(1)); - assertThat(view.getRootObjects(), hasItem(equalsDeeply(firstRoot))); - } - } - } - - @Nested - @DisplayName("commit view changes") - class CommitViewChanges { - private IdentityMappingViewType basicViewType; - private ResourceSet viewSourceResourceSet; - private ResourceSet viewResourceSet; - private ModifiableView view; - private ViewSelection viewSelection; - private ChangeableViewSource viewSource; - private UuidResolver uuidResolver; - - @BeforeEach - void initializeViewTypeAndResourceSetAndViewSource() { - this.basicViewType = new IdentityMappingViewType("name"); - this.viewSourceResourceSet = withGlobalFactories(new ResourceSetImpl()); - this.viewResourceSet = withGlobalFactories(new ResourceSetImpl()); - this.view = mock(ModifiableView.class); - this.viewSource = mock(ChangeableViewSource.class); - this.viewSelection = mock(ViewSelection.class); - this.uuidResolver = UuidResolver.create(viewSourceResourceSet); - when(view.getViewSource()).thenReturn(viewSource); - when(view.getSelection()).thenReturn(viewSelection); - when(viewSource.getViewSourceModels()).thenReturn(viewSourceResourceSet.getResources()); - when(viewSource.getUuidResolver()).thenReturn(uuidResolver); - when(viewSelection.isViewObjectSelected(any(EObject.class))).thenReturn(true); - } - - private Root createResourceWithSingleRoot(URI uri) { - Resource resource = viewSourceResourceSet.createResource(uri); - Root rootElement = aet.Root(); - uuidResolver.registerEObject(rootElement); - rootElement.setId("testid"); - resource.getContents().add(rootElement); - - Resource viewResource = viewResourceSet.createResource(uri); - Root viewRootElement = EcoreUtil.copy(rootElement); - viewResource.getContents().add(viewRootElement); - return viewRootElement; - } - - @Test - @DisplayName("with null changes") - void withNull() { - assertThrows(NullPointerException.class, () -> basicViewType.commitViewChanges(view, null)); - } - - @Test - @DisplayName("with null view") - void withNullView() { - VitruviusChange someChange = VitruviusChangeFactory.getInstance() - .createTransactionalChange(Set.of()); - assertThrows(NullPointerException.class, () -> basicViewType.commitViewChanges(null, someChange)); - } - - @ParameterizedTest - @MethodSource("testEmptyChanges") - @DisplayName("with empty changes") - void withEmptyChanges(VitruviusChange change) { - ArgumentCaptor> changeArgument = ArgumentCaptor.forClass(VitruviusChange.class); - basicViewType.commitViewChanges(view, change); - verify(viewSource).propagateChange(changeArgument.capture()); - assertEquals(changeArgument.getValue(), change); - } - - private static Stream> testEmptyChanges() { - VitruviusChangeFactory factory = VitruviusChangeFactory.getInstance(); - return Stream.of(factory.createTransactionalChange(Set.of()), factory.createCompositeChange(Set.of())); - } - - @Test - @DisplayName("with non-empty change") - void withNonEmptyChange() { - Root root = createResourceWithSingleRoot(URI.createURI("test://test.aet")); - try (ChangeRecorder changeRecorder = new ChangeRecorder(viewResourceSet)) { - changeRecorder.addToRecording(root); - changeRecorder.beginRecording(); - root.setId("testid2"); - changeRecorder.endRecording(); - VitruviusChange change = changeRecorder.getChange(); - VitruviusChange idAssignedChange = assignIds(change); - - ArgumentCaptor> changeArgument = ArgumentCaptor.forClass(VitruviusChange.class); - basicViewType.commitViewChanges(view, idAssignedChange); - verify(viewSource).propagateChange(changeArgument.capture()); - List> eChanges = changeArgument.getValue().getEChanges(); - assertThat(eChanges.size(), is(1)); - assertThat(eChanges.get(0), instanceOf(ReplaceSingleValuedEAttribute.class)); - var attributeChange = (ReplaceSingleValuedEAttribute) eChanges.get(0); - assertThat(attributeChange.getOldValue(), is("testid")); - assertThat(attributeChange.getNewValue(), is("testid2")); - } - } - - private VitruviusChange assignIds(VitruviusChange change) { - VitruviusChangeResolver idChangeResolver = VitruviusChangeResolver - .forHierarchicalIds(viewResourceSet); - return idChangeResolver.assignIds(change); - } - } -} diff --git a/views/src/main/java/tools/vitruv/framework/views/impl/ViewCreatingViewType.java b/views/src/main/java/tools/vitruv/framework/views/impl/ViewCreatingViewType.java new file mode 100644 index 0000000000..b11791df43 --- /dev/null +++ b/views/src/main/java/tools/vitruv/framework/views/impl/ViewCreatingViewType.java @@ -0,0 +1,45 @@ +package tools.vitruv.framework.views.impl; + +import tools.vitruv.change.composite.description.VitruviusChange; +import tools.vitruv.framework.views.ChangeableViewSource; +import tools.vitruv.framework.views.ViewSelector; +import tools.vitruv.framework.views.ViewType; + +/** + * A specific view type that is able to create and update views. This is not its + * public interface but only for internal usage by views and their selectors. + * + * @param the type of view selector this view type uses. + * @param the type of Id the changes to commit must have. + */ +public interface ViewCreatingViewType extends ViewType { + /** + * Creates a view for the given {@link ViewSelector}. The selector must have + * been created by calling the {@link #createSelector} method of the same + * {@link ViewCreatingViewType}. + * + * @param selector the {@link ViewSelector} to create a view for + * @return a {@link ModifiableView} with elements according to the selector. + */ + ModifiableView createView(S selector); + + /** + * Updates a view that is created from this view type to ensure it is consistent + * with the virtual model. + * + * @param view is the view to be updated. + */ + void updateView(ModifiableView view); + + /** + * Commits the changes made to the view and its containing elements to the + * underlying {@link ChangeableViewSource}. Since view elements do not + * necessarily correspond to elements of the underlying view source, the view + * type is responsible for transforming the given {@link VitruviusChange} such + * that the underlying view source can process it. + * + * @param view is the modified view. + * @param viewChange are the changes performed to the view. + */ + void commitViewChanges(ModifiableView view, VitruviusChange viewChange); +} diff --git a/views/src/main/java/tools/vitruv/framework/views/selection/AbstractViewSelection.java b/views/src/main/java/tools/vitruv/framework/views/selection/AbstractViewSelection.java new file mode 100644 index 0000000000..b38480ffd3 --- /dev/null +++ b/views/src/main/java/tools/vitruv/framework/views/selection/AbstractViewSelection.java @@ -0,0 +1,55 @@ +package tools.vitruv.framework.views.selection; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.emf.ecore.EObject; + +import tools.vitruv.framework.views.ModifiableViewSelection; + +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Preconditions.checkNotNull; + +public abstract class AbstractViewSelection implements ModifiableViewSelection { + final Map elementsSelection = new HashMap<>(); + + public AbstractViewSelection(Collection selectableElements) { + selectableElements.forEach(object -> this.elementsSelection + .put(checkNotNull(object, "element to select must not be null"), false)); + } + + public AbstractViewSelection(ModifiableViewSelection sourceViewSelection) { + this(sourceViewSelection.getSelectableElements()); + for (EObject selectableElement : sourceViewSelection.getSelectableElements()) { + setSelected(selectableElement, sourceViewSelection.isSelected(selectableElement)); + } + } + + private void checkIsSelectable(EObject eObject) { + checkState(isSelectable(eObject), "given object %s must be contained in the selector elements", eObject); + } + + @Override + public boolean isSelected(EObject eObject) { + return elementsSelection.getOrDefault(eObject, false); + } + + @Override + public boolean isSelectable(EObject eObject) { + return elementsSelection.keySet().contains(eObject); + } + + @Override + public Collection getSelectableElements() { + return Collections.unmodifiableSet(elementsSelection.keySet()); + } + + @Override + public void setSelected(EObject eObject, boolean selected) { + checkIsSelectable(eObject); + elementsSelection.put(eObject, selected); + } + +} diff --git a/views/src/main/java/tools/vitruv/framework/views/selection/ElementViewSelection.java b/views/src/main/java/tools/vitruv/framework/views/selection/ElementViewSelection.java new file mode 100644 index 0000000000..c8d00630ec --- /dev/null +++ b/views/src/main/java/tools/vitruv/framework/views/selection/ElementViewSelection.java @@ -0,0 +1,24 @@ +package tools.vitruv.framework.views.selection; + +import java.util.Collection; + +import org.eclipse.emf.ecore.EObject; + +import tools.vitruv.framework.views.ModifiableViewSelection; + +public class ElementViewSelection extends AbstractViewSelection { + + public ElementViewSelection(Collection selectableElements) { + super(selectableElements); + } + + public ElementViewSelection(ModifiableViewSelection sourceViewSelection) { + super(sourceViewSelection); + } + + @Override + public boolean isViewObjectSelected(EObject eObject) { + return isSelected(eObject); + } + +} diff --git a/views/src/main/java/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java b/views/src/main/java/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java deleted file mode 100644 index d6c4e06fd1..0000000000 --- a/views/src/main/java/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java +++ /dev/null @@ -1,279 +0,0 @@ -package tools.vitruv.framework.views.selection; - -import static java.util.Collections.emptySet; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import org.eclipse.emf.ecore.EObject; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import allElementTypes.Root; -import tools.vitruv.framework.views.ModifiableViewSelection; -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging; - -@ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) -public class ElementViewSelectionTest { - @Nested - @DisplayName("inialize") - class Initialize { - @Test - @DisplayName("empty") - public void empty() { - ModifiableViewSelection selection = new ElementViewSelection(emptySet()); - assertThat(selection.getSelectableElements(), is(emptySet())); - } - - @Test - @DisplayName("with single element") - public void withSingleElement() { - Root root = aet.Root(); - ModifiableViewSelection selection = new ElementViewSelection(Set.of(root)); - assertThat(selection.getSelectableElements(), is(Set.of(root))); - } - - @Test - @DisplayName("with multiple elements") - public void withMultipleElements() { - Root firstRoot = aet.Root(); - Root secondRoot = aet.Root(); - ModifiableViewSelection selection = new ElementViewSelection(Set.of(firstRoot, secondRoot)); - assertThat(selection.getSelectableElements(), is(Set.of(firstRoot, secondRoot))); - } - } - - @Nested - @DisplayName("selectable") - class Selectable { - ModifiableViewSelection selection; - List selectableElements; - - @BeforeEach - public void setupSelectionWithTwoElements() { - selectableElements = new ArrayList<>(); - selectableElements.add(aet.Root()); - selectableElements.add(aet.Root()); - selection = new ElementViewSelection(selectableElements); - } - - @Test - @DisplayName("element in selection") - public void elementInSelection() { - assertThat("all added elements must be selectable", - selectableElements.stream().allMatch(element -> selection.isSelectable(element))); - } - - @Test - @DisplayName("element not in selection") - public void elementNotInSelection() { - assertThat("elements not added to selection must not be selectable", !selection.isSelectable(aet.Root())); - } - - @Test - @DisplayName("null element") - public void nullElement() { - assertThat("null elements must not be selectable", !selection.isSelectable(null)); - } - - } - - @Nested - @DisplayName("select") - class Select { - ModifiableViewSelection selection; - List selectableElements; - - @BeforeEach - public void setupSelectionWithTwoElements() { - selectableElements = new ArrayList<>(); - selectableElements.add(aet.Root()); - selectableElements.add(aet.Root()); - selection = new ElementViewSelection(selectableElements); - } - - @Test - @DisplayName("no element") - public void noElement() { - assertThat("unselected elements must not be selected by default", - !selection.isSelected(selectableElements.get(0))); - assertThat("unselected elements must not be selected by default", - !selection.isSelected(selectableElements.get(1))); - } - - @Test - @DisplayName("first element") - public void firstElement() { - selection.setSelected(selectableElements.get(0), true); - assertThat("element must be selected after selection", selection.isSelected(selectableElements.get(0))); - assertThat("unselected elements must not be selected by default", - !selection.isSelected(selectableElements.get(1))); - } - - @Test - @DisplayName("second element") - public void secondElement() { - selection.setSelected(selectableElements.get(1), true); - assertThat("unselected elements must not be selected by default", - !selection.isSelected(selectableElements.get(0))); - assertThat("element must be selected after selection", selection.isSelected(selectableElements.get(1))); - } - - @Test - @DisplayName("all elements") - public void allElements() { - selection.setSelected(selectableElements.get(0), true); - selection.setSelected(selectableElements.get(1), true); - assertThat("element must be selected after selection", selection.isSelected(selectableElements.get(0))); - assertThat("element must be selected after selection", selection.isSelected(selectableElements.get(1))); - } - - @Test - @DisplayName("element that is not selectable") - public void unselectableElement() { - assertThrows(IllegalStateException.class, () -> selection.setSelected(aet.Root(), true)); - } - - } - - @Nested - @DisplayName("unselect") - class Unselect { - ModifiableViewSelection selection; - List selectableElements; - - @BeforeEach - public void setupSelectionWithTwoSelectedElements() { - selectableElements = new ArrayList<>(); - selectableElements.add(aet.Root()); - selectableElements.add(aet.Root()); - selection = new ElementViewSelection(selectableElements); - selectableElements.forEach(element -> selection.setSelected(element, true)); - } - - @Test - @DisplayName("first element") - public void firstElement() { - selection.setSelected(selectableElements.get(0), false); - assertThat("element must be unselected after deselection", - !selection.isSelected(selectableElements.get(0))); - assertThat("element must be selected without deselection", selection.isSelected(selectableElements.get(1))); - } - - @Test - @DisplayName("second element") - public void secondElement() { - selection.setSelected(selectableElements.get(1), false); - assertThat("element must be selected without deselection", selection.isSelected(selectableElements.get(0))); - assertThat("element must be unselected after deselection", - !selection.isSelected(selectableElements.get(1))); - } - - @Test - @DisplayName("all elements") - public void allElements() { - selection.setSelected(selectableElements.get(0), false); - selection.setSelected(selectableElements.get(1), false); - assertThat("element must be unselected after deselection", - !selection.isSelected(selectableElements.get(0))); - assertThat("element must be unselected after deselection", - !selection.isSelected(selectableElements.get(1))); - } - - @Test - @DisplayName("element that is not selectable") - public void unselectableElement() { - assertThrows(IllegalStateException.class, () -> selection.setSelected(aet.Root(), false)); - } - - } - - @Nested - @DisplayName("validate view element selection") - class ViewElementSelection { - ModifiableViewSelection selection; - List selectableElements; - - @BeforeEach - public void setupSelectionWithFirstOfTwoElementsSelected() { - selectableElements = new ArrayList<>(); - selectableElements.add(aet.Root()); - selectableElements.add(aet.Root()); - selection = new ElementViewSelection(selectableElements); - selection.setSelected(selectableElements.get(0), true); - } - - @Test - @DisplayName("matching selected element") - public void matchingSelectedElement() { - assertThat("view element must be validated as selected after selecting it", - selection.isViewObjectSelected(selectableElements.get(0))); - } - - @Test - @DisplayName("matching unselected element") - public void matchingUnselectedElement() { - assertThat("view element must be validated as unselected when not selecting it", - !selection.isViewObjectSelected(selectableElements.get(1))); - } - - @Test - @DisplayName("matching no element") - public void matchingNoElement() { - assertThat("view element must be validated as unselected when it cannot be selected", - !selection.isViewObjectSelected(aet.Root())); - } - - } - - @Nested - @DisplayName("copy") - class Copy { - List selectableElements; - - @BeforeEach - public void setupSelectionWithFirstOfTwoElementsSelected() { - selectableElements = new ArrayList<>(); - selectableElements.add(aet.Root()); - selectableElements.add(aet.Root()); - } - - @Test - @DisplayName("empty") - public void empty() { - ModifiableViewSelection originalSelection = new ElementViewSelection(emptySet()); - ModifiableViewSelection copy = new ElementViewSelection(originalSelection); - assertThat(copy.getSelectableElements(), is(emptySet())); - } - - @Test - @DisplayName("with single element") - public void withSingleElement() { - Root root = aet.Root(); - ModifiableViewSelection originalSelection = new ElementViewSelection(Set.of(root)); - ModifiableViewSelection copy = new ElementViewSelection(originalSelection); - assertThat(copy.getSelectableElements(), is(Set.of(root))); - } - - @Test - @DisplayName("with multiple elements") - public void withMultipleElements() { - Root firstRoot = aet.Root(); - Root secondRoot = aet.Root(); - ModifiableViewSelection originalSelection = new ElementViewSelection(Set.of(firstRoot, secondRoot)); - ModifiableViewSelection copy = new ElementViewSelection(originalSelection); - assertThat(copy.getSelectableElements(), is(Set.of(firstRoot, secondRoot))); - } - - } - -} diff --git a/views/src/main/java/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java b/views/src/main/java/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java deleted file mode 100644 index a099cef99d..0000000000 --- a/views/src/main/java/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java +++ /dev/null @@ -1,165 +0,0 @@ -package tools.vitruv.framework.views.selectors; - -import static java.util.Collections.emptySet; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import org.eclipse.emf.ecore.EObject; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import allElementTypes.Root; -import tools.vitruv.change.atomic.hid.HierarchicalId; -import tools.vitruv.framework.views.ChangeableViewSource; -import tools.vitruv.framework.views.ViewSelector; -import tools.vitruv.framework.views.impl.ModifiableView; -import tools.vitruv.framework.views.impl.ViewCreatingViewType; -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging; - -@ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) -public class DirectViewElementSelectorTest { - @Mock - ViewCreatingViewType, HierarchicalId> mockViewType; - - @Mock - ChangeableViewSource mockViewSource; - - @BeforeEach - public void initializeMocks() { - MockitoAnnotations.openMocks(this); - } - - @Nested - @DisplayName("initalize") - class Initialize { - @Nested - @DisplayName("with null elements") - class WithNullElements { - @Test - @DisplayName("with null view type") - public void nullViewType() { - assertThrows(IllegalArgumentException.class, - () -> new DirectViewElementSelector<>(null, mockViewSource, emptySet())); - } - - @Test - @DisplayName("with null view source") - public void nullViewSource() { - assertThrows(IllegalArgumentException.class, - () -> new DirectViewElementSelector<>(mockViewType, null, emptySet())); - } - - @Test - @DisplayName("with null selectable elements") - public void nullElements() { - assertThrows(IllegalArgumentException.class, - () -> new DirectViewElementSelector<>(mockViewType, mockViewSource, null)); - } - } - - @Test - @DisplayName("with no selectable elements") - public void empty() { - ViewSelector selector = new DirectViewElementSelector<>(mockViewType, mockViewSource, emptySet()); - assertThat(selector.getSelectableElements(), is(emptySet())); - assertThat("BasicViewElementSelectors must always be valid", selector.isValid()); - } - - @Test - @DisplayName("with single selectable element") - public void withSingleSelectableElement() { - Root root = aet.Root(); - ViewSelector selector = new DirectViewElementSelector<>(mockViewType, mockViewSource, Set.of(root)); - assertThat(selector.getSelectableElements(), is(Set.of(root))); - assertThat("BasicViewElementSelectors must always be valid", selector.isValid()); - } - - @Test - @DisplayName("with multiple selectable elements") - public void withMultipleSelectableElements() { - Root firstRoot = aet.Root(); - Root secondRoot = aet.Root(); - ViewSelector selector = new DirectViewElementSelector<>(mockViewType, mockViewSource, - Set.of(firstRoot, secondRoot)); - assertThat(selector.getSelectableElements(), is(Set.of(firstRoot, secondRoot))); - assertThat("BasicViewElementSelectors must always be valid", selector.isValid()); - } - - } - - @Nested - @DisplayName("provides view selection") - class ViewSelection { - ViewSelector selector; - List selectableElements; - - @BeforeEach - public void setupSelectionWithFirstOfTwoElementsSelected() { - selectableElements = new ArrayList<>(); - selectableElements.add(aet.Root()); - selectableElements.add(aet.Root()); - selector = new DirectViewElementSelector<>(mockViewType, mockViewSource, selectableElements); - selector.setSelected(selectableElements.get(0), true); - assertThat("BasicViewElementSelectors must always be valid", selector.isValid()); - } - - @Test - @DisplayName("matching selected element") - public void matchingSelectedElement() { - assertThat("view element must be validated as selected after selecting it", - selector.getSelection().isViewObjectSelected(selectableElements.get(0))); - } - - @Test - @DisplayName("matching unselected element") - public void matchingUnselectedElement() { - assertThat("view element must be validated as unselected when not selecting it", - !selector.getSelection().isViewObjectSelected(selectableElements.get(1))); - } - - @Test - @DisplayName("matching no element") - public void matchingNoElement() { - assertThat("view element must be validated as unselected when it cannot be selected", - !selector.getSelection().isViewObjectSelected(aet.Root())); - } - } - - @Nested - @DisplayName("returns") - class Returns { - @Test - @DisplayName("view created from view type") - public void createView() { - DirectViewElementSelector selector = new DirectViewElementSelector<>(mockViewType, - mockViewSource, emptySet()); - ModifiableView view = mock(ModifiableView.class); - when(mockViewType.createView(selector)).thenReturn(view); - assertThat(selector.createView(), is(view)); - } - - @Test - @DisplayName("view source") - public void getViewSource() { - DirectViewElementSelector selector = new DirectViewElementSelector<>(mockViewType, - mockViewSource, emptySet()); - assertThat(selector.getViewSource(), is(mockViewSource)); - } - - } - -} diff --git a/views/src/main/java/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java b/views/src/main/java/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java deleted file mode 100644 index b5ea0cf865..0000000000 --- a/views/src/main/java/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java +++ /dev/null @@ -1,111 +0,0 @@ -package tools.vitruv.framework.views.util; - -import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.common.util.URIUtil.createFileURI; -import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static tools.vitruv.testutils.metamodels.UmlMockupCreators.uml; - -import java.io.File; -import java.nio.file.Path; -import java.util.List; -import java.util.Map; - -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.emf.ecore.resource.ResourceSet; -import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; -import org.eclipse.emf.ecore.xmi.XMLResource; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import edu.kit.ipd.sdq.commons.util.java.Pair; -import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceCopier; -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging; -import tools.vitruv.testutils.TestProject; -import tools.vitruv.testutils.TestProjectManager; -import uml_mockup.Identified; -import uml_mockup.UClass; -import uml_mockup.UPackage; - -@ExtendWith({ TestProjectManager.class, TestLogging.class, RegisterMetamodelsInStandalone.class }) -public class XmiIdEdgeCaseTest { - private ResourceSet resourceSet; - private Path testProjectFolder; - - @BeforeEach - void setup(@TestProject Path testProjectFolder) { - this.testProjectFolder = testProjectFolder; - resourceSet = withGlobalFactories(new ResourceSetImpl()); - } - - @Test - public void testSingleResourceCopy() { - Pair> umlResourcePair = createPopulatedUmlResourceAndIdMapping("my"); - ResourceSet copyResourceSet = new ResourceSetImpl(); - XMLResource copiedModel = (XMLResource) ResourceCopier.copyViewResource(umlResourcePair.get0(), - copyResourceSet); - validateIds(copiedModel, umlResourcePair.get1()); - } - - @Test - public void testMultiResourceCopy() { - Pair> umlResourcePair = createPopulatedUmlResourceAndIdMapping("my1"); - Pair> umlResourcePair2 = createPopulatedUmlResourceAndIdMapping("my2"); - Pair> umlResourcePair3 = createPopulatedUmlResourceAndIdMapping("my3"); - - ResourceSet copyResourceSet = new ResourceSetImpl(); - Map copiedModels = ResourceCopier.copyViewResources( - List.of(umlResourcePair.get0(), umlResourcePair2.get0(), umlResourcePair3.get0()), copyResourceSet); - XMLResource copiedModel = (XMLResource) copiedModels.get(umlResourcePair.get0()); - XMLResource copiedModel2 = (XMLResource) copiedModels.get(umlResourcePair2.get0()); - XMLResource copiedModel3 = (XMLResource) copiedModels.get(umlResourcePair3.get0()); - assertNotNull(copiedModel, "copy for uml model is missing"); - assertNotNull(copiedModel2, "copy for uml model 2 is missing"); - assertNotNull(copiedModel3, "copy for uml model 3 is missing"); - validateIds(copiedModel, umlResourcePair.get1()); - validateIds(copiedModel2, umlResourcePair2.get1()); - validateIds(copiedModel3, umlResourcePair3.get1()); - } - - private Pair> createPopulatedUmlResourceAndIdMapping(String name) { - XMLResource umlResource = (XMLResource) resourceSet.createResource(getModelURI(name + ".uml_mockup")); - UPackage uPackage1 = uml.Package(); - umlResource.getContents().add(uPackage1); - uPackage1.setName("Package1"); - UClass uClass1 = uml.Class(); - uPackage1.getClasses().add(uClass1); - - UPackage uPackage2 = uml.Package(); - umlResource.getContents().add(uPackage2); - uPackage2.setName("Package2"); - UClass uClass2 = uml.Class(); - uPackage2.getClasses().add(uClass2); - - Map expectedIdMapping = Map.of( // - name + "_package-1", uPackage1, // - name + "_package-2", uPackage2, // - name + "_class-1", uClass1, // - name + "_class-2", uClass2 // - ); - expectedIdMapping.forEach((id, obj) -> umlResource.setID(obj, id)); - return new Pair>(umlResource, expectedIdMapping); - } - - private void validateIds(Resource copiedResource, Map expectedIdMapping) { - expectedIdMapping.forEach((id, object) -> { - EObject copiedObject = copiedResource.getEObject(id); - assertNotNull(copiedObject, "could not find element with id " + id); - assertEquals(((Identified) object).getId(), ((Identified) copiedObject).getId(), - "retrieved incorrect element for id " + id + "\nexpected: " + object + ", actual: " + copiedObject); - }); - } - - protected URI getModelURI(String modelFileName) { - File file = testProjectFolder.resolve("model").resolve(modelFileName).toFile(); - return createFileURI(file); - } -} diff --git a/views/src/main/xtend/tools/vitruv/framework/views/ViewTypeFactory.xtend b/views/src/main/xtend/tools/vitruv/framework/views/ViewTypeFactory.xtend new file mode 100644 index 0000000000..660479ba36 --- /dev/null +++ b/views/src/main/xtend/tools/vitruv/framework/views/ViewTypeFactory.xtend @@ -0,0 +1,15 @@ +package tools.vitruv.framework.views + +import edu.kit.ipd.sdq.activextendannotations.Utility +import tools.vitruv.framework.views.impl.IdentityMappingViewType + +@Utility +class ViewTypeFactory { + /** + * Creates a view type that generates a view via a one-to-one (identity) mapping of + * selected resource root elements in a view source to a {@link View}. + */ + static def ViewType createIdentityMappingViewType(String name) { + new IdentityMappingViewType(name) + } +} diff --git a/views/src/main/xtend/tools/vitruv/framework/views/ViewTypeRepository.xtend b/views/src/main/xtend/tools/vitruv/framework/views/ViewTypeRepository.xtend new file mode 100644 index 0000000000..2727e06d2d --- /dev/null +++ b/views/src/main/xtend/tools/vitruv/framework/views/ViewTypeRepository.xtend @@ -0,0 +1,36 @@ +package tools.vitruv.framework.views + +import java.util.ArrayList +import java.util.Collection +import java.util.HashMap +import java.util.Map +import static com.google.common.base.Preconditions.checkArgument +import static com.google.common.base.Preconditions.checkState + +/** + * Stores and manages all ViewTypes. + */ +class ViewTypeRepository implements ViewTypeProvider { + + val Map> registeredViewTypes + + new() { + registeredViewTypes = new HashMap + } + + override Collection> getViewTypes() { + return new ArrayList(registeredViewTypes.values) + } + + def void register(ViewType viewType) { + checkArgument(viewType !== null, "null cannot be added as a view type") + checkState(!registeredViewTypes.containsKey(viewType.name), "view type name '%s' already taken by another view type", viewType.name) + registeredViewTypes.put(viewType.name, viewType) + } + + def ViewType findViewType(String viewTypeName) { + checkArgument(!viewTypeName.nullOrEmpty, "view type name to search for must not be null or empty") + return registeredViewTypes.get(viewTypeName) + } + +} diff --git a/views/src/main/xtend/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend b/views/src/main/xtend/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend deleted file mode 100644 index 3175d61706..0000000000 --- a/views/src/main/xtend/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend +++ /dev/null @@ -1,355 +0,0 @@ -package tools.vitruv.framework.views.changederivation - -import allElementTypes.Root -import org.eclipse.emf.ecore.resource.Resource -import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource -import tools.vitruv.change.atomic.eobject.CreateEObject -import tools.vitruv.change.atomic.eobject.DeleteEObject -import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute -import tools.vitruv.change.atomic.root.InsertRootEObject -import tools.vitruv.change.atomic.root.RemoveRootEObject -import tools.vitruv.change.composite.description.VitruviusChangeResolver -import tools.vitruv.testutils.Capture - -import static org.hamcrest.CoreMatchers.instanceOf -import static org.hamcrest.MatcherAssert.assertThat -import static org.junit.jupiter.api.Assertions.assertEquals -import static org.junit.jupiter.api.Assertions.assertTrue -import static tools.vitruv.testutils.matchers.ModelMatchers.containsModelOf -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet - -import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories -import static extension tools.vitruv.testutils.Capture.operator_doubleGreaterThan - -class BasicStateChangePropagationTest extends StateChangePropagationTest { - private def getTestUri() { - getModelURI("Test.allElementTypes") - } - - @ParameterizedTest() - @DisplayName("create new resource and calculate state-based difference") - @MethodSource("strategiesToTest") - def void createNewResource(StateBasedChangeResolutionStrategy strategyToTest) { - val modelResource = new ResourceSetImpl().createResource(testUri) => [ - contents += aet.Root => [ - id = "Root" - ] - ] - - val changes = strategyToTest.getChangeSequenceForCreated(modelResource) - assertEquals(3, changes.EChanges.size) - assertEquals(1, changes.EChanges.filter(InsertRootEObject).size) - assertEquals(1, changes.EChanges.filter(CreateEObject).size) - assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - - // Create empty resource to apply generated changes to - val validationResourceSet = new ResourceSetImpl() - VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(changes) - - modelResource.save(null) - assertEquals(1, validationResourceSet.resources.size) - assertThat(validationResourceSet.resources.get(0), containsModelOf(modelResource)) - } - - @ParameterizedTest() - @DisplayName("delete existing resource and calculate state-based difference") - @MethodSource("strategiesToTest") - def void deleteResource(StateBasedChangeResolutionStrategy strategyToTest) { - val modelResource = new Capture - resourceSet.record [ - createResource(testUri) => [ - contents += aet.Root => [ - id = "Root" - ] - ] >> modelResource - ] - (-modelResource).save(null) - val changes = strategyToTest.getChangeSequenceForDeleted(-modelResource) - assertEquals(2, changes.EChanges.size) - assertEquals(1, changes.EChanges.filter(RemoveRootEObject).size) - assertEquals(1, changes.EChanges.filter(DeleteEObject).size) - - // Load resource to apply generated changes to - val validationResourceSet = new ResourceSetImpl() - validationResourceSet.getResource(testUri, true) - VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(changes) - - assertEquals(1, validationResourceSet.resources.size) - assertTrue(validationResourceSet.resources.get(0).contents.empty) - } - - @ParameterizedTest() - @DisplayName("replace root element and calculate state-based difference") - @MethodSource("strategiesToTest") - def void replaceRootElement(StateBasedChangeResolutionStrategy strategyToTest) { - val modelResource = new Capture - resourceSet.record [ - createResource(testUri) => [ - contents += aet.Root => [ - id = "Root" - ] - ] >> modelResource - ] - (-modelResource).save(null) - - (-modelResource).record [ - contents.clear() - contents += aet.Root => [ - id = "Root2" - ] - ] - - val validationResourceSet = new ResourceSetImpl().withGlobalFactories() - val oldState = validationResourceSet.getResource(testUri, true) - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) - - VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(changes) - - assertEquals(1, validationResourceSet.resources.size) - assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) - } - - @ParameterizedTest() - @DisplayName("change a root element property and calculate state-based difference") - @MethodSource("strategiesToTest") - def void changeRootElementFeature(StateBasedChangeResolutionStrategy strategyToTest) { - val modelResource = new Capture - val root = aet.Root - resourceSet.record [ - createResource(testUri) => [ - contents += root => [ - id = "Root" - ] - ] >> modelResource - ] - (-modelResource).save(null) - - resourceSet.record [ - root.singleValuedEAttribute = 2 - ] - - val validationResourceSet = new ResourceSetImpl().withGlobalFactories() - val oldState = validationResourceSet.getResource(testUri, true) - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) - assertEquals(1, changes.EChanges.size) - assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - - VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(changes) - - assertEquals(1, validationResourceSet.resources.size) - assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) - } - - @ParameterizedTest() - @DisplayName("change a root element's id and calculate state-based difference") - @MethodSource("strategiesToTest") - def void changeRootElementId(DefaultStateBasedChangeResolutionStrategy strategyToTest) { - val modelResource = new Capture - val root = aet.Root - resourceSet.record [ - createResource(testUri) => [ - contents += root => [ - id = "Root" - ] - ] >> modelResource - ] - (-modelResource).save(null) - - resourceSet.record [ - root.id = "Root2" - ] - - val validationResourceSet = new ResourceSetImpl().withGlobalFactories() - val oldState = validationResourceSet.getResource(testUri, true) - val unresolvedChanges = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) - val changes = VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(unresolvedChanges) - switch (strategyToTest.useIdentifiers) { - case ONLY, - case WHEN_AVAILABLE: { - val Iterable> deleteChanges = newArrayList(changes.EChanges.filter(DeleteEObject)) - assertEquals(1, deleteChanges.size) - assertThat(deleteChanges.head.affectedElement, instanceOf(Root)) - assertEquals("Root", (deleteChanges.head.affectedElement as Root).id) - - val Iterable> createChanges = newArrayList(changes.EChanges.filter(CreateEObject)) - assertEquals(1, createChanges.size) - assertThat(createChanges.head.affectedElement, instanceOf(Root)) - assertEquals("Root2", (createChanges.head.affectedElement as Root).id) - } - case NEVER: { - assertEquals(1, changes.EChanges.size) - assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - } - } - - assertEquals(1, validationResourceSet.resources.size) - assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) - } - - @ParameterizedTest() - @DisplayName("change a non-root element property and calculate state-based difference") - @MethodSource("strategiesToTest") - def void changeNonRootElementFeature(StateBasedChangeResolutionStrategy strategyToTest) { - val modelResource = new Capture - val root = aet.Root - val containedRoot = aet.Root - resourceSet.record [ - createResource(testUri) => [ - contents += root => [ - id = "Root" - recursiveRoot = containedRoot => [ - id = "ContainedRoot" - singleValuedEAttribute = 0 - ] - ] - ] >> modelResource - ] - (-modelResource).save(null) - - resourceSet.record [ - containedRoot.singleValuedEAttribute = 1 - ] - - val validationResourceSet = new ResourceSetImpl().withGlobalFactories() - val oldState = validationResourceSet.getResource(testUri, true) - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) - assertEquals(1, changes.EChanges.size) - assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - - VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(changes) - - assertEquals(1, validationResourceSet.resources.size) - assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) - } - - @ParameterizedTest() - @DisplayName("change a non-root element's id and calculate state-based difference") - @MethodSource("strategiesToTest") - def void changeNonRootElementId(DefaultStateBasedChangeResolutionStrategy strategyToTest) { - val modelResource = new Capture - val root = aet.Root - val containedRoot = aet.Root - resourceSet.record [ - createResource(testUri) => [ - contents += root => [ - id = "Root" - recursiveRoot = containedRoot => [ - id = "ContainedRoot" - singleValuedEAttribute = 0 - ] - ] - ] >> modelResource - ] - (-modelResource).save(null) - - resourceSet.record [ - containedRoot.id = "ContainedRoot2" - ] - - val validationResourceSet = new ResourceSetImpl().withGlobalFactories() - val oldState = validationResourceSet.getResource(testUri, true) - val unresolvedChanges = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) - val changes = VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(unresolvedChanges) - switch (strategyToTest.useIdentifiers) { - case ONLY, - case WHEN_AVAILABLE: { - val Iterable> deleteChanges = newArrayList(changes.EChanges.filter(DeleteEObject)) - assertEquals(1, deleteChanges.size) - assertThat(deleteChanges.head.affectedElement, instanceOf(Root)) - assertEquals("ContainedRoot", (deleteChanges.head.affectedElement as Root).id) - - val Iterable> createChanges = newArrayList(changes.EChanges.filter(CreateEObject)) - assertEquals(1, createChanges.size) - assertThat(createChanges.head.affectedElement, instanceOf(Root)) - assertEquals("ContainedRoot2", (createChanges.head.affectedElement as Root).id) - } - case NEVER: { - assertEquals(1, changes.EChanges.size) - assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - } - } - - assertEquals(1, validationResourceSet.resources.size) - assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) - } - - @ParameterizedTest() - @DisplayName("move a resource to new location and calculate state-based difference") - @MethodSource("strategiesToTest") - def void moveResource(StateBasedChangeResolutionStrategy strategyToTest) { - val modelResource = new Capture - val root = aet.Root - resourceSet.record [ - createResource(testUri) => [ - contents += root => [ - id = "Root" - ] - ] >> modelResource - ] - (-modelResource).save(null) - - val validationResourceSet = new ResourceSetImpl().withGlobalFactories() - val oldState = validationResourceSet.getResource(testUri, true) - - val movedResourceUri = getModelURI("moved.allElementTypes") - resourceSet.record [ - createResource(movedResourceUri) => [ - contents += root - ] >> modelResource - ] - - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) - assertEquals(2, changes.EChanges.size) - assertEquals(1, changes.EChanges.filter(RemoveRootEObject).size) - assertEquals(1, changes.EChanges.filter(InsertRootEObject).size) - - VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(changes) - - (-modelResource).save(null) - assertEquals(2, validationResourceSet.resources.size) - assertThat(validationResourceSet.getResource(movedResourceUri, false), containsModelOf(-modelResource)) - } - - @ParameterizedTest() - @DisplayName("move a resource to new location changing root feature and calculate state-based difference") - @MethodSource("strategiesToTest") - def void moveResourceAndChangeRootFeature(StateBasedChangeResolutionStrategy strategyToTest) { - val modelResource = new Capture - val root = aet.Root - resourceSet.record [ - createResource(testUri) => [ - contents += root => [ - id = "Root" - ] - ] >> modelResource - ] - (-modelResource).save(null) - - val validationResourceSet = new ResourceSetImpl().withGlobalFactories() - val oldState = validationResourceSet.getResource(testUri, true) - - val movedResourceUri = getModelURI("moved.allElementTypes") - resourceSet.record [ - (-modelResource).contents -= root - root.singleValuedEAttribute = 2 - createResource(movedResourceUri) => [ - contents += root - ] >> modelResource - ] - - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) - assertEquals(3, changes.EChanges.size) - assertEquals(1, changes.EChanges.filter(RemoveRootEObject).size) - assertEquals(1, changes.EChanges.filter(InsertRootEObject).size) - assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - - VitruviusChangeResolver.forHierarchicalIds(validationResourceSet).resolveAndApply(changes) - - (-modelResource).save(null) - assertEquals(2, validationResourceSet.resources.size) - assertThat(validationResourceSet.getResource(movedResourceUri, false), containsModelOf(-modelResource)) - } -} diff --git a/views/src/main/xtend/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend b/views/src/main/xtend/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend new file mode 100644 index 0000000000..9a5f991186 --- /dev/null +++ b/views/src/main/xtend/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend @@ -0,0 +1,118 @@ +package tools.vitruv.framework.views.changederivation + +import org.eclipse.emf.common.notify.Notifier +import org.eclipse.emf.common.util.BasicMonitor +import org.eclipse.emf.compare.EMFCompare +import org.eclipse.emf.compare.match.impl.MatchEngineFactoryImpl +import org.eclipse.emf.compare.match.impl.MatchEngineFactoryRegistryImpl +import org.eclipse.emf.compare.merge.BatchMerger +import org.eclipse.emf.compare.merge.IMerger +import org.eclipse.emf.compare.scope.DefaultComparisonScope +import org.eclipse.emf.compare.utils.UseIdentifiers +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl +import org.eclipse.emf.ecore.util.EcoreUtil +import tools.vitruv.change.composite.description.VitruviusChangeResolver +import tools.vitruv.change.composite.recording.ChangeRecorder + +import static com.google.common.base.Preconditions.checkArgument + +import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceUtil.getReferencedProxies +import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceCopier + +/** + * This default strategy for diff based state changes uses EMFCompare to resolve a + * diff to a sequence of individual changes. + */ +class DefaultStateBasedChangeResolutionStrategy implements StateBasedChangeResolutionStrategy { + /** The identifier matching behavior used by this strategy */ + public val UseIdentifiers useIdentifiers + + /** + * Creates a new instance with the default identifier matching behavior + * which is match by identifier when available. + */ + new() { + this(UseIdentifiers.WHEN_AVAILABLE) + } + + /** + * Creates a new instance with the provided identifier matching behavior. + * @param useIdentifiers The identifier matching behavior to use. + */ + new(UseIdentifiers useIdentifiers) { + this.useIdentifiers = useIdentifiers + } + + private def checkNoProxies(Resource resource, String stateNotice) { + val proxies = resource.referencedProxies + checkArgument(proxies.empty, "%s '%s' should not contain proxies, but contains the following: %s", stateNotice, + resource.URI, String.join(", ", proxies.map[toString])) + } + + override getChangeSequenceBetween(Resource newState, Resource oldState) { + checkArgument(oldState !== null && newState !== null, "old state or new state must not be null!") + newState.checkNoProxies("new state") + oldState.checkNoProxies("old state") + val monitoredResourceSet = new ResourceSetImpl() + val currentStateCopy = ResourceCopier.copyViewResource(oldState, monitoredResourceSet) + return currentStateCopy.record [ + if (oldState.URI != newState.URI) { + currentStateCopy.URI = newState.URI + } + compareStatesAndReplayChanges(newState, currentStateCopy) + ] + } + + override getChangeSequenceForCreated(Resource newState) { + checkArgument(newState !== null, "new state must not be null!") + newState.checkNoProxies("new state") + // It is possible that root elements are automatically generated during resource creation (e.g., Java packages). + // Thus, we create the resource and then monitor the re-insertion of the elements + val monitoredResourceSet = new ResourceSetImpl() + val newResource = monitoredResourceSet.createResource(newState.URI) + newResource.contents.clear() + return newResource.record [ + newResource.contents += EcoreUtil.copyAll(newState.contents) + ] + } + + override getChangeSequenceForDeleted(Resource oldState) { + checkArgument(oldState !== null, "old state must not be null!") + oldState.checkNoProxies("old state") + // Setup resolver and copy state: + val monitoredResourceSet = new ResourceSetImpl() + val currentStateCopy = ResourceCopier.copyViewResource(oldState, monitoredResourceSet) + return currentStateCopy.record [ + currentStateCopy.contents.clear() + ] + } + + private def record(Resource resource, ()=>void function) { + try (val changeRecorder = new ChangeRecorder(resource.resourceSet)) { + changeRecorder.beginRecording + changeRecorder.addToRecording(resource) + function.apply() + val recordedChanges = changeRecorder.endRecording + val changeResolver = VitruviusChangeResolver.forHierarchicalIds(resource.resourceSet) + return changeResolver.assignIds(recordedChanges) + } + } + + /** + * Compares states using EMFCompare and replays the changes to the current state. + */ + private def compareStatesAndReplayChanges(Notifier newState, Notifier currentState) { + val scope = new DefaultComparisonScope(newState, currentState, null) + val emfCompare = (EMFCompare.builder => [ + matchEngineFactoryRegistry = MatchEngineFactoryRegistryImpl.createStandaloneInstance => [ + add(new MatchEngineFactoryImpl(useIdentifiers)) + ] + ]).build + val differences = emfCompare.compare(scope).differences + // Replay the EMF compare differences + val mergerRegistry = IMerger.RegistryImpl.createStandaloneInstance() + val merger = new BatchMerger(mergerRegistry) + merger.copyAllLeftToRight(differences, new BasicMonitor) + } +} diff --git a/views/src/main/xtend/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend b/views/src/main/xtend/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend deleted file mode 100644 index e5d30778eb..0000000000 --- a/views/src/main/xtend/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend +++ /dev/null @@ -1,41 +0,0 @@ -package tools.vitruv.framework.views.changederivation - -import org.eclipse.emf.ecore.resource.Resource -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource - -import static org.junit.jupiter.api.Assertions.assertThrows - -class EdgeCaseStateChangeTest extends StateChangePropagationTest { - - /** - * Tests the comparison of two states with no changes for the uml mockup model. - */ - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testNoUmlChange(StateBasedChangeResolutionStrategy strategyToTest) { - compareChanges(umlModel, umlCheckpoint, strategyToTest) - } - - /** - * Tests the comparison of two states with no changes for the pcm mockup model. - */ - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testNoPcmChange(StateBasedChangeResolutionStrategy strategyToTest) { - compareChanges(umlModel, umlCheckpoint, strategyToTest) - } - - /** - * Tests invalid input: null instead of state resources. - */ - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testNullResources(StateBasedChangeResolutionStrategy strategyToTest) { - val Resource nullResource = null - assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceForCreated(nullResource)] - assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceBetween(nullResource, nullResource)] - assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceForDeleted(nullResource)] - } - -} diff --git a/views/src/main/xtend/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend b/views/src/main/xtend/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend deleted file mode 100644 index 64ac2d08d9..0000000000 --- a/views/src/main/xtend/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend +++ /dev/null @@ -1,74 +0,0 @@ -package tools.vitruv.framework.views.changederivation - -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource - -import static tools.vitruv.testutils.metamodels.PcmMockupCreators.pcm - -class PcmStateChangeTest extends StateChangePropagationTest { - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testAddComponent(StateBasedChangeResolutionStrategy strategyToTest) { - pcmRoot.components += pcm.Component => [name = "NewlyAddedComponent"] - compareChanges(pcmModel, pcmCheckpoint, strategyToTest) - } - - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testRenameComponent(StateBasedChangeResolutionStrategy strategyToTest) { - pcmRoot.components.get(0).name = "RenamedComponent" - compareChanges(pcmModel, pcmCheckpoint, strategyToTest) - } - - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testDeleteComponent(StateBasedChangeResolutionStrategy strategyToTest) { - pcmRoot.components.remove(0) - compareChanges(pcmModel, pcmCheckpoint, strategyToTest) - } - - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testAddProvidedInterface(StateBasedChangeResolutionStrategy strategyToTest) { - val newInterface = pcm.Interface => [name = "NewlyAddedInterface"] - pcmRoot.interfaces += pcm.Interface - newInterface.methods += pcm.Method => [name = "newMethod"] - pcmRoot.components.get(0).providedInterface = newInterface - compareChanges(pcmModel, pcmCheckpoint, strategyToTest) - } - - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testInterfaceWithMultipleMethods(StateBasedChangeResolutionStrategy strategyToTest) { - val newInterface = pcm.Interface => [ - name = "NewlyAddedInterface" - ] - pcmRoot.interfaces += newInterface - newInterface.methods += (0 .. 5).map [ index | - pcm.Method => [name = '''newMethod«index»'''] - ] - pcmRoot.components.get(0).providedInterface = newInterface - compareChanges(pcmModel, pcmCheckpoint, strategyToTest) - } - - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testAddDifferentProvidedInterface(StateBasedChangeResolutionStrategy strategyToTest) { - val firstInterface = pcm.Interface => [name = "NewlyAddedInterface"] - val secondInterface = pcm.Interface => [name = "NewlyAddedInterface2"] - pcmRoot.interfaces += #[firstInterface, secondInterface] - pcmRoot.components.get(0).providedInterface = firstInterface - pcmRoot.components.get(0).providedInterface = secondInterface - compareChanges(pcmModel, pcmCheckpoint, strategyToTest) - } - - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testAddMultipleInterfaces(StateBasedChangeResolutionStrategy strategyToTest) { - pcmRoot.interfaces += (1 .. 3).map [ index | - pcm.Interface => [name = '''NewlyAddedInterface«index»'''] - ] - pcmRoot.interfaces.forEach[methods += pcm.Method => [name = "newMethod"]] - compareChanges(pcmModel, pcmCheckpoint, strategyToTest) - } -} diff --git a/views/src/main/xtend/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend b/views/src/main/xtend/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend deleted file mode 100644 index a30d64e748..0000000000 --- a/views/src/main/xtend/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend +++ /dev/null @@ -1,175 +0,0 @@ -package tools.vitruv.framework.views.changederivation - -import java.nio.file.Path -import java.util.stream.Stream -import org.eclipse.emf.common.notify.Notifier -import org.eclipse.emf.common.util.URI -import org.eclipse.emf.compare.utils.UseIdentifiers -import org.eclipse.emf.ecore.EObject -import org.eclipse.emf.ecore.resource.Resource -import org.eclipse.emf.ecore.resource.ResourceSet -import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl -import org.eclipse.emf.ecore.util.EcoreUtil -import org.eclipse.xtend.lib.annotations.Accessors -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Named -import org.junit.jupiter.api.^extension.ExtendWith -import pcm_mockup.Repository -import tools.vitruv.change.composite.description.VitruviusChange -import tools.vitruv.change.composite.description.VitruviusChangeResolver -import tools.vitruv.change.composite.recording.ChangeRecorder -import tools.vitruv.testutils.RegisterMetamodelsInStandalone -import tools.vitruv.testutils.TestLogging -import tools.vitruv.testutils.TestProject -import tools.vitruv.testutils.TestProjectManager -import uml_mockup.UPackage - -import static org.junit.jupiter.api.Assertions.* -import static tools.vitruv.testutils.metamodels.PcmMockupCreators.pcm -import static tools.vitruv.testutils.metamodels.UmlMockupCreators.uml - -import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.common.util.URIUtil.createFileURI -import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories - -@ExtendWith(TestProjectManager, TestLogging, RegisterMetamodelsInStandalone) -abstract class StateChangePropagationTest { - protected static final String PCM_FILE_EXT = "pcm_mockup" - protected static final String UML_FILE_EXT = "uml_mockup" - var Path testProjectFolder - @Accessors(PROTECTED_GETTER) - var Resource umlCheckpoint - @Accessors(PROTECTED_GETTER) - var Resource pcmCheckpoint - @Accessors(PROTECTED_GETTER) - var Resource umlModel - @Accessors(PROTECTED_GETTER) - var Resource pcmModel - @Accessors(PROTECTED_GETTER) - var Repository pcmRoot - @Accessors(PROTECTED_GETTER) - var UPackage umlRoot - var ChangeRecorder changeRecorder - @Accessors(PROTECTED_GETTER) - var ResourceSet resourceSet - var ResourceSet checkpointResourceSet - - /** - * Creates the strategy, sets up the test model and prepares everything for determining changes. - */ - @BeforeEach - def void setup(@TestProject Path testProjectFolder) { - this.testProjectFolder = testProjectFolder - // Setup: - resourceSet = new ResourceSetImpl().withGlobalFactories() - checkpointResourceSet = new ResourceSetImpl().withGlobalFactories() - changeRecorder = new ChangeRecorder(resourceSet) - // Create mockup models: - resourceSet.record [ - createPcmMockupModel() - createUmlMockupModel() - ] - // create model checkpoints and start recording: - umlCheckpoint = umlModel.createCheckpoint - pcmCheckpoint = pcmModel.createCheckpoint - umlModel.startRecording - pcmModel.startRecording - } - - static def strategiesToTest() { - Stream.of( - Named.of("identifiers when available", new DefaultStateBasedChangeResolutionStrategy(UseIdentifiers.WHEN_AVAILABLE)), - Named.of("only identifiers", new DefaultStateBasedChangeResolutionStrategy(UseIdentifiers.ONLY)), - Named.of("never identifiers", new DefaultStateBasedChangeResolutionStrategy(UseIdentifiers.NEVER)) - ) - } - - /** - * Stops recording in case the test does not call getRecordedChanges() or getChangeFromComparisonWithCheckpoint(). - */ - @AfterEach - def stopRecording() { - changeRecorder.close() - } - - /** - * USE THIS METHOD TO COMPARE RESULTS! - * Compares two changes: The recorded change sequence and the resolved changes by the state delta based strategy. - */ - protected def compareChanges(Resource model, Resource checkpoint, StateBasedChangeResolutionStrategy strategyToTest) { - model.save(null) - val deltaBasedChange = resourceSet.endRecording - val unresolvedStateBasedChange = strategyToTest.getChangeSequenceBetween(model, checkpoint) - assertNotNull(unresolvedStateBasedChange) - val stateBasedChange = VitruviusChangeResolver.forHierarchicalIds(checkpoint.resourceSet).resolveAndApply(unresolvedStateBasedChange) - val message = getTextualRepresentation(stateBasedChange, deltaBasedChange) - val stateBasedChangedObjects = stateBasedChange.affectedAndReferencedEObjects - val deltaBasedChangedObjects = deltaBasedChange.affectedAndReferencedEObjects - assertEquals(stateBasedChangedObjects.size, deltaBasedChangedObjects.size, ''' - Got a different number of changed objects: - «message»''') - stateBasedChangedObjects.forEach [ stateBasedChangedObject | - assertTrue(deltaBasedChangedObjects.exists [EcoreUtil.equals(it, stateBasedChangedObject)], ''' - Could not find this changed object in the delta based change: - «stateBasedChangedObject» - - «message»''') - ] - } - - /** - * Returns the recorded change sequences (the "original" changes) for a specific model instance. - */ - private def VitruviusChange endRecording(Notifier notifier) { - changeRecorder.removeFromRecording(notifier) - return changeRecorder.endRecording - } - - private def String getTextualRepresentation(VitruviusChange stateBasedChange, VitruviusChange deltaBasedChange) ''' - State-based «stateBasedChange» - Delta-based «deltaBasedChange» - ''' - - private def createPcmMockupModel() { - pcmModel = resourceSet.createResource(getModelURI("My.pcm_mockup")) => [ - contents += (pcmRoot = pcm.Repository => [ - name = "RootRepository" - interfaces += pcm.Interface - components += pcm.Component - ]) - ] - pcmModel.save(null) - } - - private def createUmlMockupModel() { - umlModel = resourceSet.createResource(getModelURI("My.uml_mockup")) => [ - contents += (umlRoot = uml.Package => [ - name = "RootPackage" - interfaces += uml.Interface - classes += uml.Class - ]) - ] - umlModel.save(null) - } - - private def startRecording(Notifier notifier) { - changeRecorder.addToRecording(notifier) - if (!changeRecorder.isRecording) { - changeRecorder.beginRecording - } - } - - protected def record(T notifier, (T) => void function) { - notifier.startRecording - function.apply(notifier) - return notifier.endRecording - } - - private def Resource createCheckpoint(Resource original) { - return checkpointResourceSet.getResource(original.URI, true) - } - - protected def URI getModelURI(String modelFileName) { - return testProjectFolder.resolve("model").resolve(modelFileName).toFile().createFileURI() - } -} diff --git a/views/src/main/xtend/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend b/views/src/main/xtend/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend deleted file mode 100644 index c21e11d991..0000000000 --- a/views/src/main/xtend/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend +++ /dev/null @@ -1,77 +0,0 @@ -package tools.vitruv.framework.views.changederivation - -import org.eclipse.emf.compare.utils.UseIdentifiers -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.EnumSource -import org.junit.jupiter.params.provider.MethodSource - -import static tools.vitruv.testutils.metamodels.UmlMockupCreators.uml - -class UmlStateChangeTest extends StateChangePropagationTest { - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testRenameTypes(StateBasedChangeResolutionStrategy strategyToTest) { - umlRoot.classes.get(0) => [name = "RenamedClass"] - umlRoot.interfaces.get(0) => [name = "RenamedInterface"] - compareChanges(umlModel, umlCheckpoint, strategyToTest) - } - - @ParameterizedTest() - @EnumSource( - value = UseIdentifiers, - //TODO: should be re-enabled when state comparison is used instead of change comparison - names = #["ONLY"], - mode = EnumSource.Mode.EXCLUDE - ) - def void testNewAttributes(UseIdentifiers useIdentifiers) { - val strategyToTest = new DefaultStateBasedChangeResolutionStrategy(useIdentifiers) - umlRoot.classes.get(0) => [ - attributes += uml.Attribute => [attributeName = "NewlyAddedAttribute"] - ] - compareChanges(umlModel, umlCheckpoint, strategyToTest) - } - - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testNewMethod(StateBasedChangeResolutionStrategy strategyToTest) { - umlRoot.interfaces.get(0) => [ - methods += uml.Method => [name = "NewlyAddedMethod"] - ] - compareChanges(umlModel, umlCheckpoint, strategyToTest) - } - - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testNewClass(StateBasedChangeResolutionStrategy strategyToTest) { - umlRoot.classes += uml.Class => [name = "NewlyAddedClass"] - compareChanges(umlModel, umlCheckpoint, strategyToTest) - } - - @ParameterizedTest() - @EnumSource( - value = UseIdentifiers, - //TODO: should be re-enabled when state comparison is used instead of change comparison - names = #["NEVER"], - mode = EnumSource.Mode.EXCLUDE - ) - def void testReplaceClass(UseIdentifiers useIdentifiers) { - val strategyToTest = new DefaultStateBasedChangeResolutionStrategy(useIdentifiers) - umlRoot.classes.remove(0) - umlRoot.classes += uml.Class => [name = "NewlyAddedClass"] - compareChanges(umlModel, umlCheckpoint, strategyToTest) - } - - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testDeleteClass(StateBasedChangeResolutionStrategy strategyToTest) { - umlRoot.classes.remove(0) - compareChanges(umlModel, umlCheckpoint, strategyToTest) - } - - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testNewInterface(StateBasedChangeResolutionStrategy strategyToTest) { - umlRoot.interfaces += uml.Interface => [name = "NewlyAddedInterface"] - compareChanges(umlModel, umlCheckpoint, strategyToTest) - } -} diff --git a/views/src/main/xtend/tools/vitruv/framework/views/impl/AbstractViewType.xtend b/views/src/main/xtend/tools/vitruv/framework/views/impl/AbstractViewType.xtend new file mode 100644 index 0000000000..7eea1879ff --- /dev/null +++ b/views/src/main/xtend/tools/vitruv/framework/views/impl/AbstractViewType.xtend @@ -0,0 +1,16 @@ +package tools.vitruv.framework.views.impl + +import org.eclipse.xtend.lib.annotations.Accessors +import tools.vitruv.framework.views.ViewSelector + +import static com.google.common.base.Preconditions.checkArgument + +abstract package class AbstractViewType implements ViewCreatingViewType { + @Accessors(PUBLIC_GETTER) + val String name + + new(String name) { + checkArgument(name !== null, "view type name must not be null") + this.name = name + } +} diff --git a/views/src/main/xtend/tools/vitruv/framework/views/impl/BasicView.xtend b/views/src/main/xtend/tools/vitruv/framework/views/impl/BasicView.xtend new file mode 100644 index 0000000000..65ac02e0ee --- /dev/null +++ b/views/src/main/xtend/tools/vitruv/framework/views/impl/BasicView.xtend @@ -0,0 +1,151 @@ +package tools.vitruv.framework.views.impl + +import org.eclipse.emf.common.notify.Notification +import org.eclipse.emf.common.notify.Notifier +import org.eclipse.emf.common.notify.impl.AdapterImpl +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.emf.ecore.resource.ResourceSet +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl +import org.eclipse.xtend.lib.annotations.Accessors +import tools.vitruv.change.atomic.hid.HierarchicalId +import tools.vitruv.change.atomic.uuid.Uuid +import tools.vitruv.change.composite.description.PropagatedChange +import tools.vitruv.change.composite.description.VitruviusChange +import tools.vitruv.change.composite.propagation.ChangePropagationListener +import tools.vitruv.framework.views.ChangeableViewSource +import tools.vitruv.framework.views.ViewSelection +import tools.vitruv.framework.views.ViewSelector +import tools.vitruv.framework.views.changederivation.StateBasedChangeResolutionStrategy + +import static com.google.common.base.Preconditions.checkArgument +import static com.google.common.base.Preconditions.checkState + +import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories + +package class BasicView implements ModifiableView, ChangePropagationListener { + @Accessors(PUBLIC_GETTER, PROTECTED_SETTER) + var ViewSelection selection + @Accessors(PUBLIC_GETTER, PROTECTED_SETTER) + var ViewCreatingViewType viewType + @Accessors(PUBLIC_GETTER, PROTECTED_SETTER) + var ChangeableViewSource viewSource + @Accessors(PROTECTED_GETTER) + var ResourceSet viewResourceSet + boolean modelChanged + @Accessors(PROTECTED_SETTER) + boolean viewChanged + boolean closed + + protected new(ViewCreatingViewType viewType, ChangeableViewSource viewSource, + ViewSelection selection) { + checkArgument(viewType !== null, "view type must not be null") + checkArgument(viewSource !== null, "view selection must not be null") + checkArgument(selection !== null, "view source must not be null") + this.viewType = viewType + this.viewSource = viewSource + this.selection = selection + viewSource.addChangePropagationListener(this) + viewResourceSet = new ResourceSetImpl().withGlobalFactories + update + } + + override getRootObjects() { + checkNotClosed() + viewResourceSet.resources.map[contents].flatten.toList + } + + override isModified() { + return viewChanged + } + + override isOutdated() { + return modelChanged + } + + override update() { + checkNotClosed() + checkState(!isModified, "cannot update from model when view is modified") + modelChanged = false + viewType.updateView(this) + viewChanged = false + viewResourceSet.addChangeListeners() + } + + override close() throws Exception { + if (!closed) { + closed = true + viewResourceSet.resources.forEach[unload()] + viewResourceSet.resources.clear() + viewResourceSet.removeChangeListeners() + } + viewSource.removeChangePropagationListener(this) + } + + override isClosed() { + return closed + } + + override finishedChangePropagation(Iterable propagatedChanges) { + modelChanged = true + } + + override startedChangePropagation(VitruviusChange changeToPropagate) { + // do nothing + } + + override void registerRoot(EObject object, URI persistAt) { + checkNotClosed() + checkArgument(object !== null, "object to register as root must not be null") + checkArgument(persistAt !== null, "URI for root to register must not be null") + viewResourceSet.createResource(persistAt) => [ + contents += object + ] + } + + override void moveRoot(EObject object, URI newLocation) { + checkNotClosed() + checkArgument(object !== null, "object to move must not be null") + checkState(rootObjects.contains(object), "view must contain element %s to move", object) + checkArgument(newLocation !== null, "URI for new location of root must not be null") + viewResourceSet.resources.findFirst[contents.contains(object)].URI = newLocation + } + + def void checkNotClosed() { + checkState(!closed, "view is already closed!") + } + + private def void addChangeListeners(Notifier notifier) { + notifier.eAdapters += new AdapterImpl() { + override notifyChanged(Notification message) { + viewChanged = true + } + } + switch (notifier) { + ResourceSet: notifier.resources.forEach[addChangeListeners()] + Resource: notifier.contents.forEach[addChangeListeners()] + EObject: notifier.eContents.forEach[addChangeListeners] + } + } + + private def void removeChangeListeners(ResourceSet resourceSet) { + resourceSet.allContents.forEach [ + eAdapters.clear() + ] + } + + override modifyContents((ResourceSet)=>void modificationFunction) { + modificationFunction.apply(viewResourceSet) + } + + override withChangeRecordingTrait() { + checkNotClosed() + return new ChangeRecordingView(this) + } + + override withChangeDerivingTrait(StateBasedChangeResolutionStrategy changeResolutionStrategy) { + checkNotClosed() + return new ChangeDerivingView(this, changeResolutionStrategy) + } +} \ No newline at end of file diff --git a/views/src/main/xtend/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend b/views/src/main/xtend/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend new file mode 100644 index 0000000000..e4d9688e91 --- /dev/null +++ b/views/src/main/xtend/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend @@ -0,0 +1,105 @@ +package tools.vitruv.framework.views.impl + +import java.util.ArrayList +import java.util.HashMap +import java.util.HashSet +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.emf.ecore.resource.ResourceSet +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl +import org.eclipse.xtend.lib.annotations.Delegate +import tools.vitruv.change.atomic.hid.HierarchicalId +import tools.vitruv.change.composite.description.VitruviusChange +import tools.vitruv.change.composite.description.VitruviusChangeFactory +import tools.vitruv.framework.views.CommittableView +import tools.vitruv.framework.views.View +import tools.vitruv.framework.views.changederivation.StateBasedChangeResolutionStrategy + +import static com.google.common.base.Preconditions.checkArgument +import static com.google.common.base.Preconditions.checkState + +import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.common.util.URIUtil.isPathmap +import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceCopier + +/** + * A {@link View} that derives changes based on the changed state of its resources and allows to propagate them + * back to the underlying models using the {@link #commitChanges} method. + */ +class ChangeDerivingView implements ModifiableView, CommittableView { + @Delegate + BasicView view + + val StateBasedChangeResolutionStrategy changeResolutionStrategy + var ResourceSet originalStateViewResourceSet + var HashMap originalStateResourceMapping + + protected new(BasicView view, StateBasedChangeResolutionStrategy changeResolutionStrategy) { + checkArgument(view !== null, "view must not be null") + checkState(!view.isModified, "view must not be modified") + checkState(!view.isOutdated, "view must not be outdated") + checkArgument(changeResolutionStrategy !== null, "change resolution strategy must not be null") + this.view = view + this.changeResolutionStrategy = changeResolutionStrategy + setupReferenceState + } + + override update() { + closeOriginalState + view.update + setupReferenceState + } + + private def setupReferenceState() { + originalStateViewResourceSet = new ResourceSetImpl + ResourceCopier.copyViewResources(view.viewResourceSet.resources, originalStateViewResourceSet) + originalStateResourceMapping = new HashMap + view.viewResourceSet.resources.forEach[resource | originalStateResourceMapping.put(resource, originalStateViewResourceSet.resources.findFirst[URI === resource.URI])] + } + + override commitChanges() { + view.checkNotClosed() + val changes = new ArrayList() + val allResources = new HashSet(originalStateResourceMapping.keySet) + allResources.addAll(view.viewResourceSet.resources) // consider newly added resources + for (changedResource : allResources.filter[!URI.isPathmap]) { + val change = generateChange(changedResource, originalStateResourceMapping.get(changedResource)) + changes += change + } + val change = VitruviusChangeFactory.instance.createCompositeChange(changes) + view.viewType.commitViewChanges(this, change) + view.viewChanged = false + } + + override close() throws Exception { + if (!isClosed) { + closeOriginalState + } + view.close + } + + private def VitruviusChange generateChange(Resource newState, Resource referenceState) { + if (referenceState === null) { + return changeResolutionStrategy.getChangeSequenceForCreated(newState) + } else if (newState === null) { + return changeResolutionStrategy.getChangeSequenceForDeleted(referenceState) + } else { + return changeResolutionStrategy.getChangeSequenceBetween(newState, referenceState) + } + } + + private def closeOriginalState() { + originalStateViewResourceSet.resources.forEach[unload] + originalStateViewResourceSet.resources.clear + } + + override withChangeRecordingTrait() { + val newView = view.withChangeRecordingTrait + closeOriginalState + return newView + } + + override withChangeDerivingTrait(StateBasedChangeResolutionStrategy changeResolutionStrategy) { + val newView = view.withChangeDerivingTrait(changeResolutionStrategy) + closeOriginalState + return newView + } +} diff --git a/views/src/main/xtend/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend b/views/src/main/xtend/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend new file mode 100644 index 0000000000..7187b96da5 --- /dev/null +++ b/views/src/main/xtend/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend @@ -0,0 +1,76 @@ +package tools.vitruv.framework.views.impl + +import org.eclipse.xtend.lib.annotations.Delegate +import tools.vitruv.change.composite.description.VitruviusChangeResolver +import tools.vitruv.change.composite.recording.ChangeRecorder +import tools.vitruv.framework.views.CommittableView +import tools.vitruv.framework.views.View +import tools.vitruv.framework.views.changederivation.StateBasedChangeResolutionStrategy + +import static com.google.common.base.Preconditions.checkArgument +import static com.google.common.base.Preconditions.checkState + +/** + * A {@link View} that records changes to its resources and allows to propagate them + * back to the underlying models using the {@link #commitChanges} method. + */ +class ChangeRecordingView implements ModifiableView, CommittableView { + @Delegate + BasicView view + ChangeRecorder changeRecorder + + protected new(BasicView view) { + checkArgument(view !== null, "view must not be null") + checkState(!view.isModified, "view must not be modified") + this.view = view + setupChangeRecorder + } + + override update() { + changeRecorder.endRecordingAndClose() + view.update() + setupChangeRecorder + } + + private def setupChangeRecorder() { + changeRecorder = new ChangeRecorder(view.viewResourceSet) + changeRecorder.addToRecording(view.viewResourceSet) + changeRecorder.beginRecording() + } + + override commitChanges() { + view.checkNotClosed() + val recordedChange = changeRecorder.endRecording() + val changeResolver = VitruviusChangeResolver.forHierarchicalIds(view.viewResourceSet) + val unresolvedChanges = changeResolver.assignIds(recordedChange) + view.viewType.commitViewChanges(this, unresolvedChanges) + view.viewChanged = false + changeRecorder.beginRecording() + } + + override close() throws Exception { + if (!isClosed) { + changeRecorder.close() + } + view.close() + } + + private def void endRecordingAndClose(ChangeRecorder recorder) { + if (recorder.isRecording) { + recorder.endRecording() + } + recorder.close() + } + + override withChangeRecordingTrait() { + val newView = view.withChangeRecordingTrait + changeRecorder.close + return newView + } + + override withChangeDerivingTrait(StateBasedChangeResolutionStrategy changeResolutionStrategy) { + val newView = view.withChangeDerivingTrait(changeResolutionStrategy) + changeRecorder.close + return newView + } +} diff --git a/views/src/main/xtend/tools/vitruv/framework/views/impl/ModifiableView.xtend b/views/src/main/xtend/tools/vitruv/framework/views/impl/ModifiableView.xtend new file mode 100644 index 0000000000..2d667d3e5e --- /dev/null +++ b/views/src/main/xtend/tools/vitruv/framework/views/impl/ModifiableView.xtend @@ -0,0 +1,14 @@ +package tools.vitruv.framework.views.impl + +import org.eclipse.emf.ecore.resource.ResourceSet +import tools.vitruv.framework.views.ChangeableViewSource +import tools.vitruv.framework.views.View + +/** + * A view whose contents can be modified, in particular by a view type implementation. + */ +interface ModifiableView extends View { + def void modifyContents((ResourceSet)=>void modificationFunction); + + def ChangeableViewSource getViewSource() +} diff --git a/views/src/main/xtend/tools/vitruv/framework/views/selectors/DirectViewElementSelector.xtend b/views/src/main/xtend/tools/vitruv/framework/views/selectors/DirectViewElementSelector.xtend new file mode 100644 index 0000000000..50add0b64f --- /dev/null +++ b/views/src/main/xtend/tools/vitruv/framework/views/selectors/DirectViewElementSelector.xtend @@ -0,0 +1,74 @@ +package tools.vitruv.framework.views.selectors + +import java.util.Collection +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtend.lib.annotations.Accessors +import org.eclipse.xtend.lib.annotations.Delegate +import tools.vitruv.framework.views.ChangeableViewSource +import tools.vitruv.framework.views.ModifiableViewSelection +import tools.vitruv.framework.views.ViewSelector +import tools.vitruv.framework.views.ViewType +import tools.vitruv.framework.views.impl.ViewCreatingViewType +import tools.vitruv.framework.views.selection.ElementViewSelection + +import static com.google.common.base.Preconditions.checkArgument +import static com.google.common.base.Preconditions.checkState + +/** + * A view selector that provides a selection of elements being view objects at + * the same time. This means, there is no indirection between selected elements + * and view elements (such as selecting types but providing instances in the view), + * but a selection is performed on the view elements themselves. + */ +class DirectViewElementSelector implements ViewSelector { + @Delegate + val ModifiableViewSelection viewSelection + + @Accessors(PUBLIC_GETTER) + val ChangeableViewSource viewSource + + @Accessors(PUBLIC_GETTER) + val ViewCreatingViewType, Id> viewType + + /** + * Creates a new selector based on the given collection of selectable elements + * for the given {@link ViewType} and {@link ChangeableViewSource}. All arguments + * must not be null. + * All elements will be unselected after creation. + * + * @param viewType - the {@link ViewType} to create a view for when + * calling {@link createView} + * @param viewSource - the {@link ChangeableViewSource} to create a view + * from + * @param selectableElements - the elements to select from to be used by the + * {@link ViewType} when creating a view + */ + new(ViewCreatingViewType, Id> viewType, ChangeableViewSource viewSource, + Collection selectableElements) { + checkArgument(selectableElements !== null, "selectable elements must not be null") + checkArgument(viewType !== null, "view type must not be null") + checkArgument(viewSource !== null, "view source must not be null") + this.viewType = viewType + this.viewSource = viewSource + this.viewSelection = new ElementViewSelection(selectableElements) + } + + override createView() { + checkState(isValid(), "the current selection is invalid, thus a view cannot be created") + return viewType.createView(this) + } + + /** + * {@link DirectViewElementSelector}s are always valid. + */ + override boolean isValid() { + // A basic selection is always valid. In particular, it does not require at least one element to be selected + // because it must be possible to create empty views upon creation of a (virtual) model. + true + } + + override getSelection() { + return new ElementViewSelection(viewSelection) + } + +} diff --git a/views/src/test/java/tools/vitruv/framework/views/ViewTypeRepositoryTest.java b/views/src/test/java/tools/vitruv/framework/views/ViewTypeRepositoryTest.java index 86a6f1c7e1..848b33a3fe 100644 --- a/views/src/test/java/tools/vitruv/framework/views/ViewTypeRepositoryTest.java +++ b/views/src/test/java/tools/vitruv/framework/views/ViewTypeRepositoryTest.java @@ -15,8 +15,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging; +import tools.vitruv.change.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.change.testutils.TestLogging; @ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) public class ViewTypeRepositoryTest { diff --git a/views/src/test/java/tools/vitruv/framework/views/impl/BasicViewTest.java b/views/src/test/java/tools/vitruv/framework/views/impl/BasicViewTest.java index 97616f2b03..24a75b6f48 100644 --- a/views/src/test/java/tools/vitruv/framework/views/impl/BasicViewTest.java +++ b/views/src/test/java/tools/vitruv/framework/views/impl/BasicViewTest.java @@ -8,7 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; +import static tools.vitruv.change.testutils.metamodels.AllElementTypesCreators.aet; import org.eclipse.emf.common.util.URI; import org.junit.jupiter.api.BeforeEach; @@ -24,8 +24,8 @@ import tools.vitruv.change.atomic.hid.HierarchicalId; import tools.vitruv.framework.views.ChangeableViewSource; import tools.vitruv.framework.views.ModifiableViewSelection; -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging; +import tools.vitruv.change.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.change.testutils.TestLogging; @ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) public class BasicViewTest { diff --git a/views/src/test/java/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java b/views/src/test/java/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java index 6157548b81..913976e3a2 100644 --- a/views/src/test/java/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java +++ b/views/src/test/java/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java @@ -11,9 +11,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; -import static tools.vitruv.testutils.matchers.ModelMatchers.equalsDeeply; -import static tools.vitruv.testutils.matchers.ModelMatchers.ignoringFeatures; -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; +import static tools.vitruv.change.testutils.matchers.ModelMatchers.equalsDeeply; +import static tools.vitruv.change.testutils.matchers.ModelMatchers.ignoringFeatures; +import static tools.vitruv.change.testutils.metamodels.AllElementTypesCreators.aet; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; @@ -39,8 +39,8 @@ import tools.vitruv.framework.views.ChangeableViewSource; import tools.vitruv.framework.views.ModifiableViewSelection; import tools.vitruv.framework.views.changederivation.DefaultStateBasedChangeResolutionStrategy; -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging; +import tools.vitruv.change.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.change.testutils.TestLogging; @ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) public class ChangeDerivingViewTest { diff --git a/views/src/test/java/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java b/views/src/test/java/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java index c08fa2557a..8d774a7941 100644 --- a/views/src/test/java/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java +++ b/views/src/test/java/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java @@ -9,9 +9,9 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; -import static tools.vitruv.testutils.matchers.ModelMatchers.equalsDeeply; -import static tools.vitruv.testutils.matchers.ModelMatchers.ignoringFeatures; -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; +import static tools.vitruv.change.testutils.matchers.ModelMatchers.equalsDeeply; +import static tools.vitruv.change.testutils.matchers.ModelMatchers.ignoringFeatures; +import static tools.vitruv.change.testutils.metamodels.AllElementTypesCreators.aet; import java.util.List; @@ -48,8 +48,8 @@ import tools.vitruv.change.composite.description.VitruviusChangeResolver; import tools.vitruv.framework.views.ChangeableViewSource; import tools.vitruv.framework.views.ModifiableViewSelection; -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging;; +import tools.vitruv.change.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.change.testutils.TestLogging;; @ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) public class ChangeRecordingViewTest { diff --git a/views/src/test/java/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java b/views/src/test/java/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java index fd65a9f53a..d26b5e7e26 100644 --- a/views/src/test/java/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java +++ b/views/src/test/java/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java @@ -15,8 +15,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static tools.vitruv.testutils.matchers.ModelMatchers.equalsDeeply; -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; +import static tools.vitruv.change.testutils.matchers.ModelMatchers.equalsDeeply; +import static tools.vitruv.change.testutils.metamodels.AllElementTypesCreators.aet; import java.util.List; import java.util.Set; @@ -54,8 +54,8 @@ import tools.vitruv.framework.views.ViewSelection; import tools.vitruv.framework.views.ViewType; import tools.vitruv.framework.views.selectors.DirectViewElementSelector; -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging; +import tools.vitruv.change.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.change.testutils.TestLogging; @ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) public class IdentityMappingViewTypeTest { diff --git a/views/src/test/java/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java b/views/src/test/java/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java index d6c4e06fd1..472f1a2259 100644 --- a/views/src/test/java/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java +++ b/views/src/test/java/tools/vitruv/framework/views/selection/ElementViewSelectionTest.java @@ -4,7 +4,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; +import static tools.vitruv.change.testutils.metamodels.AllElementTypesCreators.aet; import java.util.ArrayList; import java.util.List; @@ -19,8 +19,8 @@ import allElementTypes.Root; import tools.vitruv.framework.views.ModifiableViewSelection; -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging; +import tools.vitruv.change.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.change.testutils.TestLogging; @ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) public class ElementViewSelectionTest { diff --git a/views/src/test/java/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java b/views/src/test/java/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java index a099cef99d..b0c0138df6 100644 --- a/views/src/test/java/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java +++ b/views/src/test/java/tools/vitruv/framework/views/selectors/DirectViewElementSelectorTest.java @@ -6,7 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; +import static tools.vitruv.change.testutils.metamodels.AllElementTypesCreators.aet; import java.util.ArrayList; import java.util.List; @@ -27,8 +27,8 @@ import tools.vitruv.framework.views.ViewSelector; import tools.vitruv.framework.views.impl.ModifiableView; import tools.vitruv.framework.views.impl.ViewCreatingViewType; -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging; +import tools.vitruv.change.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.change.testutils.TestLogging; @ExtendWith({ TestLogging.class, RegisterMetamodelsInStandalone.class }) public class DirectViewElementSelectorTest { diff --git a/views/src/test/java/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java b/views/src/test/java/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java index b5ea0cf865..79d8c4622c 100644 --- a/views/src/test/java/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java +++ b/views/src/test/java/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java @@ -4,7 +4,7 @@ import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static tools.vitruv.testutils.metamodels.UmlMockupCreators.uml; +import static tools.vitruv.change.testutils.metamodels.UmlMockupCreators.uml; import java.io.File; import java.nio.file.Path; @@ -23,10 +23,10 @@ import edu.kit.ipd.sdq.commons.util.java.Pair; import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceCopier; -import tools.vitruv.testutils.RegisterMetamodelsInStandalone; -import tools.vitruv.testutils.TestLogging; -import tools.vitruv.testutils.TestProject; -import tools.vitruv.testutils.TestProjectManager; +import tools.vitruv.change.testutils.RegisterMetamodelsInStandalone; +import tools.vitruv.change.testutils.TestLogging; +import tools.vitruv.change.testutils.TestProject; +import tools.vitruv.change.testutils.TestProjectManager; import uml_mockup.Identified; import uml_mockup.UClass; import uml_mockup.UPackage; diff --git a/views/src/test/xtend/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend index 3175d61706..6ed3f83459 100644 --- a/views/src/test/xtend/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend +++ b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend @@ -12,17 +12,17 @@ import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribut import tools.vitruv.change.atomic.root.InsertRootEObject import tools.vitruv.change.atomic.root.RemoveRootEObject import tools.vitruv.change.composite.description.VitruviusChangeResolver -import tools.vitruv.testutils.Capture +import tools.vitruv.change.testutils.Capture import static org.hamcrest.CoreMatchers.instanceOf import static org.hamcrest.MatcherAssert.assertThat import static org.junit.jupiter.api.Assertions.assertEquals import static org.junit.jupiter.api.Assertions.assertTrue -import static tools.vitruv.testutils.matchers.ModelMatchers.containsModelOf -import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet +import static tools.vitruv.change.testutils.matchers.ModelMatchers.containsModelOf +import static tools.vitruv.change.testutils.metamodels.AllElementTypesCreators.aet import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories -import static extension tools.vitruv.testutils.Capture.operator_doubleGreaterThan +import static extension tools.vitruv.change.testutils.Capture.operator_doubleGreaterThan class BasicStateChangePropagationTest extends StateChangePropagationTest { private def getTestUri() { diff --git a/views/src/test/xtend/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend index 64ac2d08d9..df3d13711b 100644 --- a/views/src/test/xtend/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend +++ b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/PcmStateChangeTest.xtend @@ -3,7 +3,7 @@ package tools.vitruv.framework.views.changederivation import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource -import static tools.vitruv.testutils.metamodels.PcmMockupCreators.pcm +import static tools.vitruv.change.testutils.metamodels.PcmMockupCreators.pcm class PcmStateChangeTest extends StateChangePropagationTest { @ParameterizedTest() diff --git a/views/src/test/xtend/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend index a30d64e748..7a6a388cdb 100644 --- a/views/src/test/xtend/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend +++ b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend @@ -19,15 +19,15 @@ import pcm_mockup.Repository import tools.vitruv.change.composite.description.VitruviusChange import tools.vitruv.change.composite.description.VitruviusChangeResolver import tools.vitruv.change.composite.recording.ChangeRecorder -import tools.vitruv.testutils.RegisterMetamodelsInStandalone -import tools.vitruv.testutils.TestLogging -import tools.vitruv.testutils.TestProject -import tools.vitruv.testutils.TestProjectManager +import tools.vitruv.change.testutils.RegisterMetamodelsInStandalone +import tools.vitruv.change.testutils.TestLogging +import tools.vitruv.change.testutils.TestProject +import tools.vitruv.change.testutils.TestProjectManager import uml_mockup.UPackage import static org.junit.jupiter.api.Assertions.* -import static tools.vitruv.testutils.metamodels.PcmMockupCreators.pcm -import static tools.vitruv.testutils.metamodels.UmlMockupCreators.uml +import static tools.vitruv.change.testutils.metamodels.PcmMockupCreators.pcm +import static tools.vitruv.change.testutils.metamodels.UmlMockupCreators.uml import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.common.util.URIUtil.createFileURI import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories diff --git a/views/src/test/xtend/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend index c21e11d991..866b365653 100644 --- a/views/src/test/xtend/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend +++ b/views/src/test/xtend/tools/vitruv/framework/views/changederivation/UmlStateChangeTest.xtend @@ -5,7 +5,7 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.EnumSource import org.junit.jupiter.params.provider.MethodSource -import static tools.vitruv.testutils.metamodels.UmlMockupCreators.uml +import static tools.vitruv.change.testutils.metamodels.UmlMockupCreators.uml class UmlStateChangeTest extends StateChangePropagationTest { @ParameterizedTest() diff --git a/vsum/pom.xml b/vsum/pom.xml new file mode 100644 index 0000000000..ea8ce8d977 --- /dev/null +++ b/vsum/pom.xml @@ -0,0 +1,137 @@ + + + + 4.0.0 + + + tools.vitruv + tools.vitruv.framework + 3.0.1-SNAPSHOT + + + tools.vitruv.framework.vsum + + Vitruv V-SUM definition + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + org.eclipse.xtend + xtend-maven-plugin + + + + + + + + ${project.groupId} + tools.vitruv.change.utils + ${project.version} + compile + + + ${project.groupId} + tools.vitruv.change.composite + ${project.version} + + + ${project.groupId} + tools.vitruv.change.propagation + ${project.version} + + + ${project.groupId} + tools.vitruv.change.correspondence + ${project.version} + + + ${project.groupId} + tools.vitruv.change.interaction + ${project.version} + + + ${project.groupId} + tools.vitruv.change.atomic + ${project.version} + + + ${project.groupId} + tools.vitruv.framework.views + ${project.version} + + + + + ${project.groupId} + tools.vitruv.change.testutils.metamodels + ${project.version} + test + + + ${project.groupId} + tools.vitruv.change.testutils.core + ${project.version} + test + + + + + com.google.guava + guava + + + org.eclipse.emf + org.eclipse.emf.common + + + org.eclipse.emf + org.eclipse.emf.ecore + + + org.eclipse.xtend + org.eclipse.xtend.lib + + + log4j + log4j + + + sdq-commons + edu.kit.ipd.sdq.commons.util.emf + + + org.eclipse.xtext + org.eclipse.xtext.xbase.lib + + + + + org.hamcrest + hamcrest + 2.2 + test + + + xannotations + edu.kit.ipd.sdq.activextendannotations + test + + + org.junit.jupiter + junit-jupiter-api + test + + + sdq-commons + edu.kit.ipd.sdq.commons.util.java + test + + + \ No newline at end of file diff --git a/vsum/src/main/java/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java b/vsum/src/main/java/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java new file mode 100644 index 0000000000..27a1b8d79a --- /dev/null +++ b/vsum/src/main/java/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java @@ -0,0 +1,215 @@ +package tools.vitruv.framework.vsum.internal; + +import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.getOrCreateResource; +import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.loadOrCreateResource; +import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories; +import static tools.vitruv.change.correspondence.model.CorrespondenceModelFactory.createPersistableCorrespondenceModel; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.log4j.Logger; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; + +import tools.vitruv.change.atomic.uuid.Uuid; +import tools.vitruv.change.atomic.uuid.UuidResolver; +import tools.vitruv.change.composite.description.TransactionalChange; +import tools.vitruv.change.composite.description.VitruviusChange; +import tools.vitruv.change.composite.description.VitruviusChangeResolver; +import tools.vitruv.change.composite.recording.ChangeRecorder; +import tools.vitruv.change.correspondence.Correspondence; +import tools.vitruv.change.correspondence.model.PersistableCorrespondenceModel; +import tools.vitruv.change.correspondence.view.CorrespondenceModelViewFactory; +import tools.vitruv.change.correspondence.view.EditableCorrespondenceModelView; +import tools.vitruv.change.propagation.impl.ResourceRegistrationAdapter; +import tools.vitruv.framework.vsum.helper.VsumFileSystemLayout; + +class ResourceRepositoryImpl implements ModelRepository { + private static final Logger LOGGER = Logger.getLogger(ResourceRepositoryImpl.class); + + private final ResourceSet modelsResourceSet = withGlobalFactories(new ResourceSetImpl()); + private final Map modelInstances = new HashMap<>(); + private final PersistableCorrespondenceModel correspondenceModel; + private UuidResolver uuidResolver = UuidResolver.create(modelsResourceSet); + private final ChangeRecorder changeRecorder = new ChangeRecorder(modelsResourceSet); + private final VitruviusChangeResolver changeResolver = VitruviusChangeResolver.forUuids(uuidResolver); + + private final VsumFileSystemLayout fileSystemLayout; + + private boolean isRecording = false; + private boolean isLoading = false; + + ResourceRepositoryImpl(VsumFileSystemLayout fileSystemLayout) { + this.fileSystemLayout = fileSystemLayout; + this.correspondenceModel = createPersistableCorrespondenceModel(fileSystemLayout.getCorrespondencesURI()); + modelsResourceSet.eAdapters() + .add(new ResourceRegistrationAdapter(resource -> getCreateOrLoadModelUnlessLoading(resource.getURI()))); + } + + @Override + public void loadExistingModels() { + isLoading = true; + try { + readModelsFile(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + correspondenceModel.loadSerializedCorrespondences(modelsResourceSet); + isLoading = false; + } + + private void writeModelsFile() throws IOException { + Files.write(fileSystemLayout.getModelsNamesFilesPath(), modelsResourceSet.getResources().stream() + .map(Resource::getURI).map(URI::toString).toList()); + } + + private void readModelsFile() throws IOException { + List modelUris; + try { + modelUris = Files.readAllLines(fileSystemLayout.getModelsNamesFilesPath()).stream().map(URI::createURI) + .toList(); + + } catch (NoSuchFileException e) { + // There are no existing models, so don't do anything + return; + } + modelUris.forEach(uri -> loadOrCreateResource(modelsResourceSet, uri)); + uuidResolver.loadFromUri(fileSystemLayout.getUuidsURI()); + modelUris.forEach(this::createOrLoadModel); + } + + @Override + public EditableCorrespondenceModelView getCorrespondenceModel() { + return CorrespondenceModelViewFactory.createEditableCorrespondenceModelView(correspondenceModel); + } + + @Override + public ModelInstance getModel(URI modelUri) { + return modelInstances.get(modelUri); + } + + @Override + public UuidResolver getUuidResolver() { + return uuidResolver; + } + + private ModelInstance getCreateOrLoadModelUnlessLoading(URI modelUri) { + if (isLoading) { + return null; + } + return getCreateOrLoadModel(modelUri); + } + + private ModelInstance getCreateOrLoadModel(URI modelUri) { + ModelInstance instance = getModel(modelUri); + if (instance != null) { + return instance; + } + return createOrLoadModel(modelUri); + } + + private ModelInstance createOrLoadModel(URI modelUri) { + Resource resource; + if (modelUri.isFile() || modelUri.isPlatform()) { + resource = getOrCreateResource(modelsResourceSet, modelUri); + } else { + resource = loadOrCreateResource(modelsResourceSet, modelUri); + } + ModelInstance modelInstance = new ModelInstance(resource); + modelInstances.put(modelUri, modelInstance); + registerRecorder(modelInstance); + return modelInstance; + } + + private void registerRecorder(ModelInstance modelInstance) { + // Only monitor modifiable models (file / platform URIs, not pathmap URIs) + if (modelInstance.getURI().isFile() || modelInstance.getURI().isPlatform()) { + changeRecorder.addToRecording(modelInstance.getResource()); + if (isRecording && !changeRecorder.isRecording()) { + changeRecorder.beginRecording(); + } + } + } + + @Override + public void persistAsRoot(EObject rootObject, URI uri) { + getCreateOrLoadModel(uri).addRoot(rootObject); + } + + @Override + public void saveOrDeleteModels() { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Saving all models of model repository for VSUM " + fileSystemLayout); + } + Iterator> modelInstancesIterator = modelInstances.entrySet().iterator(); + while (modelInstancesIterator.hasNext()) { + ModelInstance modelInstance = modelInstancesIterator.next().getValue(); + if (modelInstance.isEmpty()) { + modelInstance.delete(); + modelInstancesIterator.remove(); + } else { + modelInstance.save(); + } + } + correspondenceModel.save(); + try { + writeModelsFile(); + uuidResolver.storeAtUri(fileSystemLayout.getUuidsURI()); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public Iterable> recordChanges(Runnable changeApplicator) { + changeRecorder.beginRecording(); + isRecording = true; + LOGGER.debug("Start recording virtual model"); + changeApplicator.run(); + LOGGER.debug("End recording virtual model"); + isRecording = false; + changeRecorder.endRecording(); + TransactionalChange change = changeRecorder.getChange(); + changeResolver.assignIds(change); + return change.containsConcreteChange() ? List.of(change) : List.of(); + } + + @Override + public VitruviusChange applyChange(VitruviusChange change) { + return changeResolver.resolveAndApply(change); + } + + @Override + public URI getMetadataModelURI(String... metadataKey) { + return fileSystemLayout.getConsistencyMetadataModelURI(metadataKey); + } + + @Override + public Resource getModelResource(URI uri) { + return getCreateOrLoadModel(uri).getResource(); + } + + @Override + public Collection getModelResources() { + return modelsResourceSet.getResources(); + } + + @Override + public void close() { + changeRecorder.close(); + modelsResourceSet.getResources().forEach(Resource::unload); + modelsResourceSet.getResources().clear(); + uuidResolver = null; + } +} diff --git a/vsum/src/main/java/tools/vitruv/framework/vsum/internal/VirtualModelImpl.java b/vsum/src/main/java/tools/vitruv/framework/vsum/internal/VirtualModelImpl.java new file mode 100644 index 0000000000..0f09d7d5a5 --- /dev/null +++ b/vsum/src/main/java/tools/vitruv/framework/vsum/internal/VirtualModelImpl.java @@ -0,0 +1,183 @@ +package tools.vitruv.framework.vsum.internal; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import org.apache.log4j.Logger; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; + +import tools.vitruv.change.atomic.uuid.Uuid; +import tools.vitruv.change.atomic.uuid.UuidResolver; +import tools.vitruv.change.composite.description.PropagatedChange; +import tools.vitruv.change.composite.description.VitruviusChange; +import tools.vitruv.change.composite.propagation.ChangePropagationListener; +import tools.vitruv.change.correspondence.Correspondence; +import tools.vitruv.change.correspondence.view.EditableCorrespondenceModelView; +import tools.vitruv.change.interaction.InternalUserInteractor; +import tools.vitruv.change.propagation.ChangePropagationMode; +import tools.vitruv.change.propagation.ChangePropagationSpecificationProvider; +import tools.vitruv.change.propagation.impl.ChangePropagator; +import tools.vitruv.framework.views.ViewSelector; +import tools.vitruv.framework.views.ViewType; +import tools.vitruv.framework.views.ViewTypeProvider; +import tools.vitruv.framework.views.ViewTypeRepository; +import tools.vitruv.framework.vsum.helper.VsumFileSystemLayout; + +public class VirtualModelImpl implements InternalVirtualModel { + private static final Logger LOGGER = Logger.getLogger(VirtualModelImpl.class); + + private final ModelRepository resourceRepository; + private final ViewTypeProvider viewTypeRepository; + private final VsumFileSystemLayout fileSystemLayout; + private final List changePropagationListeners = new LinkedList<>(); + + private final ChangePropagationSpecificationProvider changePropagationSpecificationProvider; + private final InternalUserInteractor userInteractor; + private ChangePropagationMode changePropagationMode = ChangePropagationMode.TRANSITIVE_CYCLIC; + + public VirtualModelImpl(VsumFileSystemLayout fileSystemLayout, InternalUserInteractor userInteractor, + ViewTypeRepository viewTypeRepository, + ChangePropagationSpecificationProvider changePropagationSpecificationProvider) { + this.fileSystemLayout = fileSystemLayout; + this.viewTypeRepository = viewTypeRepository; + resourceRepository = new ResourceRepositoryImpl(fileSystemLayout); + + this.changePropagationSpecificationProvider = changePropagationSpecificationProvider; + this.userInteractor = userInteractor; + } + + public void loadExistingModels() { + resourceRepository.loadExistingModels(); + } + + @Override + public synchronized EditableCorrespondenceModelView getCorrespondenceModel() { + return resourceRepository.getCorrespondenceModel(); + } + + @Override + public synchronized ModelInstance getModelInstance(URI modelUri) { + return resourceRepository.getModel(modelUri); + } + + @Override + public UuidResolver getUuidResolver() { + return resourceRepository.getUuidResolver(); + } + + private synchronized void save() { + resourceRepository.saveOrDeleteModels(); + } + + @Override + public synchronized List propagateChange(VitruviusChange change) { + checkNotNull(change, "change to propagate"); + checkArgument(change.containsConcreteChange(), "This change contains no concrete change:%s%s", + System.lineSeparator(), change); + + LOGGER.info("Starting change propagation"); + startChangePropagation(change); + + ChangePropagator changePropagator = new ChangePropagator(resourceRepository, + changePropagationSpecificationProvider, userInteractor, changePropagationMode); + List result = changePropagator.propagateChange(change); + save(); + + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Propagated changes: " + result); + } + + finishChangePropagation(change, result); + LOGGER.info("Finished change propagation"); + return result; + } + + private void startChangePropagation(VitruviusChange change) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Started synchronizing change: " + change); + } + changePropagationListeners.stream().forEach(it -> it.startedChangePropagation(change)); + } + + private void finishChangePropagation(VitruviusChange inputChange, Iterable generatedChanges) { + changePropagationListeners.stream().forEach(it -> it.finishedChangePropagation(generatedChanges)); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Finished synchronizing change: " + inputChange); + } + } + + @Override + public Path getFolder() { + return fileSystemLayout.getVsumProjectFolder(); + } + + /** + * Registers the given {@link ChangePropagationListener}. The listener must not + * be null. + */ + @Override + public synchronized void addChangePropagationListener(ChangePropagationListener propagationListener) { + this.changePropagationListeners.add(checkNotNull(propagationListener, "propagationListener")); + } + + /** + * Unregisters the given {@link ChangePropagationListener}. The listener must + * not be null. + */ + @Override + public synchronized void removeChangePropagationListener(ChangePropagationListener propagationListener) { + this.changePropagationListeners.remove(checkNotNull(propagationListener, "propagationListener")); + } + + /** + * Returns the name of the virtual model. + * + * @return The name of the virtual model + */ + public String getName() { + return getFolder().getFileName().toString(); + } + + @Override + public void dispose() { + try { + resourceRepository.close(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + VirtualModelRegistry.getInstance().deregisterVirtualModel(this); + } + + @Override + public Collection getViewSourceModels() { + return resourceRepository.getModelResources(); + } + + @Override + public Collection> getViewTypes() { + return viewTypeRepository.getViewTypes(); + } + + @Override + public S createSelector(ViewType viewType) { + /* + * Note that ViewType.createSelector() accepts a ChangeableViewSource, which + * VirtualModelImpl implements but not its publicly used interface VitualModel. + * Thus calling viewType.createSelector(virtualModel) with virtualModel having + * the static type VirtualModel is not possible, i.e., this method hides + * implementation details and is not a convenience method. + */ + return viewType.createSelector(this); + } + + @Override + public void setChangePropagationMode(ChangePropagationMode changePropagationMode) { + this.changePropagationMode = changePropagationMode; + } +} diff --git a/vsum/src/main/java/tools/vitruv/framework/vsum/internal/VirtualModelRegistry.java b/vsum/src/main/java/tools/vitruv/framework/vsum/internal/VirtualModelRegistry.java new file mode 100644 index 0000000000..14298f46bc --- /dev/null +++ b/vsum/src/main/java/tools/vitruv/framework/vsum/internal/VirtualModelRegistry.java @@ -0,0 +1,30 @@ +package tools.vitruv.framework.vsum.internal; + +import java.nio.file.Path; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class VirtualModelRegistry { + private static VirtualModelRegistry instance = new VirtualModelRegistry(); + + private Map folderToVirtualModelMap = new ConcurrentHashMap<>(); + + private VirtualModelRegistry() { + } + + public static VirtualModelRegistry getInstance() { + return instance; + } + + public InternalVirtualModel getVirtualModel(Path folder) { + return folderToVirtualModelMap.get(folder); + } + + public void registerVirtualModel(InternalVirtualModel model) { + folderToVirtualModelMap.put(model.getFolder(), model); + } + + public void deregisterVirtualModel(InternalVirtualModel model) { + folderToVirtualModelMap.remove(model.getFolder()); + } +} diff --git a/vsum/src/main/xtend/tools/vitruv/framework/vsum/VirtualModel.xtend b/vsum/src/main/xtend/tools/vitruv/framework/vsum/VirtualModel.xtend new file mode 100644 index 0000000000..3775c2f399 --- /dev/null +++ b/vsum/src/main/xtend/tools/vitruv/framework/vsum/VirtualModel.xtend @@ -0,0 +1,18 @@ +package tools.vitruv.framework.vsum + +import java.nio.file.Path +import tools.vitruv.change.composite.propagation.ChangeableModelRepository +import tools.vitruv.framework.views.ViewProvider +import tools.vitruv.framework.views.ViewTypeProvider +import tools.vitruv.change.propagation.ChangePropagationMode + +interface VirtualModel extends ChangeableModelRepository, ViewProvider, ViewTypeProvider { + def Path getFolder() + + /** + * Defines how changes are propagated when passed to {@link #propagateChange(VitruviusChange)}. + * By default, {@link ChangePropagationMode#TRANSITIVE_CYCLIC} is used, i.e., changes are + * transitively propagated until no further changes are produced. + */ + def void setChangePropagationMode(ChangePropagationMode changePropagationMode) +} diff --git a/vsum/src/main/xtend/tools/vitruv/framework/vsum/VirtualModelBuilder.xtend b/vsum/src/main/xtend/tools/vitruv/framework/vsum/VirtualModelBuilder.xtend new file mode 100644 index 0000000000..773ea2ab64 --- /dev/null +++ b/vsum/src/main/xtend/tools/vitruv/framework/vsum/VirtualModelBuilder.xtend @@ -0,0 +1,107 @@ +package tools.vitruv.framework.vsum + +import java.io.File +import java.nio.file.Path +import java.util.Collection +import java.util.HashSet +import java.util.Set +import tools.vitruv.change.propagation.ChangePropagationSpecification +import tools.vitruv.change.propagation.ChangePropagationSpecificationRepository +import tools.vitruv.change.interaction.InteractionResultProvider +import tools.vitruv.change.interaction.InternalUserInteractor +import tools.vitruv.change.interaction.UserInteractionFactory +import tools.vitruv.framework.views.ViewType +import tools.vitruv.framework.views.ViewTypeRepository +import tools.vitruv.framework.vsum.helper.VsumFileSystemLayout +import tools.vitruv.framework.vsum.internal.InternalVirtualModel +import tools.vitruv.framework.vsum.internal.VirtualModelImpl +import tools.vitruv.change.utils.ProjectMarker + +import static com.google.common.base.Preconditions.checkState + +class VirtualModelBuilder { + val Set> viewTypes = new HashSet() + val Set changePropagationSpecifications = new HashSet() + var Path storageFolder + var InternalUserInteractor userInteractor + + def VirtualModelBuilder withStorageFolder(File folder) { + withStorageFolder(folder.toPath) + } + + def VirtualModelBuilder withStorageFolder(Path folder) { + checkState(storageFolder === null || storageFolder == folder, "There is already another storage folder set: %s", storageFolder) + storageFolder = folder + return this + } + + def VirtualModelBuilder withUserInteractorForResultProvider(InteractionResultProvider resultProvider) { + withUserInteractor(UserInteractionFactory.instance.createUserInteractor(resultProvider)) + } + + def VirtualModelBuilder withUserInteractor(InternalUserInteractor userInteractor) { + checkState(this.userInteractor === null || this.userInteractor == userInteractor, + "There is already another user interactor set: %s", this.userInteractor) + this.userInteractor = userInteractor + return this + } + + def VirtualModelBuilder withViewType(ViewType viewType) { + viewTypes += viewType + return this + } + + def VirtualModelBuilder withViewTypes(Collection> viewTypes) { + for (viewType : viewTypes) withViewType(viewType) + return this + } + + def VirtualModelBuilder withChangePropagationSpecifications(ChangePropagationSpecification... changePropagationSpecifications) { + for(spec : changePropagationSpecifications) withChangePropagationSpecification(spec) + return this + } + + def VirtualModelBuilder withChangePropagationSpecifications(Iterable changePropagationSpecifications) { + for(spec : changePropagationSpecifications) withChangePropagationSpecification(spec) + return this + } + + def VirtualModelBuilder withChangePropagationSpecification(ChangePropagationSpecification changePropagationSpecification) { + if (changePropagationSpecifications.contains(changePropagationSpecification)) return this + + for (existingPropagationSpecification : changePropagationSpecifications) { + if(existingPropagationSpecification == changePropagationSpecification) return this + + if (existingPropagationSpecification.sourceMetamodelDescriptor.equals( + changePropagationSpecification.sourceMetamodelDescriptor) && + existingPropagationSpecification.targetMetamodelDescriptor.equals( + changePropagationSpecification.targetMetamodelDescriptor)) { + throw new IllegalArgumentException( + '''This virtual model configuration already contains the change propagation specification «existingPropagationSpecification» between «existingPropagationSpecification.sourceMetamodelDescriptor» and «existingPropagationSpecification.targetMetamodelDescriptor»!''' + ) + } + } + + changePropagationSpecifications += changePropagationSpecification + return this + } + + def InternalVirtualModel buildAndInitialize() { + checkState(storageFolder !== null, "No storage folder was configured!") + checkState(userInteractor !== null, "No user interactor was configured!") + val viewTypeRepository = new ViewTypeRepository() + viewTypes.forEach[viewTypeRepository.register(it)] + val changeSpecificationRepository = new ChangePropagationSpecificationRepository(changePropagationSpecifications) + + val fileSystemLayout = new VsumFileSystemLayout(storageFolder) + fileSystemLayout.prepare() + val vsum = new VirtualModelImpl(fileSystemLayout, userInteractor, viewTypeRepository, changeSpecificationRepository) + vsum.loadExistingModels() + try { + ProjectMarker.getProjectRootFolder(storageFolder) + } catch (IllegalStateException exception) { + ProjectMarker.markAsProjectRootFolder(storageFolder) + } + return vsum + } +} \ No newline at end of file diff --git a/vsum/src/main/xtend/tools/vitruv/framework/vsum/VirtualModelManager.xtend b/vsum/src/main/xtend/tools/vitruv/framework/vsum/VirtualModelManager.xtend new file mode 100644 index 0000000000..66d81c6325 --- /dev/null +++ b/vsum/src/main/xtend/tools/vitruv/framework/vsum/VirtualModelManager.xtend @@ -0,0 +1,32 @@ +package tools.vitruv.framework.vsum + +import java.nio.file.Path +import tools.vitruv.framework.vsum.internal.VirtualModelRegistry +import tools.vitruv.framework.vsum.internal.InternalVirtualModel + +final class VirtualModelManager { + static val instance = new VirtualModelManager(); + + private new() { + } + + static def getInstance() { + return instance; + } + + def InternalVirtualModel getVirtualModel(Path folder) { + var virtualModel = VirtualModelRegistry.instance.getVirtualModel(folder) + if (virtualModel === null) { + // get the workspace root +// val root = ResourcesPlugin.getWorkspace().getRoot(); +// // get the project handle +// val project = root.getProject(name); +// // open up this newly-created project in Eclipse +// project.open(new NullProgressMonitor()); +// // TODO HK: Extract VSUM from project + throw new UnsupportedOperationException("Virtual models cannot be loaded yet"); + } + return virtualModel + } + +} diff --git a/vsum/src/main/xtend/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend b/vsum/src/main/xtend/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend new file mode 100644 index 0000000000..06f682af3f --- /dev/null +++ b/vsum/src/main/xtend/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend @@ -0,0 +1,103 @@ +package tools.vitruv.framework.vsum.helper + +import java.io.IOException +import java.net.URLEncoder +import java.nio.file.Path + +import static com.google.common.base.Preconditions.checkArgument +import static com.google.common.base.Preconditions.checkState +import static java.nio.charset.StandardCharsets.UTF_8 +import static java.nio.file.Files.createDirectories +import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.common.util.URIUtil.createFileURI +import org.eclipse.emf.common.util.URI + +class VsumFileSystemLayout { + static final String CORRESPONDENCES_FILE = "correspondences.correspondence"; + static final String UUIDS_FILE = "uuid.uuid"; + static final String MODELS_FILE = "models.models"; + static final String VSUM_FOLDER_NAME = "vsum"; + static final String CONSISTENCY_METADATA_FOLDER_NAME = "consistencymetadata"; + + val Path vsumProjectFolder + var prepared = false + + new(Path vsumProjectFolder) { + this.vsumProjectFolder = vsumProjectFolder + } + + def void prepare() throws IOException { + createDirectories(vsumFolder) + createDirectories(consistencyMetadataFolder) + prepared = true + } + + def private Path getMetadataFilePath(String... metadataKey) { + checkArgument(metadataKey !== null || metadataKey.length > 0, "The key must have at least one part!") + checkArgument(metadataKey.get(metadataKey.length - 1).contains('.'), "metadataKey is missing a file extension!") + + return metadataKey.fold(Path.of("")) [ last, keyPart | + checkArgument(keyPart !== null, "A key part must not be null!") + // URL-encoding the string makes it save for being a file part, + // except for the cases '', '.' and '..' + // we thus use _ as an escape character + // This also ensures that the resulting path is always located + // within the metadata folder. + val preparedKeyPart = switch (keyPart) { + case ".": "_." + case "..": "_._." + case "": "_" + default: keyPart.replaceAll("_", "__") + } + last.resolve(URLEncoder.encode(preparedKeyPart, UTF_8)) + ] + } + + /** + * Gets the {@link URI} of a model that stores metadata. + * @param metadataKeyThe key uniquely identifying the metadata model. The different parts of the key + * can be used to convey some sort of hierarchy in the metadata. The key may contain + * arbitrary characters. The last key part contains the metadata model's file name + * and extension. + * @return the URI of the specified metadata model + */ + def URI getConsistencyMetadataModelURI(String... metadataKey) { + checkPrepared() + var metadataPath = consistencyMetadataFolder.resolve(getMetadataFilePath(metadataKey)) + return metadataPath.toFile.createFileURI() + } + + def URI getCorrespondencesURI() { + checkPrepared() + return vsumFolder.resolve(CORRESPONDENCES_FILE).toFile.createFileURI() + } + + def URI getUuidsURI() { + checkPrepared() + return vsumFolder.resolve(UUIDS_FILE).toFile.createFileURI() + } + + def Path getModelsNamesFilesPath() { + checkPrepared() + return vsumFolder.resolve(MODELS_FILE) + } + + def Path getVsumProjectFolder() { + return this.vsumProjectFolder + } + + def private getVsumFolder() { + vsumProjectFolder.resolve(VSUM_FOLDER_NAME) + } + + def private Path getConsistencyMetadataFolder() { + vsumProjectFolder.resolve(CONSISTENCY_METADATA_FOLDER_NAME) + } + + override String toString() { + return '''@«vsumProjectFolder»''' + } + + def private void checkPrepared() { + checkState(prepared, "The file system layout has not been loaded yet!") + } +} diff --git a/vsum/src/main/xtend/tools/vitruv/framework/vsum/internal/InternalVirtualModel.xtend b/vsum/src/main/xtend/tools/vitruv/framework/vsum/internal/InternalVirtualModel.xtend new file mode 100644 index 0000000000..0b3e6a6f8e --- /dev/null +++ b/vsum/src/main/xtend/tools/vitruv/framework/vsum/internal/InternalVirtualModel.xtend @@ -0,0 +1,15 @@ +package tools.vitruv.framework.vsum.internal + +import org.eclipse.emf.common.util.URI +import tools.vitruv.change.correspondence.Correspondence +import tools.vitruv.change.correspondence.view.EditableCorrespondenceModelView +import tools.vitruv.framework.views.ChangeableViewSource +import tools.vitruv.framework.vsum.VirtualModel + +interface InternalVirtualModel extends VirtualModel, ChangeableViewSource { + def EditableCorrespondenceModelView getCorrespondenceModel() + + def ModelInstance getModelInstance(URI modelUri) + + def void dispose() +} diff --git a/vsum/src/main/xtend/tools/vitruv/framework/vsum/internal/ModelInstance.xtend b/vsum/src/main/xtend/tools/vitruv/framework/vsum/internal/ModelInstance.xtend new file mode 100644 index 0000000000..0475e803c0 --- /dev/null +++ b/vsum/src/main/xtend/tools/vitruv/framework/vsum/internal/ModelInstance.xtend @@ -0,0 +1,65 @@ +package tools.vitruv.framework.vsum.internal + +import java.io.IOException +import org.apache.log4j.Logger +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.xtend.lib.annotations.Accessors + +import static com.google.common.base.Preconditions.checkArgument + +class ModelInstance { + static val LOGGER = Logger.getLogger(ModelInstance) + @Accessors(PUBLIC_GETTER) + Resource resource + + new(Resource resource) { + checkArgument(resource !== null, "cannot create a model instance for a null resource") + this.resource = resource + LOGGER.debug('''Create model instance for resource with URI: «URI»''') + } + + def URI getURI() { + return resource.URI + } + + def void addRoot(EObject root) { + resource.contents += root + resource.modified = true + LOGGER.debug('''Add root to resource: «resource»''') + } + + def void markModified() { + resource.modified = true + } + + def boolean isEmpty() { + resource.contents.isEmpty + } + + def void save() { + if (!resource.modified) { + return + } + LOGGER.debug('''Save resource: «resource»''') + try { + resource.save(null) + resource.modified = false + } catch (IOException e) { + LOGGER.error('''Model could not be saved: «URI»''', e) + throw new IllegalStateException('''Could not save URI «URI»''', e) + } + } + + def void delete() { + LOGGER.debug('''Delete resource: «resource»''') + try { + resource.delete(null) + } catch (IOException e) { + LOGGER.error('''Deletion of resource «resource» did not work.''', e) + throw new IllegalStateException('''Could not delete URI «URI»''', e) + } + } + +} diff --git a/vsum/src/main/xtend/tools/vitruv/framework/vsum/internal/ModelRepository.xtend b/vsum/src/main/xtend/tools/vitruv/framework/vsum/internal/ModelRepository.xtend new file mode 100644 index 0000000000..dc664e44c2 --- /dev/null +++ b/vsum/src/main/xtend/tools/vitruv/framework/vsum/internal/ModelRepository.xtend @@ -0,0 +1,20 @@ +package tools.vitruv.framework.vsum.internal + +import java.util.Collection +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.ecore.resource.Resource +import tools.vitruv.change.propagation.ChangeRecordingModelRepository + +package interface ModelRepository extends ChangeRecordingModelRepository { + /** + * Returns the model at the given {@link URI} if it was already loaded to or created in + * the repository. Returns null otherwise. + */ + def ModelInstance getModel(URI modelUri) + + def void loadExistingModels() + + def void saveOrDeleteModels() + + def Collection getModelResources() +} diff --git a/vsum/src/test/xtend/tools/vitruv/framework/vsum/VirtualModelTest.xtend b/vsum/src/test/xtend/tools/vitruv/framework/vsum/VirtualModelTest.xtend new file mode 100644 index 0000000000..e6b7088681 --- /dev/null +++ b/vsum/src/test/xtend/tools/vitruv/framework/vsum/VirtualModelTest.xtend @@ -0,0 +1,396 @@ +package tools.vitruv.framework.vsum + +import allElementTypes.Root +import java.nio.file.Path +import java.util.HashSet +import org.eclipse.emf.ecore.resource.ResourceSet +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.^extension.ExtendWith +import tools.vitruv.change.atomic.eobject.CreateEObject +import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute +import tools.vitruv.change.atomic.feature.reference.ReplaceSingleValuedEReference +import tools.vitruv.change.atomic.root.InsertRootEObject +import tools.vitruv.change.atomic.uuid.UuidResolver +import tools.vitruv.change.composite.description.VitruviusChangeResolver +import tools.vitruv.change.composite.recording.ChangeRecorder +import tools.vitruv.framework.views.View +import tools.vitruv.framework.views.ViewTypeFactory +import tools.vitruv.framework.vsum.VirtualModelTestUtil.RedundancyChangePropagationSpecification +import tools.vitruv.change.testutils.TestProject +import tools.vitruv.change.testutils.TestProjectManager + +import static org.hamcrest.CoreMatchers.* +import static org.hamcrest.MatcherAssert.assertThat +import static org.junit.jupiter.api.Assertions.assertEquals +import static org.junit.jupiter.api.Assertions.assertNotEquals +import static org.junit.jupiter.api.Assertions.assertNull +import static tools.vitruv.change.testutils.matchers.ModelMatchers.containsModelOf +import static tools.vitruv.change.testutils.metamodels.AllElementTypesCreators.aet + +import static extension com.google.common.base.Preconditions.checkNotNull +import static extension edu.kit.ipd.sdq.commons.util.java.lang.IterableUtil.claimOne +import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories +import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceUtil.getFirstRootEObject +import static extension tools.vitruv.framework.vsum.VirtualModelTestUtil.* + +@ExtendWith(TestProjectManager) +class VirtualModelTest { + static val String NON_ROOT_ID = "NonRootId" + static val String ROOT_ID = "RootId" + + var Path projectFolder + + @BeforeEach + def void initializeProjectFolder(@TestProject Path projectFolder) { + this.projectFolder = projectFolder + } + + @Test + @DisplayName("propagate a simple change into a virtual model") + def void propagateIntoVirtualModel() { + val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) + val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet) + changeRecorder.addToRecording(resourceSet) + changeRecorder.beginRecording + val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ + contents += aet.Root => [ + id = 'root' + ] + ] + val recordedChange = changeRecorder.endRecording(uuidResolver) + virtualModel.propagateChange(recordedChange) + val vsumModel = virtualModel.getModelInstance(createTestModelResourceUri("")) + assertThat(vsumModel.resource, containsModelOf(monitoredResource)) + } + + @Test + @DisplayName("propagate a simple change into a virtual model and preserve consistency") + def void propagateIntoVirtualModelWithConsistency() { + val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) + val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet) + changeRecorder.addToRecording(resourceSet) + changeRecorder.beginRecording + val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ + contents += aet.Root => [ + id = 'root' + ] + ] + val recordedChange = changeRecorder.endRecording(uuidResolver) + virtualModel.propagateChange(recordedChange) + val sorceModel = virtualModel.getModelInstance(createTestModelResourceUri("")) + val targetModel = virtualModel.getModelInstance( + RedundancyChangePropagationSpecification.getTargetResourceUri(createTestModelResourceUri(""))) + assertThat(targetModel.resource, containsModelOf(monitoredResource)) + assertEquals(1, + virtualModel.correspondenceModel.getCorrespondingEObjects(sorceModel.resource.contents.get(0)).size) + } + + @Test + @DisplayName("persist element as resource root also contained in other persisted element") + def void singleChangeForRootElementInMultipleResource() { + val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) + val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet) + changeRecorder.addToRecording(resourceSet) + changeRecorder.beginRecording + val containedRoot = aet.Root + resourceSet.createResource(createTestModelResourceUri("")) => [ + contents += aet.Root => [ + id = 'root' + recursiveRoot = containedRoot => [ + id = 'containedRoot' + ] + ] + ] + resourceSet.createResource(createTestModelResourceUri("Contained")) => [ + contents += containedRoot + ] + val recordedChange = changeRecorder.endRecording(uuidResolver) + val propagatedChanges = virtualModel.propagateChange(recordedChange) + val consequentialChanges = propagatedChanges.map[consequentialChanges.EChanges].flatten + assertEquals(2, consequentialChanges.filter(CreateEObject).size) + assertEquals(2, consequentialChanges.filter(InsertRootEObject).size) + assertEquals(1, consequentialChanges.filter(ReplaceSingleValuedEReference).size) + assertEquals(2, consequentialChanges.filter(ReplaceSingleValuedEAttribute).size) + assertEquals(7, consequentialChanges.size) + } + + @Test + @DisplayName("add element to containment of element persisted in two resources") + def void singleChangeForElementContainedInRootElementInMultipleResource() { + val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) + val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet) + changeRecorder.addToRecording(resourceSet) + changeRecorder.beginRecording + val containedRoot = aet.Root + val containedInContainedRoot = aet.Root + resourceSet.createResource(createTestModelResourceUri("")) => [ + contents += aet.Root => [ + id = 'root' + recursiveRoot = containedRoot => [ + id = 'containedRoot' + recursiveRoot = containedInContainedRoot => [ + id = 'containedInContained' + ] + ] + ] + ] + resourceSet.createResource(createTestModelResourceUri("Contained")) => [ + contents += containedRoot + ] + resourceSet.createResource(createTestModelResourceUri("ContainedInContained")) => [ + contents += containedInContainedRoot + ] + val recordedChange = changeRecorder.endRecording(uuidResolver) + val propagatedChanges = virtualModel.propagateChange(recordedChange) + val consequentialChanges = propagatedChanges.map[consequentialChanges.EChanges].flatten + assertEquals(3, consequentialChanges.filter(CreateEObject).size) + assertEquals(3, consequentialChanges.filter(InsertRootEObject).size) + assertEquals(2, consequentialChanges.filter(ReplaceSingleValuedEReference).size) + assertEquals(3, consequentialChanges.filter(ReplaceSingleValuedEAttribute).size) + assertEquals(11, consequentialChanges.size) + } + + @Test + @DisplayName("load resource that should have been saved after propagating a change into a virtual model") + def void savedVirtualModel() { + val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) + val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet) + changeRecorder.addToRecording(resourceSet) + changeRecorder.beginRecording + val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ + contents += aet.Root => [ + id = 'root' + ] + ] + val recordedChange = changeRecorder.endRecording(uuidResolver) + virtualModel.propagateChange(recordedChange) + val reloadedResource = new ResourceSetImpl().withGlobalFactories.getResource(createTestModelResourceUri(""), + true) + assertThat(reloadedResource, containsModelOf(monitoredResource)) + } + + @Test + @DisplayName("reload a virtual model to which a simple change was propagated") + def void reloadVirtualModel() { + val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) + val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet) + changeRecorder.addToRecording(resourceSet) + changeRecorder.beginRecording + val root = aet.Root + val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ + contents += root => [ + id = 'root' + ] + ] + val recordedChange = changeRecorder.endRecording(uuidResolver) + virtualModel.propagateChange(recordedChange) + val originalModel = virtualModel.getModelInstance(createTestModelResourceUri("")) + val reloadedVirtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) + val reloadedModel = reloadedVirtualModel.getModelInstance(createTestModelResourceUri("")) + assertThat(reloadedModel.resource, containsModelOf(monitoredResource)) + assertNotEquals(originalModel, reloadedModel) + // Propagate another change to reloaded virtual model to ensure that everything is loaded correctly + changeRecorder.beginRecording + root.singleValuedEAttribute = 1 + val secondRecordedChange = changeRecorder.endRecording(uuidResolver) + val propagatedChange = reloadedVirtualModel.propagateChange(secondRecordedChange) + assertEquals(1, propagatedChange.size) + } + + @Test + @DisplayName("reload a virtual model with consistency preservation to which a simple change was propagated") + def void reloadVirtualModelWithConsistency() { + val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) + val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet) + changeRecorder.addToRecording(resourceSet) + changeRecorder.beginRecording + val root = aet.Root + val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ + contents += root => [ + id = 'root' + ] + ] + val recordedChange = changeRecorder.endRecording(uuidResolver) + virtualModel.propagateChange(recordedChange) + val originalModel = virtualModel.getModelInstance(createTestModelResourceUri("")) + val reloadedVirtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) + val reloadedModel = reloadedVirtualModel.getModelInstance(createTestModelResourceUri("")) + assertThat(reloadedModel.resource, containsModelOf(monitoredResource)) + assertNotEquals(originalModel, reloadedModel) + val reloadedTargetModel = reloadedVirtualModel.getModelInstance( + RedundancyChangePropagationSpecification.getTargetResourceUri(createTestModelResourceUri(""))) + assertThat(reloadedTargetModel.resource, containsModelOf(monitoredResource)) + assertEquals(1, + reloadedVirtualModel.correspondenceModel.getCorrespondingEObjects(reloadedModel.resource.contents.get(0)). + size) + } + + @Test + @DisplayName("move element such that corresponding element is moved from one resource to another and back") + def void moveCorrespondingToOtherResourceAndBack() { + val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) + val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet) + changeRecorder.addToRecording(resourceSet) + changeRecorder.beginRecording + val root = aet.Root + val testUri = createTestModelResourceUri("") + val monitoredResource = resourceSet.createResource(testUri) => [ + contents += root => [ + id = 'root' + ] + ] + virtualModel.propagateChange(changeRecorder.endRecording(uuidResolver)) + changeRecorder.beginRecording + val testIntermediateUri = createTestModelResourceUri("intermediate") + resourceSet.createResource(testIntermediateUri) => [ + contents += root + ] + virtualModel.propagateChange(changeRecorder.endRecording(uuidResolver)) + // There must not be the old and the old corresponding model + assertNull(virtualModel.getModelInstance(testUri)) + assertNull( + virtualModel.getModelInstance(RedundancyChangePropagationSpecification.getTargetResourceUri(testUri))) + changeRecorder.beginRecording + monitoredResource => [ + contents += root + ] + virtualModel.propagateChange(changeRecorder.endRecording(uuidResolver)) + assertNull(virtualModel.getModelInstance(testIntermediateUri)) + assertNull( + virtualModel.getModelInstance( + RedundancyChangePropagationSpecification.getTargetResourceUri(testIntermediateUri))) + } + + @Test + @DisplayName("create a view for a virtual model") + def void createView() { + val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) + val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) + virtualModel.createAndPropagateRoot(resourceSet, uuidResolver, ROOT_ID) + val testView = virtualModel.createTestView + // Check initial state: + assertThat(new HashSet(testView.rootObjects), not(is(emptySet()))) + assertEquals(testView.rootObjects.claimOne, testView.getRootObjects(Root).claimOne) + assertThat(testView.getRootObjects(Root).claimOne.id, is(ROOT_ID)) + assertThat("view source must not have been changed", !testView.outdated) + assertThat("view must not have been modified", !testView.isModified) + } + + @Test + @DisplayName("update view after a change in the virtual model") + def void updateView() { + val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) + val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) + virtualModel.createAndPropagateRoot(resourceSet, uuidResolver, ROOT_ID) + val testView = virtualModel.createTestView + + // Modify model + virtualModel.propagateChange(resourceSet.recordChanges(uuidResolver) [ + val resource = resourceSet.resources.claimOne + resource.firstRootEObject as Root => [ + multiValuedContainmentEReference += aet.NonRoot => [ + id = NON_ROOT_ID + ] + ] + ]) + + // Assert VSUM changed but view not modified: + assertThat("view source must have been changed", testView.outdated) + assertThat("view must not have been modified", !testView.isModified) + assertThat(testView.getRootObjects(Root).claimOne.multiValuedContainmentEReference, is(emptyList())) + + // Update view and assert view was updated correctly + testView.update() + assertThat("view source must not have been changed", !testView.outdated) + assertThat("view must not have been modified", !testView.isModified) + val viewRoot = testView.getRootObjects(Root).claimOne + assertThat(viewRoot.multiValuedContainmentEReference.claimOne.id, is(NON_ROOT_ID)) + } + + @Test + @DisplayName("change view and commit changes") + def void commitView() { + val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) + val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) + virtualModel.createAndPropagateRoot(resourceSet, uuidResolver, ROOT_ID) + val testView = virtualModel.createTestView.withChangeRecordingTrait + + // Modify view: + assertThat("view must not have been modified", !testView.isModified) + testView.getRootObjects(Root).claimOne => [ + multiValuedContainmentEReference += aet.NonRoot => [ + id = NON_ROOT_ID + ] + ] + + // Assert view modified but VSUM not changed: + assertThat("view source must not have been changed", !testView.outdated) + assertThat("view must have been modified", testView.isModified) + + // Commit changes and assert VSUM was updated correctly + testView.commitChanges() + assertThat("view source must have been changed", testView.outdated) + assertThat("view must not have been modified", !testView.isModified) + + testView.update(); + assertThat("view source must not have been changed", !testView.outdated) + assertThat("view must not have been modified", !testView.isModified) + + val reopenedViewRoot = virtualModel.createTestView.getRootObjects(Root).claimOne + assertThat(reopenedViewRoot.multiValuedContainmentEReference.claimOne.id, is(NON_ROOT_ID)) + } + + private def createTestModelResourceUri(String suffix) { + projectFolder.createTestModelResourceUri(suffix) + } + + private def getPathToVirtualModelProjectFolder() { + projectFolder.resolve("vsum") + } + + private def endRecording(ChangeRecorder changeRecorder, UuidResolver uuidResolver) { + val change = changeRecorder.endRecording + val changeResolver = VitruviusChangeResolver.forUuids(uuidResolver) + return changeResolver.assignIds(change) + } + + def private createAndPropagateRoot(VirtualModel virtualModel, ResourceSet resourceSet, UuidResolver uuidResolver, String rootId) { + virtualModel.propagateChange(resourceSet.recordChanges(uuidResolver) [ + resourceSet.createResource(projectFolder.createTestModelResourceUri("")) => [ + contents += aet.Root => [ + id = rootId + ] + ] + ]) + } + + def private static View createTestView(VirtualModel virtualModel) { + val viewType = ViewTypeFactory.createIdentityMappingViewType("").checkNotNull("cannot create view type") + val selector = virtualModel.createSelector(viewType).checkNotNull("cannot create selector") + selector.selectableElements.forEach[selector.setSelected(it, true)] + return selector.createView.checkNotNull("Cannot create view from selector!") + } + +} diff --git a/vsum/src/test/xtend/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend b/vsum/src/test/xtend/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend new file mode 100644 index 0000000000..2083a7ae36 --- /dev/null +++ b/vsum/src/test/xtend/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend @@ -0,0 +1,128 @@ +package tools.vitruv.framework.vsum + +import allElementTypes.AllElementTypesPackage +import allElementTypes.Root +import edu.kit.ipd.sdq.activextendannotations.Utility +import java.nio.file.Path +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.resource.ResourceSet +import tools.vitruv.change.atomic.EChange +import tools.vitruv.change.atomic.root.InsertRootEObject +import tools.vitruv.change.atomic.uuid.Uuid +import tools.vitruv.change.atomic.uuid.UuidResolver +import tools.vitruv.change.composite.MetamodelDescriptor +import tools.vitruv.change.composite.description.VitruviusChange +import tools.vitruv.change.composite.description.VitruviusChangeResolver +import tools.vitruv.change.composite.recording.ChangeRecorder +import tools.vitruv.change.correspondence.Correspondence +import tools.vitruv.change.correspondence.view.EditableCorrespondenceModelView +import tools.vitruv.change.interaction.UserInteractionFactory +import tools.vitruv.change.utils.ResourceAccess +import tools.vitruv.change.propagation.impl.AbstractChangePropagationSpecification + +import static org.junit.jupiter.api.Assertions.assertEquals +import static tools.vitruv.change.testutils.metamodels.AllElementTypesCreators.aet + +/** + * Utility methods for the VSUM and view test cases. + */ +@Utility +class VirtualModelTestUtil { + + /** + * Create a recorder, start recording a resource set, apply changes, stop and return the recorded changes. + */ + def static VitruviusChange recordChanges(ResourceSet resourceSet, UuidResolver uuidResolver, Runnable changesToPerform) { + val recorder = new ChangeRecorder(resourceSet) + val changeResolver = VitruviusChangeResolver.forUuids(uuidResolver) + recorder.addToRecording(resourceSet) + recorder.beginRecording + changesToPerform.run() + val result = recorder.endRecording + val resolvedChange = changeResolver.assignIds(result) + recorder.close + return resolvedChange + } + + /** + * Creates an empty virtual model without a change propagation specification. + */ + static def createAndLoadTestVirtualModel(Path folder) { + return new VirtualModelBuilder().withStorageFolder(folder).withUserInteractor( + UserInteractionFactory.instance.createUserInteractor( + UserInteractionFactory.instance.createPredefinedInteractionResultProvider(null))).buildAndInitialize() + } + + /** + * Creates an empty virtual model with a {@link RedundancyChangePropagationSpecification}. + */ + static def createAndLoadTestVirtualModelWithConsistencyPreservation(Path folder) { + return new VirtualModelBuilder().withStorageFolder(folder). + withChangePropagationSpecification(new RedundancyChangePropagationSpecification( + MetamodelDescriptor.with(AllElementTypesPackage.eNS_URI), MetamodelDescriptor.with(AllElementTypesPackage.eNS_URI) + )). + withUserInteractor(UserInteractionFactory.instance.createUserInteractor( + UserInteractionFactory.instance.createPredefinedInteractionResultProvider(null))).buildAndInitialize() + } + + /** + * Creates a model resource URI for a given project folder. Use the suffix to distinguish multiple URIs. + */ + static def createTestModelResourceUri(Path projectFolder, String suffix) { + URI.createFileURI(projectFolder.resolve("root" + suffix + ".allElementTypes").toString) + } + + static class RedundancyChangePropagationSpecification extends AbstractChangePropagationSpecification { + static def getTargetResourceUri(URI sourceUri) { + val sourceUriWithoutFileExtension = sourceUri.trimFileExtension.toFileString + val copySuffix = "Copy" + if (sourceUriWithoutFileExtension.endsWith(copySuffix)) { + val sourceUriWithoutSuffix = sourceUriWithoutFileExtension.substring(0, sourceUriWithoutFileExtension.length - copySuffix.length) + URI.createFileURI(sourceUriWithoutSuffix + "." + sourceUri.fileExtension) + } else { + URI.createFileURI(sourceUriWithoutFileExtension + copySuffix + "." + sourceUri.fileExtension) + } + } + + new(MetamodelDescriptor sourceMetamodelDescriptor, MetamodelDescriptor targetMetamodelDescriptor) { + super(sourceMetamodelDescriptor, targetMetamodelDescriptor) + } + + override doesHandleChange(EChange change, EditableCorrespondenceModelView correspondenceModel) { + if(change instanceof InsertRootEObject) { + return change.newValue instanceof Root + } + return false + } + + override propagateChange(EChange change, EditableCorrespondenceModelView correspondenceModel, + extension ResourceAccess resourceAccess) { + if(!doesHandleChange(change, correspondenceModel)) { + return + } + val typedChange = change as InsertRootEObject + val insertedRoot = typedChange.newValue as Root + // If there is a corresponding element, reuse it, otherwise create one + val correspondingRoots = correspondenceModel.getCorrespondingEObjects(insertedRoot).filter(Root) + val correspondingRoot = if(correspondingRoots.size == 1) { + correspondingRoots.get(0) + } else { + val newRoot = aet.Root => [ + id = insertedRoot.id + ] + correspondenceModel.addCorrespondenceBetween(insertedRoot, newRoot, null) + newRoot + } + + if(insertedRoot.eContainer !== null) { + val correspondingObjects = correspondenceModel.getCorrespondingEObjects(insertedRoot.eContainer, null).filter(Root) + assertEquals(1, correspondingObjects.size) + correspondingObjects.get(0).recursiveRoot = correspondingRoot + } + val resourceURI = typedChange.resource.URI + persistAsRoot(correspondingRoot, resourceURI.targetResourceUri) + } + } + +} From 7bac53127dfdf954ef99a390295b6f0454da679c Mon Sep 17 00:00:00 2001 From: larsk21 <57503246+larsk21@users.noreply.github.com> Date: Mon, 28 Oct 2024 12:07:48 +0100 Subject: [PATCH 03/11] build maintenance - unify git/github config - update Maven wrapper and config - remove unnecessary dependencies - update dependency versions - formatting --- .github/CODEOWNERS | 2 - .github/dependabot.yml | 12 --- .github/workflows/ci.yml | 73 --------------- .github/workflows/validation.yml | 112 ----------------------- .gitignore | 32 ++----- .mvn/extensions.xml | 8 -- .mvn/maven.config | 3 + .mvn/wrapper/maven-wrapper.jar | Bin 0 -> 62547 bytes .mvn/wrapper/maven-wrapper.properties | 2 +- applications/pom.xml | 3 +- pom.xml | 122 +++----------------------- testutils/deprecated/pom.xml | 3 +- testutils/integration/pom.xml | 3 +- testutils/pom.xml | 3 +- views/pom.xml | 21 ++--- vsum/pom.xml | 4 +- 16 files changed, 45 insertions(+), 358 deletions(-) delete mode 100644 .github/dependabot.yml delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/validation.yml delete mode 100644 .mvn/extensions.xml create mode 100644 .mvn/maven.config create mode 100644 .mvn/wrapper/maven-wrapper.jar diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4673dc1902..b97723187f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1 @@ -* @tsaglam -* @TomWerm * @vitruv-tools/maintainers diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 52a258cc4c..0000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - day: "wednesday" - time: "11:00" - timezone: "Europe/Berlin" - commit-message: - prefix: "GitHub Actions" - include: "scope" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 9da1d3ba6a..0000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: CI - -on: - push: - branches: [main] - release: - types: [created] - schedule: - - cron: '5 3 * * *' # run nightly at 3:05 am - workflow_call: - -jobs: - build: - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - fail-fast: false - runs-on: ${{ matrix.os }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Cache - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml', '**/MANIFEST.MF') }} - restore-keys: ${{ runner.os }}-m2 - - name: Set up JDK - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: 17 - - name: Build and Verify - run: > - ./mvnw -B -U clean verify - '-Dstyle.color=always' - '-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn' - '-Dorg.slf4j.simpleLogger.log.org.eclipse.tycho.core.resolver.DefaultTychoResolver=warn' - '-Dorg.slf4j.simpleLogger.log.org.eclipse.tycho.osgi.configuration.MavenContextConfigurator=warn' - '-Dorg.slf4j.simpleLogger.log.org.eclipse.sisu.equinox.launching.internal.DefaultEquinoxLauncher=warn' - '-Dorg.slf4j.simpleLogger.log.org.eclipse.xtext.maven.XtextGenerateMojo=warn' - '-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog' - env: - MAVEN_OPTS: -Djansi.force=true - - name: Store Framework Artifact - if: github.event_name == 'pull_request' && matrix.os == 'ubuntu-latest' - uses: actions/upload-artifact@v4 - with: - name: framework - path: releng/tools.vitruv.updatesite/target/repository - retention-days: 1 - - name: Publish Nightly Update Site - if: github.event_name != 'release' && github.ref == 'refs/heads/main' && github.repository_owner == 'vitruv-tools' && matrix.os == 'ubuntu-latest' - uses: peaceiris/actions-gh-pages@v4 - with: - deploy_key: ${{ secrets.UPDATE_SITE_DEPLOY_KEY }} - external_repository: vitruv-tools/updatesite - destination_dir: nightly/framework - publish_dir: releng/tools.vitruv.updatesite/target/repository - publish_branch: main - user_name: 'github-actions[bot]' - user_email: 'github-actions[bot]@users.noreply.github.com' - - name: Publish Release Update Site - if: github.event_name == 'release' && github.repository_owner == 'vitruv-tools' && matrix.os == 'ubuntu-latest' - uses: peaceiris/actions-gh-pages@v4 - with: - deploy_key: ${{ secrets.UPDATE_SITE_DEPLOY_KEY }} - external_repository: vitruv-tools/updatesite - destination_dir: release/framework/${{ github.event.release.tag_name }} - publish_dir: releng/tools.vitruv.updatesite/target/repository - publish_branch: main - user_name: 'github-actions[bot]' - user_email: 'github-actions[bot]@users.noreply.github.com' diff --git a/.github/workflows/validation.yml b/.github/workflows/validation.yml deleted file mode 100644 index 8415139598..0000000000 --- a/.github/workflows/validation.yml +++ /dev/null @@ -1,112 +0,0 @@ -name: Validation - -on: - pull_request: - -jobs: - validate_framework: - name: Framework - uses: ./.github/workflows/ci.yml - - build_dsls: - name: DSLs - runs-on: ubuntu-latest - steps: - - name: Checkout DSLs - uses: actions/checkout@v4 - with: - path: dsls - repository: vitruv-tools/Vitruv-DSLs - ref: main - fetch-depth: 0 - - name: Checkout Matching DSLs Branch - run: | - cd dsls - git checkout -B ${{ github.head_ref }} origin/${{ github.head_ref }} || true - - name: Cache - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml', '**/MANIFEST.MF') }} - restore-keys: ${{ runner.os }}-m2 - - name: Set up JDK - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: 17 - - name: Build DSLs - uses: coactions/setup-xvfb@v1 - with: - working-directory: ./dsls - run: > - ./mvnw -B -U clean package - -Dstyle.color=always - -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn - -Dorg.slf4j.simpleLogger.log.org.eclipse.tycho.core.resolver.DefaultTychoResolver=warn - -Dorg.slf4j.simpleLogger.log.org.eclipse.tycho.osgi.configuration.MavenContextConfigurator=warn - -Dorg.slf4j.simpleLogger.log.org.eclipse.sisu.equinox.launching.internal.DefaultEquinoxLauncher=warn - -Dorg.slf4j.simpleLogger.log.org.eclipse.xtext.maven.XtextGenerateMojo=warn - -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog - env: - MAVEN_OPTS: -Djansi.force=true - - name: Store DSLs Artifact - uses: actions/upload-artifact@v4 - with: - name: dsls - path: dsls/releng/tools.vitruv.dsls.updatesite/target/repository - retention-days: 1 - - validate_casestudies: - needs: [validate_framework, build_dsls] - name: Case Studies - runs-on: ubuntu-latest - steps: - - name: Download Framework Artifact - uses: actions/download-artifact@v4 - with: - name: framework - path: framework - - name: Download DSLs Artifact - uses: actions/download-artifact@v4 - with: - name: dsls - path: dsls - - name: Checkout Case Studies - uses: actions/checkout@v4 - with: - path: casestudies - repository: vitruv-tools/Vitruv-CaseStudies - ref: main - fetch-depth: 0 - - name: Checkout Matching Case Studies Branch - run: | - cd casestudies - git checkout -B ${{ github.head_ref }} origin/${{ github.head_ref }} || true - - name: Cache - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml', '**/MANIFEST.MF') }} - restore-keys: ${{ runner.os }}-m2 - - name: Set up JDK - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: 17 - - name: Build and Verify Case Studies - uses: coactions/setup-xvfb@v1 - with: - working-directory: ./casestudies - run: > - ./mvnw -B -U clean verify - -Dvitruv.framework.url=file:///${{ github.workspace }}/framework - -Dvitruv.dsls.url=file:///${{ github.workspace }}/dsls - -Dstyle.color=always - -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn - -Dorg.slf4j.simpleLogger.log.org.eclipse.tycho.core.resolver.DefaultTychoResolver=warn - -Dorg.slf4j.simpleLogger.log.org.eclipse.tycho.osgi.configuration.MavenContextConfigurator=warn - -Dorg.slf4j.simpleLogger.log.org.eclipse.sisu.equinox.launching.internal.DefaultEquinoxLauncher=warn - -Dorg.slf4j.simpleLogger.log.org.eclipse.xtext.maven.XtextGenerateMojo=warn - -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog - env: - MAVEN_OPTS: -Djansi.force=true diff --git a/.gitignore b/.gitignore index ea8d7021e7..baa32e7ae8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,29 +1,11 @@ -# Eclipse/Java -.metadata -bin/ -tmp/ -*.tmp -*.bak -*.swp -*.class - -# Xtext -src-gen/ -**/model/generated/ -.antlr-generator-*-patch.jar - -# EMF -model-gen/ - -# Xtend -xtend-gen/ -*._trace -*.xtendbin +# VS Code +.vscode/ # Maven target/ -.polyglot.build.properties -.mvn/wrapper/maven-wrapper.jar +*.log -# Custom -EvaluationData +# Eclipse +META-INF +build.properties +plugin.properties diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml deleted file mode 100644 index 754e258708..0000000000 --- a/.mvn/extensions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - org.eclipse.tycho - tycho-build - 3.0.4 - - \ No newline at end of file diff --git a/.mvn/maven.config b/.mvn/maven.config new file mode 100644 index 0000000000..4e39a1cd0d --- /dev/null +++ b/.mvn/maven.config @@ -0,0 +1,3 @@ +--update-snapshots +--fail-at-end +-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..cb28b0e37c7d206feb564310fdeec0927af4123a GIT binary patch literal 62547 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 A?*IS* literal 0 HcmV?d00001 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index d8b2495a1e..346d645fd0 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -14,5 +14,5 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.1/apache-maven-3.9.1-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/applications/pom.xml b/applications/pom.xml index ad8aff8b20..0fb33fe20a 100644 --- a/applications/pom.xml +++ b/applications/pom.xml @@ -1,7 +1,8 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diff --git a/pom.xml b/pom.xml index d3498b3ff1..edad0b3525 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 @@ -83,7 +84,6 @@ - @@ -106,11 +106,6 @@ tools.vitruv.change.propagation 3.0.1-SNAPSHOT - - tools.vitruv - tools.vitruv.change.changederivation - 3.0.1-SNAPSHOT - tools.vitruv tools.vitruv.change.testutils.core @@ -121,22 +116,8 @@ tools.vitruv.change.testutils.integration 3.0.1-SNAPSHOT - - tools.vitruv - tools.vitruv.change.testutils - 3.0.1-SNAPSHOT - + - - ch.qos.logback - logback-classic - 1.4.11 - - - ch.qos.logback - logback-core - 1.4.11 - com.google.guava guava @@ -155,98 +136,43 @@ org.eclipse.emf org.eclipse.emf.common - 2.29.0 + 2.31.0 org.eclipse.emf org.eclipse.emf.ecore - 2.35.0 - - - org.eclipse.emf - org.eclipse.emf.ecore.change - 2.15.0 + 2.37.0 org.eclipse.emf org.eclipse.emf.ecore.xmi - 2.36.0 - - - org.eclipse.emf - org.eclipse.emf.edit - 2.19.0 - - - org.eclipse.platform - org.eclipse.core.resources - 3.19.100 + 2.38.0 org.eclipse.platform org.eclipse.core.runtime - 3.29.0 + 3.31.100 + org.eclipse.platform org.eclipse.equinox.common - 3.18.100 + 3.19.100 org.eclipse.platform org.eclipse.equinox.registry 3.12.100 - - org.eclipse.platform - org.eclipse.jface - 3.31.0 - - - - org.eclipse.platform - org.eclipse.swt - - - - - org.eclipse.platform - org.eclipse.swt.${swt.platform} - 3.124.100 - - - - org.eclipse.platform - org.eclipse.swt - - - - - org.eclipse.platform - org.eclipse.ui.workbench - 3.130.0 - - - - org.eclipse.platform - org.eclipse.swt - - - org.eclipse.xtend org.eclipse.xtend.lib - 2.32.0 - - - org.eclipse.xtend - org.eclipse.xtend.lib.macro - 2.32.0 + 2.36.0 org.eclipse.xtext org.eclipse.xtext.xbase.lib - 2.32.0 + 2.36.0 org.hamcrest @@ -263,28 +189,6 @@ junit-jupiter-params 5.10.1 - - org.junit.platform - junit-platform-commons - 1.10.1 - - - org.slf4j - slf4j-api - 2.0.9 - - - sdq-commons - edu.kit.ipd.sdq.commons.util.eclipse - 2.3.0.202304271319 - - - - * - * - - - sdq-commons edu.kit.ipd.sdq.commons.util.emf @@ -317,10 +221,10 @@ org.mockito mockito-core - 5.12.0 + 5.14.2 test - + \ No newline at end of file diff --git a/testutils/deprecated/pom.xml b/testutils/deprecated/pom.xml index 4ee9f58aef..fdd12b3d82 100644 --- a/testutils/deprecated/pom.xml +++ b/testutils/deprecated/pom.xml @@ -1,7 +1,8 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diff --git a/testutils/integration/pom.xml b/testutils/integration/pom.xml index e6c1dd61ce..4ec184031f 100644 --- a/testutils/integration/pom.xml +++ b/testutils/integration/pom.xml @@ -1,7 +1,8 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diff --git a/testutils/pom.xml b/testutils/pom.xml index 7488f3660f..780623acf6 100644 --- a/testutils/pom.xml +++ b/testutils/pom.xml @@ -1,7 +1,8 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diff --git a/views/pom.xml b/views/pom.xml index 7d4a9f2efb..2959def066 100644 --- a/views/pom.xml +++ b/views/pom.xml @@ -1,7 +1,8 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 @@ -66,23 +67,23 @@ emf-compare - org.eclipse.emf.compare + org.eclipse.emf.compare org.eclipse.emf - org.eclipse.emf.ecore + org.eclipse.emf.ecore org.eclipse.xtext - org.eclipse.xtext.xbase.lib + org.eclipse.xtext.xbase.lib com.google.guava - guava + guava org.eclipse.emf - org.eclipse.emf.common + org.eclipse.emf.common @@ -116,11 +117,11 @@ mockito-core test - + org.eclipse.emf - org.eclipse.emf.ecore.xmi - test + org.eclipse.emf.ecore.xmi + test - + \ No newline at end of file diff --git a/vsum/pom.xml b/vsum/pom.xml index ea8ce8d977..6f2f6abb16 100644 --- a/vsum/pom.xml +++ b/vsum/pom.xml @@ -1,7 +1,8 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 @@ -115,7 +116,6 @@ org.hamcrest hamcrest - 2.2 test From d0d085e988a18e8cda7eb7b39f8ab898659a726f Mon Sep 17 00:00:00 2001 From: larsk21 <57503246+larsk21@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:56:54 +0100 Subject: [PATCH 04/11] update Vitruv-Change snapshot version --- pom.xml | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index edad0b3525..69760e6604 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,19 @@ + + + ossrh-snapshots + OSSRH Snapshots + https://oss.sonatype.org/content/repositories/snapshots + + true + + + false + + + emf-compare @@ -89,32 +102,32 @@ tools.vitruv tools.vitruv.change.correspondence - 3.0.1-SNAPSHOT + 3.1.0-SNAPSHOT tools.vitruv tools.vitruv.change.composite - 3.0.1-SNAPSHOT + 3.1.0-SNAPSHOT tools.vitruv tools.vitruv.change.interaction - 3.0.1-SNAPSHOT + 3.1.0-SNAPSHOT tools.vitruv tools.vitruv.change.propagation - 3.0.1-SNAPSHOT + 3.1.0-SNAPSHOT tools.vitruv tools.vitruv.change.testutils.core - 3.0.1-SNAPSHOT + 3.1.0-SNAPSHOT tools.vitruv tools.vitruv.change.testutils.integration - 3.0.1-SNAPSHOT + 3.1.0-SNAPSHOT From d39d8732468625d544cb52555a12e7deb39cd455 Mon Sep 17 00:00:00 2001 From: larsk21 <57503246+larsk21@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:57:23 +0100 Subject: [PATCH 05/11] add CI workflow --- .github/workflows/ci.yml | 87 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..3980fc0a2f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,87 @@ +name: Continuous Integration Workflow + +# workflow triggers +on: + # manually + workflow_dispatch: + # PRs on `main` + pull_request: + branches: + - main + # nightly + schedule: + - cron: "0 3 * * *" + +jobs: + verify: + name: Verify build on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] + + steps: + - name: Checkout repository + uses: actions/checkout@v4.1.1 + + - name: Setup Java and Maven cache + uses: actions/setup-java@v3.13.0 + with: + distribution: 'temurin' + java-version: '17' + check-latest: true + cache: 'maven' + + - name: Verify build + run: > + ./mvnw clean verify + --batch-mode + --update-snapshots + --no-transfer-progress + + - name: Stage build results + run: mkdir staging-${{ matrix.os }} && cp **/target/*.jar staging-${{ matrix.os }}/ + + - name: Upload build results + uses: actions/upload-artifact@v3.1.3 + with: + name: Build Results + path: staging-*/ + + deploy-snapshot: + name: Deploy snapshot + runs-on: ubuntu-latest + needs: [verify] + #if: github.ref == 'refs/heads/main' && github.repository_owner == 'vitruv-tools' + strategy: + fail-fast: true + + steps: + - name: Checkout repository + uses: actions/checkout@v4.1.1 + + - name: Setup Java and Maven cache + uses: actions/setup-java@v3.13.0 + with: + distribution: 'temurin' + java-version: '17' + check-latest: true + cache: 'maven' + server-id: ossrh + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + gpg-private-key: ${{ secrets.OSSRH_GPG_SECRET_KEY }} + gpg-passphrase: MAVEN_GPG_PASSPHRASE + + - name: Deploy snapshot + run: > + ./mvnw clean deploy -P snapshot + -DskipTests + --batch-mode + --update-snapshots + --no-transfer-progress + env: + MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} From da62791572dea1f1025f8669234343801d094593 Mon Sep 17 00:00:00 2001 From: larsk21 <57503246+larsk21@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:58:05 +0100 Subject: [PATCH 06/11] update snapshot version to 3.1.0-SNAPSHOT --- applications/pom.xml | 2 +- pom.xml | 2 +- testutils/deprecated/pom.xml | 2 +- testutils/integration/pom.xml | 2 +- testutils/pom.xml | 2 +- views/pom.xml | 2 +- vsum/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/applications/pom.xml b/applications/pom.xml index 0fb33fe20a..7069935696 100644 --- a/applications/pom.xml +++ b/applications/pom.xml @@ -8,7 +8,7 @@ tools.vitruv tools.vitruv.framework - 3.0.1-SNAPSHOT + 3.1.0-SNAPSHOT tools.vitruv.framework.applications diff --git a/pom.xml b/pom.xml index 69760e6604..f2ae54cc15 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ tools.vitruv.framework - 3.0.1-SNAPSHOT + 3.1.0-SNAPSHOT pom diff --git a/testutils/deprecated/pom.xml b/testutils/deprecated/pom.xml index fdd12b3d82..db9b19d5a1 100644 --- a/testutils/deprecated/pom.xml +++ b/testutils/deprecated/pom.xml @@ -8,7 +8,7 @@ tools.vitruv tools.vitruv.framework.testutils - 3.0.1-SNAPSHOT + 3.1.0-SNAPSHOT tools.vitruv.framework.testutils.deprecated diff --git a/testutils/integration/pom.xml b/testutils/integration/pom.xml index 4ec184031f..8c345bfc5f 100644 --- a/testutils/integration/pom.xml +++ b/testutils/integration/pom.xml @@ -8,7 +8,7 @@ tools.vitruv tools.vitruv.framework.testutils - 3.0.1-SNAPSHOT + 3.1.0-SNAPSHOT tools.vitruv.framework.testutils.integration diff --git a/testutils/pom.xml b/testutils/pom.xml index 780623acf6..913b5c1779 100644 --- a/testutils/pom.xml +++ b/testutils/pom.xml @@ -8,7 +8,7 @@ tools.vitruv tools.vitruv.framework - 3.0.1-SNAPSHOT + 3.1.0-SNAPSHOT tools.vitruv.framework.testutils diff --git a/views/pom.xml b/views/pom.xml index 2959def066..a8c8940e80 100644 --- a/views/pom.xml +++ b/views/pom.xml @@ -8,7 +8,7 @@ tools.vitruv tools.vitruv.framework - 3.0.1-SNAPSHOT + 3.1.0-SNAPSHOT tools.vitruv.framework.views diff --git a/vsum/pom.xml b/vsum/pom.xml index 6f2f6abb16..816924bd18 100644 --- a/vsum/pom.xml +++ b/vsum/pom.xml @@ -8,7 +8,7 @@ tools.vitruv tools.vitruv.framework - 3.0.1-SNAPSHOT + 3.1.0-SNAPSHOT tools.vitruv.framework.vsum From ea34d387d52df4d3ff76dab1fc6d6ce0030d66e6 Mon Sep 17 00:00:00 2001 From: larsk21 <57503246+larsk21@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:08:18 +0100 Subject: [PATCH 07/11] enable snapshot deployment only for nightly builds --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3980fc0a2f..3f9b59c3c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,7 +53,7 @@ jobs: name: Deploy snapshot runs-on: ubuntu-latest needs: [verify] - #if: github.ref == 'refs/heads/main' && github.repository_owner == 'vitruv-tools' + if: github.ref == 'refs/heads/main' && github.repository_owner == 'vitruv-tools' strategy: fail-fast: true From c0358d02829387f9dacdcf505ea0b4ede17358e7 Mon Sep 17 00:00:00 2001 From: larsk21 <57503246+larsk21@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:07:02 +0100 Subject: [PATCH 08/11] add SCM information and license URL --- pom.xml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f2ae54cc15..61ebf6c3bc 100644 --- a/pom.xml +++ b/pom.xml @@ -16,16 +16,24 @@ 3.1.0-SNAPSHOT pom - Vitruv Framework The Vitruv framework providing the definition of views and v-sums. + https://github.com/vitruv-tools/Vitruv + Eclipse Public License - v 1.0 + https://www.eclipse.org/org/documents/epl-v10.php + + scm:git:git://github.com/vitruv-tools/Vitruv.git + scm:git:https://github.com/vitruv-tools/Vitruv.git + https://github.com/vitruv-tools/Vitruv/tree/main + + views From 246f73cc76a2196bd203235dbe8e808b36d4d8fa Mon Sep 17 00:00:00 2001 From: larsk21 <57503246+larsk21@users.noreply.github.com> Date: Wed, 6 Nov 2024 13:25:50 +0100 Subject: [PATCH 09/11] ignore maven-wrapper.jar --- .gitignore | 1 + .mvn/wrapper/maven-wrapper.jar | Bin 62547 -> 0 bytes 2 files changed, 1 insertion(+) delete mode 100644 .mvn/wrapper/maven-wrapper.jar diff --git a/.gitignore b/.gitignore index baa32e7ae8..078418f500 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ # Maven target/ *.log +.mvn/wrapper/*.jar # Eclipse META-INF diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index cb28b0e37c7d206feb564310fdeec0927af4123a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62547 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 A?*IS* From b43fd29de0694b9747b13fa82e677ea85381aa2f Mon Sep 17 00:00:00 2001 From: larsk21 <57503246+larsk21@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:06:09 +0100 Subject: [PATCH 10/11] add dependabot config --- .github/dependabot.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..c6f9cfc95e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +version: 2 +updates: +- package-ecosystem: maven + directory: "/" + schedule: + interval: daily + ignore: + - dependency-name: tools.vitruv:* + commit-message: + prefix: "Maven" + include: "scope" +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + commit-message: + prefix: "GitHub Actions" + include: "scope" \ No newline at end of file From 6453e68a5d826b42e070d432e2dad0bb16adab72 Mon Sep 17 00:00:00 2001 From: larsk21 <57503246+larsk21@users.noreply.github.com> Date: Wed, 20 Nov 2024 13:40:00 +0100 Subject: [PATCH 11/11] rename CI workflow --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3f9b59c3c8..0f0e911fca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Continuous Integration Workflow +name: CI # workflow triggers on: