aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects')
-rw-r--r--subprojects/language-ide/build.gradle18
-rw-r--r--subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeModule.java30
-rw-r--r--subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeSetup.java24
-rw-r--r--subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/FuzzyMatcher.java44
-rw-r--r--subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java77
-rw-r--r--subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java122
-rw-r--r--subprojects/language-model/META-INF/MANIFEST.MF17
-rw-r--r--subprojects/language-model/build.gradle56
-rw-r--r--subprojects/language-model/build.properties11
-rw-r--r--subprojects/language-model/plugin.properties4
-rw-r--r--subprojects/language-model/plugin.xml24
-rw-r--r--subprojects/language-model/problem.aird3646
-rw-r--r--subprojects/language-model/src/main/java/tools/refinery/language/model/GenerateProblemModel.mwe220
-rw-r--r--subprojects/language-model/src/main/java/tools/refinery/language/model/ProblemEMFSetup.java34
-rw-r--r--subprojects/language-model/src/main/java/tools/refinery/language/model/ProblemUtil.java121
-rw-r--r--subprojects/language-model/src/main/resources/model/builtin.problem_xmi67
-rw-r--r--subprojects/language-model/src/main/resources/model/problem.ecore191
-rw-r--r--subprojects/language-model/src/main/resources/model/problem.genmodel165
-rw-r--r--subprojects/language-model/src/testFixtures/java/tools/refinery/language/model/tests/ProblemTestUtil.java197
-rw-r--r--subprojects/language-to-store/build.gradle10
-rw-r--r--subprojects/language-to-store/src/main/java/tools/refinery/language/mapping/PartialModelMapper.java239
-rw-r--r--subprojects/language-to-store/src/main/java/tools/refinery/language/mapping/PartialModelMapperDTO.java54
-rw-r--r--subprojects/language-to-store/src/test/java/tools/refinery/language/mapping/tests/PartialModelMapperTest.xtend438
-rw-r--r--subprojects/language-web/.editorconfig5
-rw-r--r--subprojects/language-web/.eslintrc.js40
-rw-r--r--subprojects/language-web/.stylelintrc.js15
-rw-r--r--subprojects/language-web/build.gradle147
-rw-r--r--subprojects/language-web/package.json103
-rw-r--r--subprojects/language-web/src/main/css/index.scss16
-rw-r--r--subprojects/language-web/src/main/css/themeVariables.module.scss9
-rw-r--r--subprojects/language-web/src/main/css/themes.scss38
-rw-r--r--subprojects/language-web/src/main/html/index.html16
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java52
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebModule.java35
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebSetup.java25
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebSocketServlet.java29
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java192
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/occurrences/ProblemOccurrencesService.java16
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/PongResult.java44
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ResponseHandler.java8
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ResponseHandlerException.java14
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/SubscribingServiceContext.java26
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java180
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebErrorKind.java11
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebErrorResponse.java79
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebOkResponse.java72
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebPushMessage.java81
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebRequest.java57
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebResponse.java4
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PrecomputationListener.java15
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushServiceDispatcher.java23
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java89
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentAccess.java68
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java33
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/SimpleServiceContext.java26
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/SimpleSession.java35
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextStatusCode.java9
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java133
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketServlet.java83
-rw-r--r--subprojects/language-web/src/main/js/App.tsx60
-rw-r--r--subprojects/language-web/src/main/js/RootStore.tsx39
-rw-r--r--subprojects/language-web/src/main/js/editor/EditorArea.tsx152
-rw-r--r--subprojects/language-web/src/main/js/editor/EditorButtons.tsx98
-rw-r--r--subprojects/language-web/src/main/js/editor/EditorParent.ts205
-rw-r--r--subprojects/language-web/src/main/js/editor/EditorStore.ts289
-rw-r--r--subprojects/language-web/src/main/js/editor/GenerateButton.tsx44
-rw-r--r--subprojects/language-web/src/main/js/editor/decorationSetExtension.ts39
-rw-r--r--subprojects/language-web/src/main/js/editor/findOccurrences.ts35
-rw-r--r--subprojects/language-web/src/main/js/editor/semanticHighlighting.ts24
-rw-r--r--subprojects/language-web/src/main/js/global.d.ts11
-rw-r--r--subprojects/language-web/src/main/js/index.tsx69
-rw-r--r--subprojects/language-web/src/main/js/language/folding.ts115
-rw-r--r--subprojects/language-web/src/main/js/language/indentation.ts87
-rw-r--r--subprojects/language-web/src/main/js/language/problem.grammar149
-rw-r--r--subprojects/language-web/src/main/js/language/problemLanguageSupport.ts92
-rw-r--r--subprojects/language-web/src/main/js/language/props.ts7
-rw-r--r--subprojects/language-web/src/main/js/theme/EditorTheme.ts47
-rw-r--r--subprojects/language-web/src/main/js/theme/ThemeProvider.tsx15
-rw-r--r--subprojects/language-web/src/main/js/theme/ThemeStore.ts64
-rw-r--r--subprojects/language-web/src/main/js/utils/ConditionVariable.ts64
-rw-r--r--subprojects/language-web/src/main/js/utils/PendingTask.ts60
-rw-r--r--subprojects/language-web/src/main/js/utils/Timer.ts33
-rw-r--r--subprojects/language-web/src/main/js/utils/logger.ts49
-rw-r--r--subprojects/language-web/src/main/js/xtext/ContentAssistService.ts219
-rw-r--r--subprojects/language-web/src/main/js/xtext/HighlightingService.ts37
-rw-r--r--subprojects/language-web/src/main/js/xtext/OccurrencesService.ts127
-rw-r--r--subprojects/language-web/src/main/js/xtext/UpdateService.ts363
-rw-r--r--subprojects/language-web/src/main/js/xtext/ValidationService.ts39
-rw-r--r--subprojects/language-web/src/main/js/xtext/XtextClient.ts86
-rw-r--r--subprojects/language-web/src/main/js/xtext/XtextWebSocketClient.ts362
-rw-r--r--subprojects/language-web/src/main/js/xtext/xtextMessages.ts40
-rw-r--r--subprojects/language-web/src/main/js/xtext/xtextServiceResults.ts112
-rw-r--r--subprojects/language-web/src/test/java/tools/refinery/language/web/ProblemWebSocketServletIntegrationTest.java204
-rw-r--r--subprojects/language-web/src/test/java/tools/refinery/language/web/tests/AwaitTerminationExecutorServiceProvider.java42
-rw-r--r--subprojects/language-web/src/test/java/tools/refinery/language/web/tests/ProblemWebInjectorProvider.java47
-rw-r--r--subprojects/language-web/src/test/java/tools/refinery/language/web/tests/RestartableCachedThreadPool.java109
-rw-r--r--subprojects/language-web/src/test/java/tools/refinery/language/web/tests/WebSocketIntegrationTestClient.java98
-rw-r--r--subprojects/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/TransactionExecutorTest.java165
-rw-r--r--subprojects/language-web/tsconfig.json18
-rw-r--r--subprojects/language-web/tsconfig.sonar.json17
-rw-r--r--subprojects/language-web/webpack.config.js232
-rw-r--r--subprojects/language/build.gradle66
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/GenerateProblem.mwe268
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/Problem.xtext205
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java84
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/ProblemStandaloneSetup.java44
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/ProblemXmiRuntimeModule.java35
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/conversion/ProblemValueConverterService.java19
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/conversion/UpperBoundValueConverter.java35
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java183
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java25
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java15
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java194
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java88
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java163
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java33
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java103
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemXmiResourceFactory.java16
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java51
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java18
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java42
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java104
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java62
-rw-r--r--subprojects/language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.xtend64
-rw-r--r--subprojects/language/src/test/java/tools/refinery/language/tests/formatting2/ProblemFormatterTest.java235
-rw-r--r--subprojects/language/src/test/java/tools/refinery/language/tests/rules/DirectRuleParsingTest.xtend96
-rw-r--r--subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.xtend322
-rw-r--r--subprojects/language/src/test/java/tools/refinery/language/tests/serializer/ProblemSerializerTest.java229
-rw-r--r--subprojects/store/build.gradle9
-rw-r--r--subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutBenchmark.java77
-rw-r--r--subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutExecutionPlan.java57
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/ContinousHashProvider.java69
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/Cursor.java14
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/CursorAsIterator.java57
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/DiffCursor.java6
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/MapAsIterable.java26
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/Versioned.java7
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java13
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java14
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreConfiguration.java48
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreImpl.java135
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/HashClash.java18
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java378
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java131
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java221
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java454
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java85
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/OldValueBox.java19
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java171
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/Model.java20
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/ModelCursor.java25
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/ModelDiffCursor.java26
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java16
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreImpl.java122
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/Tuple.java148
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProvider.java65
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProviderBitMagic.java28
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java124
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/SimilarRelationEquivalenceClass.java33
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/representation/AuxilaryData.java22
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/representation/DataRepresentation.java24
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/representation/Relation.java31
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/representation/TruthValue.java51
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/QueriableModel.java30
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/QueriableModelStore.java23
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/QueriableModelStoreImpl.java127
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/building/DNFAnd.java37
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/building/DNFAtom.java33
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/building/DNFPredicate.java72
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/building/EquivalenceAtom.java44
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/building/PredicateAtom.java66
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/building/RelationAtom.java49
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/building/Variable.java22
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/internal/DNF2PQuery.java189
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/internal/DummyBaseIndexer.java59
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/internal/ModelUpdateListener.java103
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/internal/PredicateResult.java24
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/internal/QueriableModelImpl.java212
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/internal/RawPatternMatcher.java57
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalEngineContext.java33
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalQueryMetaContext.java58
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalRuntimeContext.java178
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalScope.java43
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/internal/ViewUpdate.java34
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/internal/ViewUpdateBuffer.java46
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/internal/ViewUpdateTranslator.java57
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/view/FilteredRelationView.java48
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java50
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java16
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/view/RelationView.java85
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/util/CollectionsUtil.java72
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java22
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java96
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java143
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java117
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java97
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java101
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java92
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java89
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java109
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java113
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java64
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtilsTest.java33
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java214
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/model/hashTests/HashEfficiencyTest.java161
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java148
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/query/test/QueryTest.java445
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/query/test/QueryTransactionTest.java58
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/util/CollectionsUtilTests.java78
209 files changed, 20991 insertions, 0 deletions
diff --git a/subprojects/language-ide/build.gradle b/subprojects/language-ide/build.gradle
new file mode 100644
index 00000000..3786762b
--- /dev/null
+++ b/subprojects/language-ide/build.gradle
@@ -0,0 +1,18 @@
1plugins {
2 id 'refinery-java-library'
3 id 'refinery-xtext-conventions'
4}
5
6dependencies {
7 api project(':refinery-language')
8 api libs.xtext.ide
9 api libs.xtext.xbase.ide
10}
11
12def generateXtextLanguage = project(':refinery-language').tasks.named('generateXtextLanguage')
13
14for (taskName in ['compileJava', 'processResources']) {
15 tasks.named(taskName) {
16 dependsOn generateXtextLanguage
17 }
18}
diff --git a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeModule.java b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeModule.java
new file mode 100644
index 00000000..51cecf06
--- /dev/null
+++ b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeModule.java
@@ -0,0 +1,30 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language.ide;
5
6import org.eclipse.xtext.ide.editor.contentassist.IPrefixMatcher;
7import org.eclipse.xtext.ide.editor.contentassist.IdeCrossrefProposalProvider;
8import org.eclipse.xtext.ide.editor.syntaxcoloring.ISemanticHighlightingCalculator;
9
10import tools.refinery.language.ide.contentassist.FuzzyMatcher;
11import tools.refinery.language.ide.contentassist.ProblemCrossrefProposalProvider;
12import tools.refinery.language.ide.syntaxcoloring.ProblemSemanticHighlightingCalculator;
13
14/**
15 * Use this class to register ide components.
16 */
17public class ProblemIdeModule extends AbstractProblemIdeModule {
18 public Class<? extends ISemanticHighlightingCalculator> bindISemanticHighlightingCalculator() {
19 return ProblemSemanticHighlightingCalculator.class;
20 }
21
22 @Override
23 public Class<? extends IPrefixMatcher> bindIPrefixMatcher() {
24 return FuzzyMatcher.class;
25 }
26
27 public Class<? extends IdeCrossrefProposalProvider> bindIdeCrossrefProposalProvider() {
28 return ProblemCrossrefProposalProvider.class;
29 }
30}
diff --git a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeSetup.java b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeSetup.java
new file mode 100644
index 00000000..5b88d41f
--- /dev/null
+++ b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeSetup.java
@@ -0,0 +1,24 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language.ide;
5
6import org.eclipse.xtext.util.Modules2;
7
8import com.google.inject.Guice;
9import com.google.inject.Injector;
10
11import tools.refinery.language.ProblemRuntimeModule;
12import tools.refinery.language.ProblemStandaloneSetup;
13
14/**
15 * Initialization support for running Xtext languages as language servers.
16 */
17public class ProblemIdeSetup extends ProblemStandaloneSetup {
18
19 @Override
20 public Injector createInjector() {
21 return Guice.createInjector(Modules2.mixin(new ProblemRuntimeModule(), new ProblemIdeModule()));
22 }
23
24}
diff --git a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/FuzzyMatcher.java b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/FuzzyMatcher.java
new file mode 100644
index 00000000..fe722ca1
--- /dev/null
+++ b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/FuzzyMatcher.java
@@ -0,0 +1,44 @@
1package tools.refinery.language.ide.contentassist;
2
3import org.eclipse.xtext.ide.editor.contentassist.IPrefixMatcher;
4
5import com.google.inject.Singleton;
6
7/**
8 * Implements the candidate matching algoritm used by CodeMirror 6.
9 *
10 * Using this class ensures that the same candidates will be returned when
11 * filtering content assist proposals on the server as on the client.
12 *
13 * The matching is "fuzzy" (<code>fzf</code>-like), i.e., the prefix characters
14 * may occur anywhere in the name, but must be in the same order as in the
15 * prefix.
16 *
17 * @author Kristóf Marussy
18 */
19@Singleton
20public class FuzzyMatcher implements IPrefixMatcher {
21 @Override
22 public boolean isCandidateMatchingPrefix(String name, String prefix) {
23 var nameIgnoreCase = name.toLowerCase();
24 var prefixIgnoreCase = prefix.toLowerCase();
25 int prefixLength = prefixIgnoreCase.length();
26 if (prefixLength == 0) {
27 return true;
28 }
29 int nameLength = nameIgnoreCase.length();
30 if (prefixLength > nameLength) {
31 return false;
32 }
33 int prefixIndex = 0;
34 for (int nameIndex = 0; nameIndex < nameLength; nameIndex++) {
35 if (nameIgnoreCase.charAt(nameIndex) == prefixIgnoreCase.charAt(prefixIndex)) {
36 prefixIndex++;
37 if (prefixIndex == prefixLength) {
38 return true;
39 }
40 }
41 }
42 return false;
43 }
44}
diff --git a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java
new file mode 100644
index 00000000..f828e836
--- /dev/null
+++ b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java
@@ -0,0 +1,77 @@
1package tools.refinery.language.ide.contentassist;
2
3import java.util.Objects;
4
5import org.eclipse.emf.ecore.EObject;
6import org.eclipse.emf.ecore.util.EcoreUtil;
7import org.eclipse.xtext.CrossReference;
8import org.eclipse.xtext.GrammarUtil;
9import org.eclipse.xtext.ide.editor.contentassist.ContentAssistContext;
10import org.eclipse.xtext.ide.editor.contentassist.ContentAssistEntry;
11import org.eclipse.xtext.ide.editor.contentassist.IdeCrossrefProposalProvider;
12import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
13import org.eclipse.xtext.resource.IEObjectDescription;
14
15import com.google.inject.Inject;
16
17import tools.refinery.language.model.ProblemUtil;
18import tools.refinery.language.model.problem.Problem;
19import tools.refinery.language.resource.ReferenceCounter;
20
21public class ProblemCrossrefProposalProvider extends IdeCrossrefProposalProvider {
22 @Inject
23 private ReferenceCounter referenceCounter;
24
25 @Override
26 protected ContentAssistEntry createProposal(IEObjectDescription candidate, CrossReference crossRef,
27 ContentAssistContext context) {
28 if (!shouldCreateProposal(candidate, crossRef, context)) {
29 return null;
30 }
31 return super.createProposal(candidate, crossRef, context);
32 }
33
34 protected boolean shouldCreateProposal(IEObjectDescription candidate, CrossReference crossRef,
35 ContentAssistContext context) {
36 var rootModel = context.getRootModel();
37 var eObjectOrProxy = candidate.getEObjectOrProxy();
38 if (!Objects.equals(rootModel.eResource(), eObjectOrProxy.eResource())) {
39 return true;
40 }
41 var currentValue = getCurrentValue(crossRef, context);
42 if (currentValue == null) {
43 return true;
44 }
45 var eObject = EcoreUtil.resolve(eObjectOrProxy, rootModel);
46 if (!Objects.equals(currentValue, eObject)) {
47 return true;
48 }
49 if (!ProblemUtil.isImplicit(eObject)) {
50 return true;
51 }
52 if (rootModel instanceof Problem problem) {
53 return referenceCounter.countReferences(problem, eObject) >= 2;
54 }
55 return true;
56 }
57
58 protected EObject getCurrentValue(CrossReference crossRef, ContentAssistContext context) {
59 var value = getCurrentValue(crossRef, context.getCurrentModel());
60 if (value != null) {
61 return value;
62 }
63 var currentNodeSemanticObject = NodeModelUtils.findActualSemanticObjectFor(context.getCurrentNode());
64 return getCurrentValue(crossRef, currentNodeSemanticObject);
65 }
66
67 protected EObject getCurrentValue(CrossReference crossRef, EObject context) {
68 if (context == null) {
69 return null;
70 }
71 var eReference = GrammarUtil.getReference(crossRef, context.eClass());
72 if (eReference == null || eReference.isMany()) {
73 return null;
74 }
75 return (EObject) context.eGet(eReference);
76 }
77}
diff --git a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java
new file mode 100644
index 00000000..01ac33f7
--- /dev/null
+++ b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java
@@ -0,0 +1,122 @@
1package tools.refinery.language.ide.syntaxcoloring;
2
3import java.util.List;
4
5import org.eclipse.emf.common.util.EList;
6import org.eclipse.emf.ecore.EObject;
7import org.eclipse.emf.ecore.EReference;
8import org.eclipse.xtext.ide.editor.syntaxcoloring.DefaultSemanticHighlightingCalculator;
9import org.eclipse.xtext.ide.editor.syntaxcoloring.IHighlightedPositionAcceptor;
10import org.eclipse.xtext.nodemodel.INode;
11import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
12import org.eclipse.xtext.service.OperationCanceledManager;
13import org.eclipse.xtext.util.CancelIndicator;
14
15import com.google.common.collect.ImmutableList;
16import com.google.inject.Inject;
17
18import tools.refinery.language.model.ProblemUtil;
19import tools.refinery.language.model.problem.ClassDeclaration;
20import tools.refinery.language.model.problem.NamedElement;
21import tools.refinery.language.model.problem.Node;
22import tools.refinery.language.model.problem.PredicateDefinition;
23import tools.refinery.language.model.problem.ProblemPackage;
24import tools.refinery.language.model.problem.ReferenceDeclaration;
25
26public class ProblemSemanticHighlightingCalculator extends DefaultSemanticHighlightingCalculator {
27 private static final String BUILTIN_CLASS = "builtin";
28 private static final String ABSTRACT_CLASS = "abstract";
29 private static final String CONTAINMENT_CLASS = "containment";
30 private static final String ERROR_CLASS = "error";
31 private static final String NODE_CLASS = "node";
32 private static final String INDIVIDUAL_NODE_CLASS = "individual";
33 private static final String NEW_NODE_CLASS = "new";
34
35 @Inject
36 private OperationCanceledManager operationCanceledManager;
37
38 @Override
39 protected boolean highlightElement(EObject object, IHighlightedPositionAcceptor acceptor,
40 CancelIndicator cancelIndicator) {
41 highlightName(object, acceptor);
42 highlightCrossReferences(object, acceptor, cancelIndicator);
43 return false;
44 }
45
46 protected void highlightName(EObject object, IHighlightedPositionAcceptor acceptor) {
47 if (!(object instanceof NamedElement)) {
48 return;
49 }
50 String[] highlightClass = getHighlightClass(object, null);
51 if (highlightClass.length > 0) {
52 highlightFeature(acceptor, object, ProblemPackage.Literals.NAMED_ELEMENT__NAME, highlightClass);
53 }
54 }
55
56 protected void highlightCrossReferences(EObject object, IHighlightedPositionAcceptor acceptor,
57 CancelIndicator cancelIndicator) {
58 for (EReference reference : object.eClass().getEAllReferences()) {
59 if (reference.isContainment()) {
60 continue;
61 }
62 operationCanceledManager.checkCanceled(cancelIndicator);
63 if (reference.isMany()) {
64 highlightManyValues(object, reference, acceptor);
65 } else {
66 highlightSingleValue(object, reference, acceptor);
67 }
68 }
69 }
70
71 protected void highlightSingleValue(EObject object, EReference reference, IHighlightedPositionAcceptor acceptor) {
72 EObject valueObj = (EObject) object.eGet(reference);
73 String[] highlightClass = getHighlightClass(valueObj, reference);
74 if (highlightClass.length > 0) {
75 highlightFeature(acceptor, object, reference, highlightClass);
76 }
77 }
78
79 protected void highlightManyValues(EObject object, EReference reference, IHighlightedPositionAcceptor acceptor) {
80 @SuppressWarnings("unchecked")
81 EList<? extends EObject> values = (EList<? extends EObject>) object.eGet(reference);
82 List<INode> nodes = NodeModelUtils.findNodesForFeature(object, reference);
83 int size = Math.min(values.size(), nodes.size());
84 for (var i = 0; i < size; i++) {
85 EObject valueInList = values.get(i);
86 INode node = nodes.get(i);
87 String[] highlightClass = getHighlightClass(valueInList, reference);
88 if (highlightClass.length > 0) {
89 highlightNode(acceptor, node, highlightClass);
90 }
91 }
92 }
93
94 protected String[] getHighlightClass(EObject eObject, EReference reference) {
95 if (ProblemUtil.isBuiltIn(eObject)) {
96 return new String[] { BUILTIN_CLASS };
97 }
98 ImmutableList.Builder<String> classesBuilder = ImmutableList.builder();
99 if (eObject instanceof ClassDeclaration classDeclaration && classDeclaration.isAbstract()) {
100 classesBuilder.add(ABSTRACT_CLASS);
101 }
102 if (eObject instanceof ReferenceDeclaration referenceDeclaration && referenceDeclaration.isContainment()) {
103 classesBuilder.add(CONTAINMENT_CLASS);
104 }
105 if (eObject instanceof PredicateDefinition predicateDefinition && predicateDefinition.isError()) {
106 classesBuilder.add(ERROR_CLASS);
107 }
108 if (eObject instanceof Node node) {
109 if (reference == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE) {
110 classesBuilder.add(NODE_CLASS);
111 }
112 if (ProblemUtil.isIndividualNode(node)) {
113 classesBuilder.add(INDIVIDUAL_NODE_CLASS);
114 }
115 if (ProblemUtil.isNewNode(node)) {
116 classesBuilder.add(NEW_NODE_CLASS);
117 }
118 }
119 List<String> classes = classesBuilder.build();
120 return classes.toArray(new String[0]);
121 }
122}
diff --git a/subprojects/language-model/META-INF/MANIFEST.MF b/subprojects/language-model/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..96617f85
--- /dev/null
+++ b/subprojects/language-model/META-INF/MANIFEST.MF
@@ -0,0 +1,17 @@
1Manifest-Version: 1.0
2Bundle-ManifestVersion: 2
3Bundle-Name: %pluginName
4Bundle-SymbolicName: language-model;singleton:=true
5Automatic-Module-Name: language-model
6Bundle-Version: 0.0.0.qualifier
7Bundle-ClassPath: .
8Bundle-Vendor: %providerName
9Bundle-Localization: plugin
10Bundle-RequiredExecutionEnvironment: JavaSE-17
11Export-Package: tools.refinery.language.model.problem,
12 tools.refinery.language.model.problem.impl,
13 tools.refinery.language.model.problem.util
14Require-Bundle: org.eclipse.emf.ecore;visibility:=reexport,
15 org.eclipse.emf.ecore.xmi;visibility:=reexport,
16 org.eclipse.core.runtime
17Bundle-ActivationPolicy: lazy
diff --git a/subprojects/language-model/build.gradle b/subprojects/language-model/build.gradle
new file mode 100644
index 00000000..2cbddddb
--- /dev/null
+++ b/subprojects/language-model/build.gradle
@@ -0,0 +1,56 @@
1plugins {
2 id 'java-test-fixtures'
3 id 'refinery-java-library'
4 id 'refinery-mwe2'
5 id 'refinery-sonarqube'
6}
7
8dependencies {
9 api libs.ecore
10 api libs.ecore.xmi
11 mwe2 libs.ecore.codegen
12 mwe2 libs.mwe.utils
13 mwe2 libs.mwe2.lib
14 mwe2 libs.xtext.core
15 mwe2 libs.xtext.xbase
16}
17
18sourceSets {
19 main {
20 java.srcDirs += ['src/main/emf-gen']
21 }
22}
23
24def generateEPackage = tasks.register('generateEPackage', JavaExec) {
25 mainClass = 'org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher'
26 classpath = configurations.mwe2
27 inputs.file 'src/main/java/tools/refinery/language/model/GenerateProblemModel.mwe2'
28 inputs.file 'src/main/resources/model/problem.ecore'
29 inputs.file 'src/main/resources/model/problem.genmodel'
30 outputs.dir 'src/main/emf-gen'
31 args += 'src/main/java/tools/refinery/language/model/GenerateProblemModel.mwe2'
32 args += '-p'
33 args += "rootPath=/${projectDir}"
34}
35
36for (taskName in ['compileJava', 'processResources', 'generateEclipseSourceFolders']) {
37 tasks.named(taskName) {
38 dependsOn generateEPackage
39 }
40}
41
42tasks.named('clean') {
43 delete 'src/main/emf-gen'
44}
45
46sonarqube.properties {
47 properties['sonar.exclusions'] += [
48 'src/main/emf-gen/**',
49 ]
50}
51
52eclipse.project.natures += [
53 'org.eclipse.sirius.nature.modelingproject',
54 'org.eclipse.pde.PluginNature',
55 'org.eclipse.xtext.ui.shared.xtextNature'
56]
diff --git a/subprojects/language-model/build.properties b/subprojects/language-model/build.properties
new file mode 100644
index 00000000..65dfc7c4
--- /dev/null
+++ b/subprojects/language-model/build.properties
@@ -0,0 +1,11 @@
1#
2
3bin.includes = .,\
4 src/main/resources/model/,\
5 META-INF/,\
6 plugin.xml,\
7 plugin.properties
8jars.compile.order = .
9source.. = src/main/emf-gen/,\
10 src/main/java/,\
11 src/main/resources/
diff --git a/subprojects/language-model/plugin.properties b/subprojects/language-model/plugin.properties
new file mode 100644
index 00000000..c4fb7e23
--- /dev/null
+++ b/subprojects/language-model/plugin.properties
@@ -0,0 +1,4 @@
1#
2
3pluginName = tools.refinery.language.model
4providerName = refinery.tools
diff --git a/subprojects/language-model/plugin.xml b/subprojects/language-model/plugin.xml
new file mode 100644
index 00000000..4ca005a8
--- /dev/null
+++ b/subprojects/language-model/plugin.xml
@@ -0,0 +1,24 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<?eclipse version="3.0"?>
3
4<!--
5-->
6
7<plugin>
8
9 <extension point="org.eclipse.emf.ecore.generated_package">
10 <!-- @generated problem -->
11 <package
12 uri="https://refinery.tools/emf/2021/Problem"
13 class="tools.refinery.language.model.problem.ProblemPackage"
14 genModel="src/main/resources/model/problem.genmodel"/>
15 </extension>
16
17 <extension point="org.eclipse.emf.ecore.extension_parser">
18 <!-- @generated problem -->
19 <parser
20 type="problem_xmi"
21 class="tools.refinery.language.model.problem.util.ProblemResourceFactoryImpl"/>
22 </extension>
23
24</plugin>
diff --git a/subprojects/language-model/problem.aird b/subprojects/language-model/problem.aird
new file mode 100644
index 00000000..027070bf
--- /dev/null
+++ b/subprojects/language-model/problem.aird
@@ -0,0 +1,3646 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:description="http://www.eclipse.org/sirius/description/1.1.0" xmlns:description_1="http://www.eclipse.org/sirius/diagram/description/1.1.0" xmlns:diagram="http://www.eclipse.org/sirius/diagram/1.1.0" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:notation="http://www.eclipse.org/gmf/runtime/1.0.3/notation" xmlns:style="http://www.eclipse.org/sirius/diagram/description/style/1.1.0" xmlns:viewpoint="http://www.eclipse.org/sirius/1.1.0" xsi:schemaLocation="http://www.eclipse.org/sirius/description/1.1.0 http://www.eclipse.org/sirius/1.1.0#//description http://www.eclipse.org/sirius/diagram/description/1.1.0 http://www.eclipse.org/sirius/diagram/1.1.0#//description http://www.eclipse.org/sirius/diagram/description/style/1.1.0 http://www.eclipse.org/sirius/diagram/1.1.0#//description/style">
3 <viewpoint:DAnalysis uid="_CqOewKA4EeuqkpDnuik1sg" selectedViews="_CsAAYKA4EeuqkpDnuik1sg" version="14.5.1.202106111100">
4 <semanticResources>src/main/resources/model/problem.ecore</semanticResources>
5 <semanticResources>src/main/resources/model/problem.genmodel</semanticResources>
6 <semanticResources>build/resources/main/model/problem.ecore</semanticResources>
7 <semanticResources>build/resources/main/model/problem.genmodel</semanticResources>
8 <ownedViews xmi:type="viewpoint:DView" uid="_CsAAYKA4EeuqkpDnuik1sg">
9 <viewpoint xmi:type="description:Viewpoint" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']"/>
10 <ownedRepresentationDescriptors xmi:type="viewpoint:DRepresentationDescriptor" uid="_CsYa4KA4EeuqkpDnuik1sg" name="problem" repPath="#_CsUwgKA4EeuqkpDnuik1sg" changeId="0181b56c-ca28-4ed2-99c9-6ae4164f5993">
11 <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']"/>
12 <target xmi:type="ecore:EPackage" href="src/main/resources/model/problem.ecore#/"/>
13 </ownedRepresentationDescriptors>
14 </ownedViews>
15 </viewpoint:DAnalysis>
16 <diagram:DSemanticDiagram uid="_CsUwgKA4EeuqkpDnuik1sg">
17 <ownedAnnotationEntries xmi:type="description:AnnotationEntry" uid="_CsZB8KA4EeuqkpDnuik1sg" source="GMF_DIAGRAMS">
18 <data xmi:type="notation:Diagram" xmi:id="_CsZB8aA4EeuqkpDnuik1sg" type="Sirius" element="_CsUwgKA4EeuqkpDnuik1sg" measurementUnit="Pixel">
19 <children xmi:type="notation:Node" xmi:id="_D1D6MKA4EeuqkpDnuik1sg" type="2003" element="_D05iIKA4EeuqkpDnuik1sg">
20 <children xmi:type="notation:Node" xmi:id="_D1EhQKA4EeuqkpDnuik1sg" type="5007"/>
21 <children xmi:type="notation:Node" xmi:id="_D1FIUKA4EeuqkpDnuik1sg" type="7004">
22 <styles xmi:type="notation:SortingStyle" xmi:id="_D1FIUaA4EeuqkpDnuik1sg"/>
23 <styles xmi:type="notation:FilteringStyle" xmi:id="_D1FIUqA4EeuqkpDnuik1sg"/>
24 </children>
25 <styles xmi:type="notation:ShapeStyle" xmi:id="_D1D6MaA4EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
26 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_D1D6MqA4EeuqkpDnuik1sg" x="804" y="180" width="120" height="100"/>
27 </children>
28 <children xmi:type="notation:Node" xmi:id="_EfWNUKA4EeuqkpDnuik1sg" type="2003" element="_EfNqcKA4EeuqkpDnuik1sg">
29 <children xmi:type="notation:Node" xmi:id="_EfXbcKA4EeuqkpDnuik1sg" type="5007"/>
30 <children xmi:type="notation:Node" xmi:id="_EfXbcaA4EeuqkpDnuik1sg" type="7004">
31 <styles xmi:type="notation:SortingStyle" xmi:id="_EfXbcqA4EeuqkpDnuik1sg"/>
32 <styles xmi:type="notation:FilteringStyle" xmi:id="_EfXbc6A4EeuqkpDnuik1sg"/>
33 </children>
34 <styles xmi:type="notation:ShapeStyle" xmi:id="_EfWNUaA4EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
35 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_EfWNUqA4EeuqkpDnuik1sg" x="624" y="528" width="120" height="100"/>
36 </children>
37 <children xmi:type="notation:Node" xmi:id="_JT0o8KA4EeuqkpDnuik1sg" type="2003" element="_JTstIKA4EeuqkpDnuik1sg">
38 <children xmi:type="notation:Node" xmi:id="_JT1QAKA4EeuqkpDnuik1sg" type="5007"/>
39 <children xmi:type="notation:Node" xmi:id="_JT13EKA4EeuqkpDnuik1sg" type="7004">
40 <children xmi:type="notation:Node" xmi:id="_SrcfoKA4EeuqkpDnuik1sg" type="3010" element="_SrSuoKA4EeuqkpDnuik1sg">
41 <styles xmi:type="notation:FontStyle" xmi:id="_SrcfoaA4EeuqkpDnuik1sg" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/>
42 <layoutConstraint xmi:type="notation:Location" xmi:id="_SrcfoqA4EeuqkpDnuik1sg"/>
43 </children>
44 <styles xmi:type="notation:SortingStyle" xmi:id="_JT13EaA4EeuqkpDnuik1sg"/>
45 <styles xmi:type="notation:FilteringStyle" xmi:id="_JT13EqA4EeuqkpDnuik1sg"/>
46 </children>
47 <styles xmi:type="notation:ShapeStyle" xmi:id="_JT0o8aA4EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
48 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_JT0o8qA4EeuqkpDnuik1sg" x="672" y="720" width="120" height="100"/>
49 </children>
50 <children xmi:type="notation:Node" xmi:id="_c-HCQKA4EeuqkpDnuik1sg" type="2003" element="_c-A7oKA4EeuqkpDnuik1sg">
51 <children xmi:type="notation:Node" xmi:id="_c-HCQ6A4EeuqkpDnuik1sg" type="5007"/>
52 <children xmi:type="notation:Node" xmi:id="_c-HCRKA4EeuqkpDnuik1sg" type="7004">
53 <children xmi:type="notation:Node" xmi:id="_HjIR8KA5EeuqkpDnuik1sg" type="3010" element="_HjDZcKA5EeuqkpDnuik1sg">
54 <styles xmi:type="notation:FontStyle" xmi:id="_HjIR8aA5EeuqkpDnuik1sg" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/>
55 <layoutConstraint xmi:type="notation:Location" xmi:id="_HjIR8qA5EeuqkpDnuik1sg"/>
56 </children>
57 <styles xmi:type="notation:SortingStyle" xmi:id="_c-HCRaA4EeuqkpDnuik1sg"/>
58 <styles xmi:type="notation:FilteringStyle" xmi:id="_c-HCRqA4EeuqkpDnuik1sg"/>
59 </children>
60 <styles xmi:type="notation:ShapeStyle" xmi:id="_c-HCQaA4EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
61 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_c-HCQqA4EeuqkpDnuik1sg" x="588" y="960" width="147" height="100"/>
62 </children>
63 <children xmi:type="notation:Node" xmi:id="_RzZA0KA5EeuqkpDnuik1sg" type="2003" element="_RzK-YKA5EeuqkpDnuik1sg">
64 <children xmi:type="notation:Node" xmi:id="_RzZn4KA5EeuqkpDnuik1sg" type="5007"/>
65 <children xmi:type="notation:Node" xmi:id="_RzZn4aA5EeuqkpDnuik1sg" type="7004">
66 <children xmi:type="notation:Node" xmi:id="_S-2uYKA5EeuqkpDnuik1sg" type="3010" element="_S-lBkKA5EeuqkpDnuik1sg">
67 <styles xmi:type="notation:FontStyle" xmi:id="_S-2uYaA5EeuqkpDnuik1sg" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/>
68 <layoutConstraint xmi:type="notation:Location" xmi:id="_S-2uYqA5EeuqkpDnuik1sg"/>
69 </children>
70 <styles xmi:type="notation:SortingStyle" xmi:id="_RzZn4qA5EeuqkpDnuik1sg"/>
71 <styles xmi:type="notation:FilteringStyle" xmi:id="_RzZn46A5EeuqkpDnuik1sg"/>
72 </children>
73 <styles xmi:type="notation:ShapeStyle" xmi:id="_RzZA0aA5EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
74 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_RzZA0qA5EeuqkpDnuik1sg" x="1056" y="24" width="120" height="100"/>
75 </children>
76 <children xmi:type="notation:Node" xmi:id="_fit3kKA5EeuqkpDnuik1sg" type="2003" element="_fihqUKA5EeuqkpDnuik1sg">
77 <children xmi:type="notation:Node" xmi:id="_fit3k6A5EeuqkpDnuik1sg" type="5007"/>
78 <children xmi:type="notation:Node" xmi:id="_fit3lKA5EeuqkpDnuik1sg" type="7004">
79 <children xmi:type="notation:Node" xmi:id="_sv1ZIKA5EeuqkpDnuik1sg" type="3010" element="_svs2QKA5EeuqkpDnuik1sg">
80 <styles xmi:type="notation:FontStyle" xmi:id="_sv1ZIaA5EeuqkpDnuik1sg" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/>
81 <layoutConstraint xmi:type="notation:Location" xmi:id="_sv1ZIqA5EeuqkpDnuik1sg"/>
82 </children>
83 <children xmi:type="notation:Node" xmi:id="_7sgDoCrcEeyyC-O0_LlY9w" type="3010" element="_7sQzECrcEeyyC-O0_LlY9w">
84 <styles xmi:type="notation:FontStyle" xmi:id="_7sgDoSrcEeyyC-O0_LlY9w" fontColor="2697711" fontName="Segoe UI" fontHeight="8"/>
85 <layoutConstraint xmi:type="notation:Location" xmi:id="_7sgDoircEeyyC-O0_LlY9w"/>
86 </children>
87 <styles xmi:type="notation:SortingStyle" xmi:id="_fit3laA5EeuqkpDnuik1sg"/>
88 <styles xmi:type="notation:FilteringStyle" xmi:id="_fit3lqA5EeuqkpDnuik1sg"/>
89 </children>
90 <styles xmi:type="notation:ShapeStyle" xmi:id="_fit3kaA5EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
91 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_fit3kqA5EeuqkpDnuik1sg" x="1385" y="720" width="150" height="100"/>
92 </children>
93 <children xmi:type="notation:Node" xmi:id="_QKLK0KA6EeuqkpDnuik1sg" type="2003" element="_QKD2EKA6EeuqkpDnuik1sg">
94 <children xmi:type="notation:Node" xmi:id="_QKLK06A6EeuqkpDnuik1sg" type="5007"/>
95 <children xmi:type="notation:Node" xmi:id="_QKLK1KA6EeuqkpDnuik1sg" type="7004">
96 <styles xmi:type="notation:SortingStyle" xmi:id="_QKLK1aA6EeuqkpDnuik1sg"/>
97 <styles xmi:type="notation:FilteringStyle" xmi:id="_QKLK1qA6EeuqkpDnuik1sg"/>
98 </children>
99 <styles xmi:type="notation:ShapeStyle" xmi:id="_QKLK0aA6EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
100 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_QKLK0qA6EeuqkpDnuik1sg" x="1236" y="960" width="120" height="100"/>
101 </children>
102 <children xmi:type="notation:Node" xmi:id="_jP6FkKA6EeuqkpDnuik1sg" type="2003" element="_jPpm4KA6EeuqkpDnuik1sg">
103 <children xmi:type="notation:Node" xmi:id="_jP6soKA6EeuqkpDnuik1sg" type="5007"/>
104 <children xmi:type="notation:Node" xmi:id="_jP6soaA6EeuqkpDnuik1sg" type="7004">
105 <styles xmi:type="notation:SortingStyle" xmi:id="_jP6soqA6EeuqkpDnuik1sg"/>
106 <styles xmi:type="notation:FilteringStyle" xmi:id="_jP6so6A6EeuqkpDnuik1sg"/>
107 </children>
108 <styles xmi:type="notation:ShapeStyle" xmi:id="_jP6FkaA6EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
109 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_jP6FkqA6EeuqkpDnuik1sg" x="1236" y="720" width="120" height="100"/>
110 </children>
111 <children xmi:type="notation:Node" xmi:id="_sdPX0KA6EeuqkpDnuik1sg" type="2003" element="_sc_gMKA6EeuqkpDnuik1sg">
112 <children xmi:type="notation:Node" xmi:id="_sdP-4KA6EeuqkpDnuik1sg" type="5007"/>
113 <children xmi:type="notation:Node" xmi:id="_sdP-4aA6EeuqkpDnuik1sg" type="7004">
114 <styles xmi:type="notation:SortingStyle" xmi:id="_sdP-4qA6EeuqkpDnuik1sg"/>
115 <styles xmi:type="notation:FilteringStyle" xmi:id="_sdP-46A6EeuqkpDnuik1sg"/>
116 </children>
117 <styles xmi:type="notation:ShapeStyle" xmi:id="_sdPX0aA6EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
118 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_sdPX0qA6EeuqkpDnuik1sg" x="1416" y="960" width="120" height="100"/>
119 </children>
120 <children xmi:type="notation:Node" xmi:id="_4k5GIKA6EeuqkpDnuik1sg" type="2003" element="_4k00sKA6EeuqkpDnuik1sg">
121 <children xmi:type="notation:Node" xmi:id="_4k5GI6A6EeuqkpDnuik1sg" type="5007"/>
122 <children xmi:type="notation:Node" xmi:id="_4k5GJKA6EeuqkpDnuik1sg" type="7004">
123 <styles xmi:type="notation:SortingStyle" xmi:id="_4k5GJaA6EeuqkpDnuik1sg"/>
124 <styles xmi:type="notation:FilteringStyle" xmi:id="_4k5GJqA6EeuqkpDnuik1sg"/>
125 </children>
126 <styles xmi:type="notation:ShapeStyle" xmi:id="_4k5GIaA6EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
127 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_4k5GIqA6EeuqkpDnuik1sg" x="1360" y="1128" width="120" height="100"/>
128 </children>
129 <children xmi:type="notation:Node" xmi:id="_6KEUMKA6EeuqkpDnuik1sg" type="2003" element="_6J_bsKA6EeuqkpDnuik1sg">
130 <children xmi:type="notation:Node" xmi:id="_6KEUM6A6EeuqkpDnuik1sg" type="5007"/>
131 <children xmi:type="notation:Node" xmi:id="_6KEUNKA6EeuqkpDnuik1sg" type="7004">
132 <children xmi:type="notation:Node" xmi:id="_UAq-cKA-EeuqkpDnuik1sg" type="3010" element="_UAle4KA-EeuqkpDnuik1sg">
133 <styles xmi:type="notation:FontStyle" xmi:id="_UAq-caA-EeuqkpDnuik1sg" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/>
134 <layoutConstraint xmi:type="notation:Location" xmi:id="_UAq-cqA-EeuqkpDnuik1sg"/>
135 </children>
136 <styles xmi:type="notation:SortingStyle" xmi:id="_6KEUNaA6EeuqkpDnuik1sg"/>
137 <styles xmi:type="notation:FilteringStyle" xmi:id="_6KEUNqA6EeuqkpDnuik1sg"/>
138 </children>
139 <styles xmi:type="notation:ShapeStyle" xmi:id="_6KEUMaA6EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
140 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_6KEUMqA6EeuqkpDnuik1sg" x="1236" y="1284" width="120" height="100"/>
141 </children>
142 <children xmi:type="notation:Node" xmi:id="_-O-UEKA6EeuqkpDnuik1sg" type="2003" element="_-O6CoKA6EeuqkpDnuik1sg">
143 <children xmi:type="notation:Node" xmi:id="_-O-7IKA6EeuqkpDnuik1sg" type="5007"/>
144 <children xmi:type="notation:Node" xmi:id="_-O-7IaA6EeuqkpDnuik1sg" type="7004">
145 <styles xmi:type="notation:SortingStyle" xmi:id="_-O-7IqA6EeuqkpDnuik1sg"/>
146 <styles xmi:type="notation:FilteringStyle" xmi:id="_-O-7I6A6EeuqkpDnuik1sg"/>
147 </children>
148 <styles xmi:type="notation:ShapeStyle" xmi:id="_-O-UEaA6EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
149 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_-O-UEqA6EeuqkpDnuik1sg" x="1656" y="1752" width="120" height="100"/>
150 </children>
151 <children xmi:type="notation:Node" xmi:id="_V6pfMKA7EeuqkpDnuik1sg" type="2003" element="_V6YZcKA7EeuqkpDnuik1sg">
152 <children xmi:type="notation:Node" xmi:id="_V6qGQKA7EeuqkpDnuik1sg" type="5007"/>
153 <children xmi:type="notation:Node" xmi:id="_V6qGQaA7EeuqkpDnuik1sg" type="7004">
154 <styles xmi:type="notation:SortingStyle" xmi:id="_V6qGQqA7EeuqkpDnuik1sg"/>
155 <styles xmi:type="notation:FilteringStyle" xmi:id="_V6qGQ6A7EeuqkpDnuik1sg"/>
156 </children>
157 <styles xmi:type="notation:ShapeStyle" xmi:id="_V6pfMaA7EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
158 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_V6pfMqA7EeuqkpDnuik1sg" x="1546" y="1452" width="120" height="100"/>
159 </children>
160 <children xmi:type="notation:Node" xmi:id="_rRhWIKA7EeuqkpDnuik1sg" type="2003" element="_rRcdoKA7EeuqkpDnuik1sg">
161 <children xmi:type="notation:Node" xmi:id="_rRh9MKA7EeuqkpDnuik1sg" type="5007"/>
162 <children xmi:type="notation:Node" xmi:id="_rRh9MaA7EeuqkpDnuik1sg" type="7004">
163 <styles xmi:type="notation:SortingStyle" xmi:id="_rRh9MqA7EeuqkpDnuik1sg"/>
164 <styles xmi:type="notation:FilteringStyle" xmi:id="_rRh9M6A7EeuqkpDnuik1sg"/>
165 </children>
166 <styles xmi:type="notation:ShapeStyle" xmi:id="_rRhWIaA7EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
167 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_rRhWIqA7EeuqkpDnuik1sg" x="1620" y="720" width="147" height="100"/>
168 </children>
169 <children xmi:type="notation:Node" xmi:id="_p9wywKA8EeuqkpDnuik1sg" type="2003" element="_p9d30KA8EeuqkpDnuik1sg">
170 <children xmi:type="notation:Node" xmi:id="_p9xZ0KA8EeuqkpDnuik1sg" type="5007"/>
171 <children xmi:type="notation:Node" xmi:id="_p9xZ0aA8EeuqkpDnuik1sg" type="7004">
172 <children xmi:type="notation:Node" xmi:id="_NAri8KA9EeuqkpDnuik1sg" type="3010" element="_NAnRgKA9EeuqkpDnuik1sg">
173 <styles xmi:type="notation:FontStyle" xmi:id="_NAri8aA9EeuqkpDnuik1sg" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/>
174 <layoutConstraint xmi:type="notation:Location" xmi:id="_NAri8qA9EeuqkpDnuik1sg"/>
175 </children>
176 <children xmi:type="notation:Node" xmi:id="_Y3GDAAGzEey7cfH5K6RyCw" type="3010" element="_Y2m60AGzEey7cfH5K6RyCw">
177 <styles xmi:type="notation:FontStyle" xmi:id="_Y3GDAQGzEey7cfH5K6RyCw" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/>
178 <layoutConstraint xmi:type="notation:Location" xmi:id="_Y3GDAgGzEey7cfH5K6RyCw"/>
179 </children>
180 <styles xmi:type="notation:SortingStyle" xmi:id="_p9xZ0qA8EeuqkpDnuik1sg"/>
181 <styles xmi:type="notation:FilteringStyle" xmi:id="_p9xZ06A8EeuqkpDnuik1sg"/>
182 </children>
183 <styles xmi:type="notation:ShapeStyle" xmi:id="_p9wywaA8EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
184 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_p9wywqA8EeuqkpDnuik1sg" x="2163" y="528" width="120" height="111"/>
185 </children>
186 <children xmi:type="notation:Node" xmi:id="_xsq_MKA8EeuqkpDnuik1sg" type="2003" element="_xsYrUKA8EeuqkpDnuik1sg">
187 <children xmi:type="notation:Node" xmi:id="_xsrmQKA8EeuqkpDnuik1sg" type="5007"/>
188 <children xmi:type="notation:Node" xmi:id="_xsrmQaA8EeuqkpDnuik1sg" type="7004">
189 <styles xmi:type="notation:SortingStyle" xmi:id="_xsrmQqA8EeuqkpDnuik1sg"/>
190 <styles xmi:type="notation:FilteringStyle" xmi:id="_xsrmQ6A8EeuqkpDnuik1sg"/>
191 </children>
192 <styles xmi:type="notation:ShapeStyle" xmi:id="_xsq_MaA8EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
193 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_xsq_MqA8EeuqkpDnuik1sg" x="1020" y="336" width="120" height="100"/>
194 </children>
195 <children xmi:type="notation:Node" xmi:id="_BMfjMKA9EeuqkpDnuik1sg" type="2003" element="_BMXnYKA9EeuqkpDnuik1sg">
196 <children xmi:type="notation:Node" xmi:id="_BMfjM6A9EeuqkpDnuik1sg" type="5007"/>
197 <children xmi:type="notation:Node" xmi:id="_BMfjNKA9EeuqkpDnuik1sg" type="7004">
198 <children xmi:type="notation:Node" xmi:id="_B6eDgKA9EeuqkpDnuik1sg" type="3010" element="_B6IFQKA9EeuqkpDnuik1sg">
199 <styles xmi:type="notation:FontStyle" xmi:id="_B6eDgaA9EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
200 <layoutConstraint xmi:type="notation:Location" xmi:id="_B6eDgqA9EeuqkpDnuik1sg"/>
201 </children>
202 <children xmi:type="notation:Node" xmi:id="_D8xScKA9EeuqkpDnuik1sg" type="3010" element="_D8ciUKA9EeuqkpDnuik1sg">
203 <styles xmi:type="notation:FontStyle" xmi:id="_D8xScaA9EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
204 <layoutConstraint xmi:type="notation:Location" xmi:id="_D8xScqA9EeuqkpDnuik1sg"/>
205 </children>
206 <children xmi:type="notation:Node" xmi:id="_JW_fcKA9EeuqkpDnuik1sg" type="3010" element="_JWqvUKA9EeuqkpDnuik1sg">
207 <styles xmi:type="notation:FontStyle" xmi:id="_JW_fcaA9EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
208 <layoutConstraint xmi:type="notation:Location" xmi:id="_JW_fcqA9EeuqkpDnuik1sg"/>
209 </children>
210 <children xmi:type="notation:Node" xmi:id="_9LOjcAGzEey7cfH5K6RyCw" type="3010" element="_9K7BcAGzEey7cfH5K6RyCw">
211 <styles xmi:type="notation:FontStyle" xmi:id="_9LOjcQGzEey7cfH5K6RyCw" fontName="Noto Sans" fontHeight="8"/>
212 <layoutConstraint xmi:type="notation:Location" xmi:id="_9LOjcgGzEey7cfH5K6RyCw"/>
213 </children>
214 <styles xmi:type="notation:SortingStyle" xmi:id="_BMfjNaA9EeuqkpDnuik1sg"/>
215 <styles xmi:type="notation:FilteringStyle" xmi:id="_BMfjNqA9EeuqkpDnuik1sg"/>
216 </children>
217 <styles xmi:type="notation:ShapeStyle" xmi:id="_BMfjMaA9EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
218 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_BMfjMqA9EeuqkpDnuik1sg" x="1917" y="180" width="120" height="100"/>
219 </children>
220 <children xmi:type="notation:Node" xmi:id="_QUDYMKA9EeuqkpDnuik1sg" type="2003" element="_QTzgkKA9EeuqkpDnuik1sg">
221 <children xmi:type="notation:Node" xmi:id="_QUDYM6A9EeuqkpDnuik1sg" type="5007"/>
222 <children xmi:type="notation:Node" xmi:id="_QUDYNKA9EeuqkpDnuik1sg" type="7004">
223 <styles xmi:type="notation:SortingStyle" xmi:id="_QUDYNaA9EeuqkpDnuik1sg"/>
224 <styles xmi:type="notation:FilteringStyle" xmi:id="_QUDYNqA9EeuqkpDnuik1sg"/>
225 </children>
226 <styles xmi:type="notation:ShapeStyle" xmi:id="_QUDYMaA9EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
227 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_QUDYMqA9EeuqkpDnuik1sg" x="336" y="528" width="120" height="100"/>
228 </children>
229 <children xmi:type="notation:Node" xmi:id="_e73WIKA9EeuqkpDnuik1sg" type="2003" element="_e7ydoKA9EeuqkpDnuik1sg">
230 <children xmi:type="notation:Node" xmi:id="_e73WI6A9EeuqkpDnuik1sg" type="5007"/>
231 <children xmi:type="notation:Node" xmi:id="_e73WJKA9EeuqkpDnuik1sg" type="7004">
232 <styles xmi:type="notation:SortingStyle" xmi:id="_e73WJaA9EeuqkpDnuik1sg"/>
233 <styles xmi:type="notation:FilteringStyle" xmi:id="_e73WJqA9EeuqkpDnuik1sg"/>
234 </children>
235 <styles xmi:type="notation:ShapeStyle" xmi:id="_e73WIaA9EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
236 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_e73WIqA9EeuqkpDnuik1sg" x="804" y="336" width="120" height="100"/>
237 </children>
238 <children xmi:type="notation:Node" xmi:id="_zaq8oKA9EeuqkpDnuik1sg" type="2003" element="_zac6MKA9EeuqkpDnuik1sg">
239 <children xmi:type="notation:Node" xmi:id="_zarjsKA9EeuqkpDnuik1sg" type="5007"/>
240 <children xmi:type="notation:Node" xmi:id="_zarjsaA9EeuqkpDnuik1sg" type="7004">
241 <children xmi:type="notation:Node" xmi:id="_BtMnEKA-EeuqkpDnuik1sg" type="3010" element="_BtHHgKA-EeuqkpDnuik1sg">
242 <styles xmi:type="notation:FontStyle" xmi:id="_BtMnEaA-EeuqkpDnuik1sg" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/>
243 <layoutConstraint xmi:type="notation:Location" xmi:id="_BtMnEqA-EeuqkpDnuik1sg"/>
244 </children>
245 <styles xmi:type="notation:SortingStyle" xmi:id="_zarjsqA9EeuqkpDnuik1sg"/>
246 <styles xmi:type="notation:FilteringStyle" xmi:id="_zarjs6A9EeuqkpDnuik1sg"/>
247 </children>
248 <styles xmi:type="notation:ShapeStyle" xmi:id="_zaq8oaA9EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
249 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_zaq8oqA9EeuqkpDnuik1sg" x="336" y="720" width="120" height="99"/>
250 </children>
251 <children xmi:type="notation:Node" xmi:id="_Ren3cKBJEeuqkpDnuik1sg" type="2003" element="_ReiX4KBJEeuqkpDnuik1sg">
252 <children xmi:type="notation:Node" xmi:id="_Ren3c6BJEeuqkpDnuik1sg" type="5007"/>
253 <children xmi:type="notation:Node" xmi:id="_Ren3dKBJEeuqkpDnuik1sg" type="7004">
254 <styles xmi:type="notation:SortingStyle" xmi:id="_Ren3daBJEeuqkpDnuik1sg"/>
255 <styles xmi:type="notation:FilteringStyle" xmi:id="_Ren3dqBJEeuqkpDnuik1sg"/>
256 </children>
257 <styles xmi:type="notation:ShapeStyle" xmi:id="_Ren3caBJEeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
258 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Ren3cqBJEeuqkpDnuik1sg" x="336" y="960" width="120" height="100"/>
259 </children>
260 <children xmi:type="notation:Node" xmi:id="_Tx6IsKBJEeuqkpDnuik1sg" type="2003" element="_Tx0CEKBJEeuqkpDnuik1sg">
261 <children xmi:type="notation:Node" xmi:id="_Tx6Is6BJEeuqkpDnuik1sg" type="5007"/>
262 <children xmi:type="notation:Node" xmi:id="_Tx6ItKBJEeuqkpDnuik1sg" type="7004">
263 <children xmi:type="notation:Node" xmi:id="_icxaoKBJEeuqkpDnuik1sg" type="3010" element="_iccDcKBJEeuqkpDnuik1sg">
264 <styles xmi:type="notation:FontStyle" xmi:id="_icxaoaBJEeuqkpDnuik1sg" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/>
265 <layoutConstraint xmi:type="notation:Location" xmi:id="_icxaoqBJEeuqkpDnuik1sg"/>
266 </children>
267 <children xmi:type="notation:Node" xmi:id="_kKwmcKBJEeuqkpDnuik1sg" type="3010" element="_kKaoMKBJEeuqkpDnuik1sg">
268 <styles xmi:type="notation:FontStyle" xmi:id="_kKwmcaBJEeuqkpDnuik1sg" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/>
269 <layoutConstraint xmi:type="notation:Location" xmi:id="_kKwmcqBJEeuqkpDnuik1sg"/>
270 </children>
271 <styles xmi:type="notation:SortingStyle" xmi:id="_Tx6ItaBJEeuqkpDnuik1sg"/>
272 <styles xmi:type="notation:FilteringStyle" xmi:id="_Tx6ItqBJEeuqkpDnuik1sg"/>
273 </children>
274 <styles xmi:type="notation:ShapeStyle" xmi:id="_Tx6IsaBJEeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
275 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Tx6IsqBJEeuqkpDnuik1sg" x="240" y="1128" width="120" height="100"/>
276 </children>
277 <children xmi:type="notation:Node" xmi:id="_aPNjUKBJEeuqkpDnuik1sg" type="2003" element="_aPIDwKBJEeuqkpDnuik1sg">
278 <children xmi:type="notation:Node" xmi:id="_aPOKYKBJEeuqkpDnuik1sg" type="5007"/>
279 <children xmi:type="notation:Node" xmi:id="_aPOKYaBJEeuqkpDnuik1sg" type="7004">
280 <children xmi:type="notation:Node" xmi:id="_lhdToKBJEeuqkpDnuik1sg" type="3010" element="_lhXNAKBJEeuqkpDnuik1sg">
281 <styles xmi:type="notation:FontStyle" xmi:id="_lhdToaBJEeuqkpDnuik1sg" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/>
282 <layoutConstraint xmi:type="notation:Location" xmi:id="_lhdToqBJEeuqkpDnuik1sg"/>
283 </children>
284 <styles xmi:type="notation:SortingStyle" xmi:id="_aPOKYqBJEeuqkpDnuik1sg"/>
285 <styles xmi:type="notation:FilteringStyle" xmi:id="_aPOKY6BJEeuqkpDnuik1sg"/>
286 </children>
287 <styles xmi:type="notation:ShapeStyle" xmi:id="_aPNjUaBJEeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
288 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_aPNjUqBJEeuqkpDnuik1sg" x="408" y="1128" width="120" height="100"/>
289 </children>
290 <children xmi:type="notation:Node" xmi:id="_D-lH8NYBEeuF_d0WEhR3Xw" type="2003" element="_D9_5INYBEeuF_d0WEhR3Xw">
291 <children xmi:type="notation:Node" xmi:id="_D-lvANYBEeuF_d0WEhR3Xw" type="5007"/>
292 <children xmi:type="notation:Node" xmi:id="_D-lvAdYBEeuF_d0WEhR3Xw" type="7004">
293 <styles xmi:type="notation:SortingStyle" xmi:id="_D-lvAtYBEeuF_d0WEhR3Xw"/>
294 <styles xmi:type="notation:FilteringStyle" xmi:id="_D-lvA9YBEeuF_d0WEhR3Xw"/>
295 </children>
296 <styles xmi:type="notation:ShapeStyle" xmi:id="_D-lH8dYBEeuF_d0WEhR3Xw" fontName="Noto Sans" fontHeight="8"/>
297 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_D-lH8tYBEeuF_d0WEhR3Xw" x="48" y="1128" width="147" height="100"/>
298 </children>
299 <children xmi:type="notation:Node" xmi:id="_IwsqwNYPEeuF_d0WEhR3Xw" type="2003" element="_IwJ4MNYPEeuF_d0WEhR3Xw">
300 <children xmi:type="notation:Node" xmi:id="_IwtR0NYPEeuF_d0WEhR3Xw" type="5007"/>
301 <children xmi:type="notation:Node" xmi:id="_IwtR0dYPEeuF_d0WEhR3Xw" type="7004">
302 <styles xmi:type="notation:SortingStyle" xmi:id="_IwtR0tYPEeuF_d0WEhR3Xw"/>
303 <styles xmi:type="notation:FilteringStyle" xmi:id="_IwtR09YPEeuF_d0WEhR3Xw"/>
304 </children>
305 <styles xmi:type="notation:ShapeStyle" xmi:id="_IwsqwdYPEeuF_d0WEhR3Xw" fontName="Noto Sans" fontHeight="8"/>
306 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_IwsqwtYPEeuF_d0WEhR3Xw" x="1117" y="1607" width="159" height="100"/>
307 </children>
308 <children xmi:type="notation:Node" xmi:id="_M6O-0NbGEeuymriYTNxK2g" type="2003" element="_M58q8NbGEeuymriYTNxK2g">
309 <children xmi:type="notation:Node" xmi:id="_M6Pl4NbGEeuymriYTNxK2g" type="5007"/>
310 <children xmi:type="notation:Node" xmi:id="_M6Pl4dbGEeuymriYTNxK2g" type="7004">
311 <styles xmi:type="notation:SortingStyle" xmi:id="_M6Pl4tbGEeuymriYTNxK2g"/>
312 <styles xmi:type="notation:FilteringStyle" xmi:id="_M6Pl49bGEeuymriYTNxK2g"/>
313 </children>
314 <styles xmi:type="notation:ShapeStyle" xmi:id="_M6O-0dbGEeuymriYTNxK2g" fontName="Noto Sans" fontHeight="8"/>
315 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_M6O-0tbGEeuymriYTNxK2g" x="903" y="720" width="120" height="100"/>
316 </children>
317 <children xmi:type="notation:Node" xmi:id="_Kw-vINbNEeuymriYTNxK2g" type="2003" element="_KwtCUNbNEeuymriYTNxK2g">
318 <children xmi:type="notation:Node" xmi:id="_Kw_WMNbNEeuymriYTNxK2g" type="5007"/>
319 <children xmi:type="notation:Node" xmi:id="_Kw_WMdbNEeuymriYTNxK2g" type="7004">
320 <styles xmi:type="notation:SortingStyle" xmi:id="_Kw_WMtbNEeuymriYTNxK2g"/>
321 <styles xmi:type="notation:FilteringStyle" xmi:id="_Kw_WM9bNEeuymriYTNxK2g"/>
322 </children>
323 <styles xmi:type="notation:ShapeStyle" xmi:id="_Kw-vIdbNEeuymriYTNxK2g" fontName="Noto Sans" fontHeight="8"/>
324 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Kw-vItbNEeuymriYTNxK2g" x="1137" y="180" width="120" height="100"/>
325 </children>
326 <children xmi:type="notation:Node" xmi:id="_IsM5ENd_EeufiOvRR5sVhg" type="2003" element="_IrcrINd_EeufiOvRR5sVhg">
327 <children xmi:type="notation:Node" xmi:id="_IsRxkNd_EeufiOvRR5sVhg" type="5007"/>
328 <children xmi:type="notation:Node" xmi:id="_IsRxkdd_EeufiOvRR5sVhg" type="7004">
329 <styles xmi:type="notation:SortingStyle" xmi:id="_IsRxktd_EeufiOvRR5sVhg"/>
330 <styles xmi:type="notation:FilteringStyle" xmi:id="_IsRxk9d_EeufiOvRR5sVhg"/>
331 </children>
332 <styles xmi:type="notation:ShapeStyle" xmi:id="_IsM5Edd_EeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
333 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_IsM5Etd_EeufiOvRR5sVhg" x="2284" y="1607" width="120" height="100"/>
334 </children>
335 <children xmi:type="notation:Node" xmi:id="_KdsE0Nd_EeufiOvRR5sVhg" type="2003" element="_KdTqUNd_EeufiOvRR5sVhg">
336 <children xmi:type="notation:Node" xmi:id="_Kdsr4Nd_EeufiOvRR5sVhg" type="5007"/>
337 <children xmi:type="notation:Node" xmi:id="_Kdsr4dd_EeufiOvRR5sVhg" type="7004">
338 <children xmi:type="notation:Node" xmi:id="_bremgNd_EeufiOvRR5sVhg" type="3010" element="_brB6kNd_EeufiOvRR5sVhg">
339 <styles xmi:type="notation:FontStyle" xmi:id="_bremgdd_EeufiOvRR5sVhg" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/>
340 <layoutConstraint xmi:type="notation:Location" xmi:id="_bremgtd_EeufiOvRR5sVhg"/>
341 </children>
342 <styles xmi:type="notation:SortingStyle" xmi:id="_Kdsr4td_EeufiOvRR5sVhg"/>
343 <styles xmi:type="notation:FilteringStyle" xmi:id="_Kdsr49d_EeufiOvRR5sVhg"/>
344 </children>
345 <styles xmi:type="notation:ShapeStyle" xmi:id="_KdsE0dd_EeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
346 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_KdsE0td_EeufiOvRR5sVhg" x="2140" y="1751" width="120" height="100"/>
347 </children>
348 <children xmi:type="notation:Node" xmi:id="_MAkM0Nd_EeufiOvRR5sVhg" type="2003" element="_MARR4Nd_EeufiOvRR5sVhg">
349 <children xmi:type="notation:Node" xmi:id="_MAkM09d_EeufiOvRR5sVhg" type="5007"/>
350 <children xmi:type="notation:Node" xmi:id="_MAkM1Nd_EeufiOvRR5sVhg" type="7004">
351 <children xmi:type="notation:Node" xmi:id="_exbq0Nd_EeufiOvRR5sVhg" type="3010" element="_exQEoNd_EeufiOvRR5sVhg">
352 <styles xmi:type="notation:FontStyle" xmi:id="_exbq0dd_EeufiOvRR5sVhg" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/>
353 <layoutConstraint xmi:type="notation:Location" xmi:id="_exbq0td_EeufiOvRR5sVhg"/>
354 </children>
355 <styles xmi:type="notation:SortingStyle" xmi:id="_MAkM1dd_EeufiOvRR5sVhg"/>
356 <styles xmi:type="notation:FilteringStyle" xmi:id="_MAkM1td_EeufiOvRR5sVhg"/>
357 </children>
358 <styles xmi:type="notation:ShapeStyle" xmi:id="_MAkM0dd_EeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
359 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_MAkM0td_EeufiOvRR5sVhg" x="2284" y="1751" width="120" height="100"/>
360 </children>
361 <children xmi:type="notation:Node" xmi:id="_RwPFYNd_EeufiOvRR5sVhg" type="2003" element="_Rv9_oNd_EeufiOvRR5sVhg">
362 <children xmi:type="notation:Node" xmi:id="_RwPFY9d_EeufiOvRR5sVhg" type="5007"/>
363 <children xmi:type="notation:Node" xmi:id="_RwPFZNd_EeufiOvRR5sVhg" type="7004">
364 <children xmi:type="notation:Node" xmi:id="_g44fgNd_EeufiOvRR5sVhg" type="3010" element="_g4rrMNd_EeufiOvRR5sVhg">
365 <styles xmi:type="notation:FontStyle" xmi:id="_g44fgdd_EeufiOvRR5sVhg" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/>
366 <layoutConstraint xmi:type="notation:Location" xmi:id="_g44fgtd_EeufiOvRR5sVhg"/>
367 </children>
368 <styles xmi:type="notation:SortingStyle" xmi:id="_RwPFZdd_EeufiOvRR5sVhg"/>
369 <styles xmi:type="notation:FilteringStyle" xmi:id="_RwPFZtd_EeufiOvRR5sVhg"/>
370 </children>
371 <styles xmi:type="notation:ShapeStyle" xmi:id="_RwPFYdd_EeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
372 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_RwPFYtd_EeufiOvRR5sVhg" x="2428" y="1751" width="120" height="100"/>
373 </children>
374 <children xmi:type="notation:Node" xmi:id="_mCh54Nd_EeufiOvRR5sVhg" type="2003" element="_mCSCQNd_EeufiOvRR5sVhg">
375 <children xmi:type="notation:Node" xmi:id="_mCh549d_EeufiOvRR5sVhg" type="5007"/>
376 <children xmi:type="notation:Node" xmi:id="_mCig8Nd_EeufiOvRR5sVhg" type="7004">
377 <styles xmi:type="notation:SortingStyle" xmi:id="_mCig8dd_EeufiOvRR5sVhg"/>
378 <styles xmi:type="notation:FilteringStyle" xmi:id="_mCig8td_EeufiOvRR5sVhg"/>
379 </children>
380 <styles xmi:type="notation:ShapeStyle" xmi:id="_mCh54dd_EeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
381 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_mCh54td_EeufiOvRR5sVhg" x="1375" y="1607" width="135" height="100"/>
382 </children>
383 <children xmi:type="notation:Node" xmi:id="_rwMAQNd_EeufiOvRR5sVhg" type="2003" element="_rwC2UNd_EeufiOvRR5sVhg">
384 <children xmi:type="notation:Node" xmi:id="_rwMAQ9d_EeufiOvRR5sVhg" type="5007"/>
385 <children xmi:type="notation:Node" xmi:id="_rwMARNd_EeufiOvRR5sVhg" type="7004">
386 <styles xmi:type="notation:SortingStyle" xmi:id="_rwMARdd_EeufiOvRR5sVhg"/>
387 <styles xmi:type="notation:FilteringStyle" xmi:id="_rwMARtd_EeufiOvRR5sVhg"/>
388 </children>
389 <styles xmi:type="notation:ShapeStyle" xmi:id="_rwMAQdd_EeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
390 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_rwMAQtd_EeufiOvRR5sVhg" x="1236" y="1452" width="120" height="100"/>
391 </children>
392 <children xmi:type="notation:Node" xmi:id="_OWhiINeAEeufiOvRR5sVhg" type="2003" element="_OWYYMNeAEeufiOvRR5sVhg">
393 <children xmi:type="notation:Node" xmi:id="_OWhiI9eAEeufiOvRR5sVhg" type="5007"/>
394 <children xmi:type="notation:Node" xmi:id="_OWhiJNeAEeufiOvRR5sVhg" type="7004">
395 <styles xmi:type="notation:SortingStyle" xmi:id="_OWhiJdeAEeufiOvRR5sVhg"/>
396 <styles xmi:type="notation:FilteringStyle" xmi:id="_OWhiJteAEeufiOvRR5sVhg"/>
397 </children>
398 <styles xmi:type="notation:ShapeStyle" xmi:id="_OWhiIdeAEeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
399 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_OWhiIteAEeufiOvRR5sVhg" x="2271" y="960" width="156" height="100"/>
400 </children>
401 <children xmi:type="notation:Node" xmi:id="_VikSENeAEeufiOvRR5sVhg" type="2003" element="_ViJbUNeAEeufiOvRR5sVhg">
402 <children xmi:type="notation:Node" xmi:id="_Vik5INeAEeufiOvRR5sVhg" type="5007"/>
403 <children xmi:type="notation:Node" xmi:id="_Vik5IdeAEeufiOvRR5sVhg" type="7004">
404 <styles xmi:type="notation:SortingStyle" xmi:id="_Vik5IteAEeufiOvRR5sVhg"/>
405 <styles xmi:type="notation:FilteringStyle" xmi:id="_Vik5I9eAEeufiOvRR5sVhg"/>
406 </children>
407 <styles xmi:type="notation:ShapeStyle" xmi:id="_VikSEdeAEeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
408 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_VikSEteAEeufiOvRR5sVhg" x="2155" y="708" width="135" height="100"/>
409 </children>
410 <children xmi:type="notation:Node" xmi:id="_dZlRoNeAEeufiOvRR5sVhg" type="2003" element="_dZVaANeAEeufiOvRR5sVhg">
411 <children xmi:type="notation:Node" xmi:id="_dZl4sNeAEeufiOvRR5sVhg" type="5007"/>
412 <children xmi:type="notation:Node" xmi:id="_dZl4sdeAEeufiOvRR5sVhg" type="7004">
413 <styles xmi:type="notation:SortingStyle" xmi:id="_dZl4steAEeufiOvRR5sVhg"/>
414 <styles xmi:type="notation:FilteringStyle" xmi:id="_dZl4s9eAEeufiOvRR5sVhg"/>
415 </children>
416 <styles xmi:type="notation:ShapeStyle" xmi:id="_dZlRodeAEeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
417 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_dZlRoteAEeufiOvRR5sVhg" x="2038" y="960" width="188" height="100"/>
418 </children>
419 <children xmi:type="notation:Node" xmi:id="_9Tu6ENeAEeufiOvRR5sVhg" type="2003" element="_9TjT4NeAEeufiOvRR5sVhg">
420 <children xmi:type="notation:Node" xmi:id="_9Tu6E9eAEeufiOvRR5sVhg" type="5007"/>
421 <children xmi:type="notation:Node" xmi:id="_9Tu6FNeAEeufiOvRR5sVhg" type="7004">
422 <styles xmi:type="notation:SortingStyle" xmi:id="_9Tu6FdeAEeufiOvRR5sVhg"/>
423 <styles xmi:type="notation:FilteringStyle" xmi:id="_9Tu6FteAEeufiOvRR5sVhg"/>
424 </children>
425 <styles xmi:type="notation:ShapeStyle" xmi:id="_9Tu6EdeAEeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
426 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_9Tu6EteAEeufiOvRR5sVhg" x="2384" y="527" width="144" height="100"/>
427 </children>
428 <children xmi:type="notation:Node" xmi:id="_pdJrwAGyEey7cfH5K6RyCw" type="2003" element="_pcXBkAGyEey7cfH5K6RyCw">
429 <children xmi:type="notation:Node" xmi:id="_pdOkQAGyEey7cfH5K6RyCw" type="5007"/>
430 <children xmi:type="notation:Node" xmi:id="_pdOkQQGyEey7cfH5K6RyCw" type="7004">
431 <styles xmi:type="notation:SortingStyle" xmi:id="_pdOkQgGyEey7cfH5K6RyCw"/>
432 <styles xmi:type="notation:FilteringStyle" xmi:id="_pdOkQwGyEey7cfH5K6RyCw"/>
433 </children>
434 <styles xmi:type="notation:ShapeStyle" xmi:id="_pdJrwQGyEey7cfH5K6RyCw" fontName="Noto Sans" fontHeight="8"/>
435 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_pdJrwgGyEey7cfH5K6RyCw" x="132" y="528" width="147" height="100"/>
436 </children>
437 <children xmi:type="notation:Node" xmi:id="_SNlYYAGzEey7cfH5K6RyCw" type="2003" element="_SNSdcAGzEey7cfH5K6RyCw">
438 <children xmi:type="notation:Node" xmi:id="_SNlYYwGzEey7cfH5K6RyCw" type="5007"/>
439 <children xmi:type="notation:Node" xmi:id="_SNlYZAGzEey7cfH5K6RyCw" type="7004">
440 <styles xmi:type="notation:SortingStyle" xmi:id="_SNlYZQGzEey7cfH5K6RyCw"/>
441 <styles xmi:type="notation:FilteringStyle" xmi:id="_SNlYZgGzEey7cfH5K6RyCw"/>
442 </children>
443 <styles xmi:type="notation:ShapeStyle" xmi:id="_SNlYYQGzEey7cfH5K6RyCw" fontName="Noto Sans" fontHeight="8"/>
444 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_SNlYYgGzEey7cfH5K6RyCw" x="2127" y="840" width="180" height="100"/>
445 </children>
446 <children xmi:type="notation:Node" xmi:id="_A9YrQCrZEeyyC-O0_LlY9w" type="2003" element="_A8hIkCrZEeyyC-O0_LlY9w">
447 <children xmi:type="notation:Node" xmi:id="_A9c8sCrZEeyyC-O0_LlY9w" type="5007"/>
448 <children xmi:type="notation:Node" xmi:id="_A9eK0CrZEeyyC-O0_LlY9w" type="7004">
449 <styles xmi:type="notation:SortingStyle" xmi:id="_A9eK0SrZEeyyC-O0_LlY9w"/>
450 <styles xmi:type="notation:FilteringStyle" xmi:id="_A9eK0irZEeyyC-O0_LlY9w"/>
451 </children>
452 <styles xmi:type="notation:ShapeStyle" xmi:id="_A9YrQSrZEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
453 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_A9YrQirZEeyyC-O0_LlY9w" x="1610" y="534" width="148" height="100"/>
454 </children>
455 <children xmi:type="notation:Node" xmi:id="_N0FQ4CrZEeyyC-O0_LlY9w" type="2003" element="_NzpMACrZEeyyC-O0_LlY9w">
456 <children xmi:type="notation:Node" xmi:id="_N0F38CrZEeyyC-O0_LlY9w" type="5007"/>
457 <children xmi:type="notation:Node" xmi:id="_N0F38SrZEeyyC-O0_LlY9w" type="7004">
458 <children xmi:type="notation:Node" xmi:id="_H_UDoCreEeyyC-O0_LlY9w" type="3010" element="_H_EMACreEeyyC-O0_LlY9w">
459 <styles xmi:type="notation:FontStyle" xmi:id="_H_UDoSreEeyyC-O0_LlY9w" fontColor="2697711" fontName="Segoe UI" fontHeight="8"/>
460 <layoutConstraint xmi:type="notation:Location" xmi:id="_H_UDoireEeyyC-O0_LlY9w"/>
461 </children>
462 <styles xmi:type="notation:SortingStyle" xmi:id="_N0F38irZEeyyC-O0_LlY9w"/>
463 <styles xmi:type="notation:FilteringStyle" xmi:id="_N0F38yrZEeyyC-O0_LlY9w"/>
464 </children>
465 <styles xmi:type="notation:ShapeStyle" xmi:id="_N0FQ4SrZEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
466 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_N0FQ4irZEeyyC-O0_LlY9w" x="1820" y="720" width="132" height="100"/>
467 </children>
468 <children xmi:type="notation:Node" xmi:id="_jzknACrZEeyyC-O0_LlY9w" type="2003" element="_jzRFACrZEeyyC-O0_LlY9w">
469 <children xmi:type="notation:Node" xmi:id="_jzknAyrZEeyyC-O0_LlY9w" type="5007"/>
470 <children xmi:type="notation:Node" xmi:id="_jzknBCrZEeyyC-O0_LlY9w" type="7004">
471 <styles xmi:type="notation:SortingStyle" xmi:id="_jzknBSrZEeyyC-O0_LlY9w"/>
472 <styles xmi:type="notation:FilteringStyle" xmi:id="_jzknBirZEeyyC-O0_LlY9w"/>
473 </children>
474 <styles xmi:type="notation:ShapeStyle" xmi:id="_jzknASrZEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
475 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_jzknAirZEeyyC-O0_LlY9w" x="1820" y="960" width="120" height="100"/>
476 </children>
477 <children xmi:type="notation:Node" xmi:id="_re7JICrZEeyyC-O0_LlY9w" type="2003" element="_remZACrZEeyyC-O0_LlY9w">
478 <children xmi:type="notation:Node" xmi:id="_re7JIyrZEeyyC-O0_LlY9w" type="5007"/>
479 <children xmi:type="notation:Node" xmi:id="_re7JJCrZEeyyC-O0_LlY9w" type="7004">
480 <styles xmi:type="notation:SortingStyle" xmi:id="_re7JJSrZEeyyC-O0_LlY9w"/>
481 <styles xmi:type="notation:FilteringStyle" xmi:id="_re7JJirZEeyyC-O0_LlY9w"/>
482 </children>
483 <styles xmi:type="notation:ShapeStyle" xmi:id="_re7JISrZEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
484 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_re7JIirZEeyyC-O0_LlY9w" x="1820" y="1284" width="120" height="100"/>
485 </children>
486 <children xmi:type="notation:Node" xmi:id="_3aLaMCrZEeyyC-O0_LlY9w" type="2003" element="_3Z67gCrZEeyyC-O0_LlY9w">
487 <children xmi:type="notation:Node" xmi:id="_3aLaMyrZEeyyC-O0_LlY9w" type="5007"/>
488 <children xmi:type="notation:Node" xmi:id="_3aLaNCrZEeyyC-O0_LlY9w" type="7004">
489 <children xmi:type="notation:Node" xmi:id="_cSv_0CtbEeySS4mYSornnA" type="3010" element="_cSL_ICtbEeySS4mYSornnA">
490 <styles xmi:type="notation:FontStyle" xmi:id="_cSv_0StbEeySS4mYSornnA" fontColor="2697711" fontName="Segoe UI" fontHeight="8"/>
491 <layoutConstraint xmi:type="notation:Location" xmi:id="_cSv_0itbEeySS4mYSornnA"/>
492 </children>
493 <children xmi:type="notation:Node" xmi:id="_e2dSsCtbEeySS4mYSornnA" type="3010" element="_e16gICtbEeySS4mYSornnA">
494 <styles xmi:type="notation:FontStyle" xmi:id="_e2dSsStbEeySS4mYSornnA" fontColor="2697711" fontName="Segoe UI" fontHeight="8"/>
495 <layoutConstraint xmi:type="notation:Location" xmi:id="_e2dSsitbEeySS4mYSornnA"/>
496 </children>
497 <styles xmi:type="notation:SortingStyle" xmi:id="_3aLaNSrZEeyyC-O0_LlY9w"/>
498 <styles xmi:type="notation:FilteringStyle" xmi:id="_3aLaNirZEeyyC-O0_LlY9w"/>
499 </children>
500 <styles xmi:type="notation:ShapeStyle" xmi:id="_3aLaMSrZEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
501 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_3aLaMirZEeyyC-O0_LlY9w" x="1795" y="1452" width="135" height="100"/>
502 </children>
503 <children xmi:type="notation:Node" xmi:id="_9mWokCrZEeyyC-O0_LlY9w" type="2003" element="_9mHYACrZEeyyC-O0_LlY9w">
504 <children xmi:type="notation:Node" xmi:id="_9mXPoCrZEeyyC-O0_LlY9w" type="5007"/>
505 <children xmi:type="notation:Node" xmi:id="_9mXPoSrZEeyyC-O0_LlY9w" type="7004">
506 <styles xmi:type="notation:SortingStyle" xmi:id="_9mXPoirZEeyyC-O0_LlY9w"/>
507 <styles xmi:type="notation:FilteringStyle" xmi:id="_9mXPoyrZEeyyC-O0_LlY9w"/>
508 </children>
509 <styles xmi:type="notation:ShapeStyle" xmi:id="_9mWokSrZEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
510 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_9mWokirZEeyyC-O0_LlY9w" x="1965" y="1452" width="120" height="100"/>
511 </children>
512 <children xmi:type="notation:Node" xmi:id="_Av5zcCraEeyyC-O0_LlY9w" type="2003" element="_AvrxACraEeyyC-O0_LlY9w">
513 <children xmi:type="notation:Node" xmi:id="_Av5zcyraEeyyC-O0_LlY9w" type="5007"/>
514 <children xmi:type="notation:Node" xmi:id="_Av6agCraEeyyC-O0_LlY9w" type="7004">
515 <styles xmi:type="notation:SortingStyle" xmi:id="_Av6agSraEeyyC-O0_LlY9w"/>
516 <styles xmi:type="notation:FilteringStyle" xmi:id="_Av6agiraEeyyC-O0_LlY9w"/>
517 </children>
518 <styles xmi:type="notation:ShapeStyle" xmi:id="_Av5zcSraEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
519 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Av5zciraEeyyC-O0_LlY9w" x="2119" y="1452" width="120" height="100"/>
520 </children>
521 <children xmi:type="notation:Node" xmi:id="_FmJJoCraEeyyC-O0_LlY9w" type="2003" element="_Fl5SACraEeyyC-O0_LlY9w">
522 <children xmi:type="notation:Node" xmi:id="_FmJJoyraEeyyC-O0_LlY9w" type="5007"/>
523 <children xmi:type="notation:Node" xmi:id="_FmJwsCraEeyyC-O0_LlY9w" type="7004">
524 <styles xmi:type="notation:SortingStyle" xmi:id="_FmJwsSraEeyyC-O0_LlY9w"/>
525 <styles xmi:type="notation:FilteringStyle" xmi:id="_FmJwsiraEeyyC-O0_LlY9w"/>
526 </children>
527 <styles xmi:type="notation:ShapeStyle" xmi:id="_FmJJoSraEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
528 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_FmJJoiraEeyyC-O0_LlY9w" x="2315" y="1452" width="120" height="100"/>
529 </children>
530 <children xmi:type="notation:Node" xmi:id="_XLYiACraEeyyC-O0_LlY9w" type="2003" element="_XLJ4gCraEeyyC-O0_LlY9w">
531 <children xmi:type="notation:Node" xmi:id="_XLYiAyraEeyyC-O0_LlY9w" type="5007"/>
532 <children xmi:type="notation:Node" xmi:id="_XLYiBCraEeyyC-O0_LlY9w" type="7004">
533 <styles xmi:type="notation:SortingStyle" xmi:id="_XLYiBSraEeyyC-O0_LlY9w"/>
534 <styles xmi:type="notation:FilteringStyle" xmi:id="_XLYiBiraEeyyC-O0_LlY9w"/>
535 </children>
536 <styles xmi:type="notation:ShapeStyle" xmi:id="_XLYiASraEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
537 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_XLYiAiraEeyyC-O0_LlY9w" x="1470" y="1284" width="120" height="100"/>
538 </children>
539 <children xmi:type="notation:Node" xmi:id="_jjhjYCraEeyyC-O0_LlY9w" type="2003" element="_jjUIACraEeyyC-O0_LlY9w">
540 <children xmi:type="notation:Node" xmi:id="_jjiKcCraEeyyC-O0_LlY9w" type="5007"/>
541 <children xmi:type="notation:Node" xmi:id="_jjiKcSraEeyyC-O0_LlY9w" type="7004">
542 <children xmi:type="notation:Node" xmi:id="_z_SVwCrbEeyyC-O0_LlY9w" type="3010" element="_z_B3ECrbEeyyC-O0_LlY9w">
543 <styles xmi:type="notation:FontStyle" xmi:id="_z_SVwSrbEeyyC-O0_LlY9w" fontColor="2697711" fontName="Segoe UI" fontHeight="8"/>
544 <layoutConstraint xmi:type="notation:Location" xmi:id="_z_SVwirbEeyyC-O0_LlY9w"/>
545 </children>
546 <styles xmi:type="notation:SortingStyle" xmi:id="_jjiKciraEeyyC-O0_LlY9w"/>
547 <styles xmi:type="notation:FilteringStyle" xmi:id="_jjiKcyraEeyyC-O0_LlY9w"/>
548 </children>
549 <styles xmi:type="notation:ShapeStyle" xmi:id="_jjhjYSraEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
550 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_jjhjYiraEeyyC-O0_LlY9w" x="1397" y="1452" width="120" height="100"/>
551 </children>
552 <children xmi:type="notation:Node" xmi:id="_q-1B4CrbEeyyC-O0_LlY9w" type="2003" element="_q-nmgCrbEeyyC-O0_LlY9w">
553 <children xmi:type="notation:Node" xmi:id="_q-1B4yrbEeyyC-O0_LlY9w" type="5007"/>
554 <children xmi:type="notation:Node" xmi:id="_q-1o8CrbEeyyC-O0_LlY9w" type="7004">
555 <children xmi:type="notation:Node" xmi:id="_GMAVsCtcEeySS4mYSornnA" type="3010" element="_GLYqoCtcEeySS4mYSornnA">
556 <styles xmi:type="notation:FontStyle" xmi:id="_GMAVsStcEeySS4mYSornnA" fontColor="2697711" fontName="Segoe UI" fontHeight="8"/>
557 <layoutConstraint xmi:type="notation:Location" xmi:id="_GMAVsitcEeySS4mYSornnA"/>
558 </children>
559 <styles xmi:type="notation:SortingStyle" xmi:id="_q-1o8SrbEeyyC-O0_LlY9w"/>
560 <styles xmi:type="notation:FilteringStyle" xmi:id="_q-1o8irbEeyyC-O0_LlY9w"/>
561 </children>
562 <styles xmi:type="notation:ShapeStyle" xmi:id="_q-1B4SrbEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
563 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_q-1B4irbEeyyC-O0_LlY9w" x="1855" y="1751" width="120" height="100"/>
564 </children>
565 <children xmi:type="notation:Node" xmi:id="_LPNZACrcEeyyC-O0_LlY9w" type="2003" element="_LO_WkCrcEeyyC-O0_LlY9w">
566 <children xmi:type="notation:Node" xmi:id="_LPNZAyrcEeyyC-O0_LlY9w" type="5007"/>
567 <children xmi:type="notation:Node" xmi:id="_LPNZBCrcEeyyC-O0_LlY9w" type="7004">
568 <children xmi:type="notation:Node" xmi:id="_M8-iYCrcEeyyC-O0_LlY9w" type="3010" element="_M8yVICrcEeyyC-O0_LlY9w">
569 <styles xmi:type="notation:FontStyle" xmi:id="_M8-iYSrcEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
570 <layoutConstraint xmi:type="notation:Location" xmi:id="_M8-iYircEeyyC-O0_LlY9w"/>
571 </children>
572 <children xmi:type="notation:Node" xmi:id="_OHgb0CrcEeyyC-O0_LlY9w" type="3010" element="_OHUOkCrcEeyyC-O0_LlY9w">
573 <styles xmi:type="notation:FontStyle" xmi:id="_OHgb0SrcEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
574 <layoutConstraint xmi:type="notation:Location" xmi:id="_OHgb0ircEeyyC-O0_LlY9w"/>
575 </children>
576 <styles xmi:type="notation:SortingStyle" xmi:id="_LPNZBSrcEeyyC-O0_LlY9w"/>
577 <styles xmi:type="notation:FilteringStyle" xmi:id="_LPNZBircEeyyC-O0_LlY9w"/>
578 </children>
579 <styles xmi:type="notation:ShapeStyle" xmi:id="_LPNZASrcEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
580 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_LPNZAircEeyyC-O0_LlY9w" x="2090" y="180" width="120" height="100"/>
581 </children>
582 <children xmi:type="notation:Node" xmi:id="_PSiEgCrcEeyyC-O0_LlY9w" type="2003" element="_PSV3QCrcEeyyC-O0_LlY9w">
583 <children xmi:type="notation:Node" xmi:id="_PSiEgyrcEeyyC-O0_LlY9w" type="5007"/>
584 <children xmi:type="notation:Node" xmi:id="_PSiEhCrcEeyyC-O0_LlY9w" type="7004">
585 <children xmi:type="notation:Node" xmi:id="_QZiv4CrcEeyyC-O0_LlY9w" type="3010" element="_QZVUgCrcEeyyC-O0_LlY9w">
586 <styles xmi:type="notation:FontStyle" xmi:id="_QZiv4SrcEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
587 <layoutConstraint xmi:type="notation:Location" xmi:id="_QZiv4ircEeyyC-O0_LlY9w"/>
588 </children>
589 <styles xmi:type="notation:SortingStyle" xmi:id="_PSiEhSrcEeyyC-O0_LlY9w"/>
590 <styles xmi:type="notation:FilteringStyle" xmi:id="_PSiEhircEeyyC-O0_LlY9w"/>
591 </children>
592 <styles xmi:type="notation:ShapeStyle" xmi:id="_PSiEgSrcEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
593 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_PSiEgircEeyyC-O0_LlY9w" x="2260" y="180" width="120" height="100"/>
594 </children>
595 <styles xmi:type="notation:DiagramStyle" xmi:id="_CsZB8qA4EeuqkpDnuik1sg"/>
596 <edges xmi:type="notation:Edge" xmi:id="_ODhSQKA4EeuqkpDnuik1sg" type="4001" element="_ODPlcKA4EeuqkpDnuik1sg" source="_JT0o8KA4EeuqkpDnuik1sg" target="_EfWNUKA4EeuqkpDnuik1sg">
597 <children xmi:type="notation:Node" xmi:id="_ODhSRKA4EeuqkpDnuik1sg" type="6001">
598 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_ODhSRaA4EeuqkpDnuik1sg" x="-36" y="-6"/>
599 </children>
600 <children xmi:type="notation:Node" xmi:id="_ODh5UKA4EeuqkpDnuik1sg" type="6002">
601 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_ODh5UaA4EeuqkpDnuik1sg" y="10"/>
602 </children>
603 <children xmi:type="notation:Node" xmi:id="_ODh5UqA4EeuqkpDnuik1sg" type="6003">
604 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_ODh5U6A4EeuqkpDnuik1sg" x="22" y="4"/>
605 </children>
606 <styles xmi:type="notation:ConnectorStyle" xmi:id="_ODhSQaA4EeuqkpDnuik1sg" routing="Tree"/>
607 <styles xmi:type="notation:FontStyle" xmi:id="_ODhSQqA4EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
608 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_ODhSQ6A4EeuqkpDnuik1sg" points="[0, 0, 48, 143]$[0, -24, 48, 119]$[-47, -24, 1, 119]$[-47, -94, 1, 49]"/>
609 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_ODh5VKA4EeuqkpDnuik1sg" id="(0.5,0.0)"/>
610 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_ODh5VaA4EeuqkpDnuik1sg" id="(0.5,0.5)"/>
611 </edges>
612 <edges xmi:type="notation:Edge" xmi:id="_gR35EKA4EeuqkpDnuik1sg" type="4001" element="_gRoBcKA4EeuqkpDnuik1sg" source="_c-HCQKA4EeuqkpDnuik1sg" target="_EfWNUKA4EeuqkpDnuik1sg">
613 <children xmi:type="notation:Node" xmi:id="_gR4gIKA4EeuqkpDnuik1sg" type="6001">
614 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_gR4gIaA4EeuqkpDnuik1sg" y="-10"/>
615 </children>
616 <children xmi:type="notation:Node" xmi:id="_gR4gIqA4EeuqkpDnuik1sg" type="6002">
617 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_gR4gI6A4EeuqkpDnuik1sg" y="10"/>
618 </children>
619 <children xmi:type="notation:Node" xmi:id="_gR4gJKA4EeuqkpDnuik1sg" type="6003">
620 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_gR4gJaA4EeuqkpDnuik1sg" y="10"/>
621 </children>
622 <styles xmi:type="notation:ConnectorStyle" xmi:id="_gR35EaA4EeuqkpDnuik1sg" routing="Tree"/>
623 <styles xmi:type="notation:FontStyle" xmi:id="_gR35EqA4EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
624 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_gR35E6A4EeuqkpDnuik1sg" points="[0, 0, -36, 334]$[36, -334, 0, 0]"/>
625 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_gR5HMKA4EeuqkpDnuik1sg" id="(0.3310344827586207,0.0)"/>
626 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_gR5HMaA4EeuqkpDnuik1sg" id="(0.5,0.5)"/>
627 </edges>
628 <edges xmi:type="notation:Edge" xmi:id="_jlBE0KA4EeuqkpDnuik1sg" type="4001" element="_jk6-PKA4EeuqkpDnuik1sg" source="_JT0o8KA4EeuqkpDnuik1sg" target="_c-HCQKA4EeuqkpDnuik1sg">
629 <children xmi:type="notation:Node" xmi:id="_jlBE1KA4EeuqkpDnuik1sg" type="6001">
630 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_jlBE1aA4EeuqkpDnuik1sg" x="-76" y="-14"/>
631 </children>
632 <children xmi:type="notation:Node" xmi:id="_jlBE1qA4EeuqkpDnuik1sg" type="6002">
633 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_jlBE16A4EeuqkpDnuik1sg" x="4" y="10"/>
634 </children>
635 <children xmi:type="notation:Node" xmi:id="_jlBE2KA4EeuqkpDnuik1sg" type="6003">
636 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_jlBE2aA4EeuqkpDnuik1sg" x="-4" y="10"/>
637 </children>
638 <styles xmi:type="notation:ConnectorStyle" xmi:id="_jlBE0aA4EeuqkpDnuik1sg" routing="Rectilinear"/>
639 <styles xmi:type="notation:FontStyle" xmi:id="_jlBE0qA4EeuqkpDnuik1sg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
640 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_jlBE06A4EeuqkpDnuik1sg" points="[-24, 0, 35, -142]$[-24, 58, 35, -84]$[-59, 58, 0, -84]$[-59, 142, 0, 0]"/>
641 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_jlBr4KA4EeuqkpDnuik1sg" id="(0.4067796610169492,1.0)"/>
642 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_jlBr4aA4EeuqkpDnuik1sg" id="(0.503448275862069,0.0)"/>
643 </edges>
644 <edges xmi:type="notation:Edge" xmi:id="_0V8EUKA4EeuqkpDnuik1sg" type="4001" element="_0V3L1qA4EeuqkpDnuik1sg" source="_c-HCQKA4EeuqkpDnuik1sg" target="_c-HCQKA4EeuqkpDnuik1sg">
645 <children xmi:type="notation:Node" xmi:id="_0V8rYKA4EeuqkpDnuik1sg" type="6001">
646 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_0V8rYaA4EeuqkpDnuik1sg" x="30" y="11"/>
647 </children>
648 <children xmi:type="notation:Node" xmi:id="_0V8rYqA4EeuqkpDnuik1sg" type="6002">
649 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_0V8rY6A4EeuqkpDnuik1sg" x="-8" y="2"/>
650 </children>
651 <children xmi:type="notation:Node" xmi:id="_0V8rZKA4EeuqkpDnuik1sg" type="6003">
652 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_0V8rZaA4EeuqkpDnuik1sg" x="9" y="3"/>
653 </children>
654 <styles xmi:type="notation:ConnectorStyle" xmi:id="_0V8EUaA4EeuqkpDnuik1sg" routing="Rectilinear"/>
655 <styles xmi:type="notation:FontStyle" xmi:id="_0V8EUqA4EeuqkpDnuik1sg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
656 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_0V8EU6A4EeuqkpDnuik1sg" points="[0, 60, 145, 60]$[40, 60, 185, 60]$[40, 126, 185, 126]$[-73, 126, 72, 126]$[-73, 86, 72, 86]"/>
657 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_0V8rZqA4EeuqkpDnuik1sg" id="(1.0,0.12244897959183673)"/>
658 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_0V8rZ6A4EeuqkpDnuik1sg" id="(0.0,0.12244897959183673)"/>
659 </edges>
660 <edges xmi:type="notation:Edge" xmi:id="_XWh5QKA5EeuqkpDnuik1sg" type="4001" element="_XWbypqA5EeuqkpDnuik1sg" source="_EfWNUKA4EeuqkpDnuik1sg" target="_RzZA0KA5EeuqkpDnuik1sg">
661 <children xmi:type="notation:Node" xmi:id="_XWh5RKA5EeuqkpDnuik1sg" type="6001">
662 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_XWh5RaA5EeuqkpDnuik1sg" x="-4" y="-10"/>
663 </children>
664 <children xmi:type="notation:Node" xmi:id="_XWh5RqA5EeuqkpDnuik1sg" type="6002">
665 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_XWh5R6A5EeuqkpDnuik1sg" y="10"/>
666 </children>
667 <children xmi:type="notation:Node" xmi:id="_XWh5SKA5EeuqkpDnuik1sg" type="6003">
668 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_XWh5SaA5EeuqkpDnuik1sg" y="10"/>
669 </children>
670 <styles xmi:type="notation:ConnectorStyle" xmi:id="_XWh5QaA5EeuqkpDnuik1sg" routing="Tree"/>
671 <styles xmi:type="notation:FontStyle" xmi:id="_XWh5QqA5EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
672 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_XWh5Q6A5EeuqkpDnuik1sg" points="[0, 0, -11, 95]$[0, -24, -11, 71]$[12, -24, 1, 71]$[12, -46, 1, 49]"/>
673 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_XWigUKA5EeuqkpDnuik1sg" id="(0.2288135593220339,0.030612244897959183)"/>
674 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_XWigUaA5EeuqkpDnuik1sg" id="(0.5,0.5)"/>
675 </edges>
676 <edges xmi:type="notation:Edge" xmi:id="_rUvUIKA5EeuqkpDnuik1sg" type="4001" element="_rUgDkKA5EeuqkpDnuik1sg" source="_fit3kKA5EeuqkpDnuik1sg" target="_EfWNUKA4EeuqkpDnuik1sg">
677 <children xmi:type="notation:Node" xmi:id="_rUvUJKA5EeuqkpDnuik1sg" type="6001">
678 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_rUvUJaA5EeuqkpDnuik1sg" y="-10"/>
679 </children>
680 <children xmi:type="notation:Node" xmi:id="_rUvUJqA5EeuqkpDnuik1sg" type="6002">
681 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_rUvUJ6A5EeuqkpDnuik1sg" y="10"/>
682 </children>
683 <children xmi:type="notation:Node" xmi:id="_rUvUKKA5EeuqkpDnuik1sg" type="6003">
684 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_rUvUKaA5EeuqkpDnuik1sg" y="10"/>
685 </children>
686 <styles xmi:type="notation:ConnectorStyle" xmi:id="_rUvUIaA5EeuqkpDnuik1sg" routing="Tree"/>
687 <styles xmi:type="notation:FontStyle" xmi:id="_rUvUIqA5EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
688 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_rUvUI6A5EeuqkpDnuik1sg" points="[0, 0, 278, 180]$[-278, -180, 0, 0]"/>
689 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_rUv7MKA5EeuqkpDnuik1sg" id="(0.41882771042183686,0.0)"/>
690 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_rUv7MaA5EeuqkpDnuik1sg" id="(0.5,0.5)"/>
691 </edges>
692 <edges xmi:type="notation:Edge" xmi:id="_onzXUKA6EeuqkpDnuik1sg" type="4001" element="_oni4rKA6EeuqkpDnuik1sg" source="_QKLK0KA6EeuqkpDnuik1sg" target="_jP6FkKA6EeuqkpDnuik1sg">
693 <children xmi:type="notation:Node" xmi:id="_onz-YKA6EeuqkpDnuik1sg" type="6001">
694 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_onz-YaA6EeuqkpDnuik1sg" x="-21" y="23"/>
695 </children>
696 <children xmi:type="notation:Node" xmi:id="_on0lcKA6EeuqkpDnuik1sg" type="6002">
697 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_on0lcaA6EeuqkpDnuik1sg" y="10"/>
698 </children>
699 <children xmi:type="notation:Node" xmi:id="_on0lcqA6EeuqkpDnuik1sg" type="6003">
700 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_on0lc6A6EeuqkpDnuik1sg" x="-55" y="-7"/>
701 </children>
702 <styles xmi:type="notation:ConnectorStyle" xmi:id="_onzXUaA6EeuqkpDnuik1sg" routing="Tree"/>
703 <styles xmi:type="notation:FontStyle" xmi:id="_onzXUqA6EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
704 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_onzXU6A6EeuqkpDnuik1sg" points="[0, 0, -23, 191]$[0, -72, -23, 119]$[24, -72, 1, 119]$[24, -142, 1, 49]"/>
705 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_on1MgKA6EeuqkpDnuik1sg" id="(0.2033898305084746,0.0)"/>
706 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_on1MgaA6EeuqkpDnuik1sg" id="(0.5,0.5)"/>
707 </edges>
708 <edges xmi:type="notation:Edge" xmi:id="_BWCysKA7EeuqkpDnuik1sg" type="4001" element="_BVyUAKA7EeuqkpDnuik1sg" source="_-O-UEKA6EeuqkpDnuik1sg" target="_jP6FkKA6EeuqkpDnuik1sg">
709 <children xmi:type="notation:Node" xmi:id="_BWCytKA7EeuqkpDnuik1sg" type="6001">
710 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_BWCytaA7EeuqkpDnuik1sg" x="-107" y="-10"/>
711 </children>
712 <children xmi:type="notation:Node" xmi:id="_BWCytqA7EeuqkpDnuik1sg" type="6002">
713 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_BWCyt6A7EeuqkpDnuik1sg" x="-46" y="10"/>
714 </children>
715 <children xmi:type="notation:Node" xmi:id="_BWCyuKA7EeuqkpDnuik1sg" type="6003">
716 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_BWCyuaA7EeuqkpDnuik1sg" x="-95" y="10"/>
717 </children>
718 <styles xmi:type="notation:ConnectorStyle" xmi:id="_BWCysaA7EeuqkpDnuik1sg" routing="Tree"/>
719 <styles xmi:type="notation:FontStyle" xmi:id="_BWCysqA7EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
720 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_BWCys6A7EeuqkpDnuik1sg" points="[0, 0, 397, 983]$[0, -877, 397, 106]$[-396, -877, 1, 106]$[-396, -934, 1, 49]"/>
721 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_BWDZwKA7EeuqkpDnuik1sg" id="(0.3050847457627119,0.0)"/>
722 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_BWDZwaA7EeuqkpDnuik1sg" id="(0.5,0.5)"/>
723 </edges>
724 <edges xmi:type="notation:Edge" xmi:id="_SyzkgKA7EeuqkpDnuik1sg" type="4001" element="_SykT9qA7EeuqkpDnuik1sg" source="_sdPX0KA6EeuqkpDnuik1sg" target="_4k5GIKA6EeuqkpDnuik1sg">
725 <children xmi:type="notation:Node" xmi:id="_SyzkhKA7EeuqkpDnuik1sg" type="6001">
726 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_SyzkhaA7EeuqkpDnuik1sg" x="6" y="-45"/>
727 </children>
728 <children xmi:type="notation:Node" xmi:id="_SyzkhqA7EeuqkpDnuik1sg" type="6002">
729 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Syzkh6A7EeuqkpDnuik1sg" x="52" y="10"/>
730 </children>
731 <children xmi:type="notation:Node" xmi:id="_SyzkiKA7EeuqkpDnuik1sg" type="6003">
732 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_SyzkiaA7EeuqkpDnuik1sg" x="4"/>
733 </children>
734 <styles xmi:type="notation:ConnectorStyle" xmi:id="_SyzkgaA7EeuqkpDnuik1sg" routing="Rectilinear"/>
735 <styles xmi:type="notation:FontStyle" xmi:id="_SyzkgqA7EeuqkpDnuik1sg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
736 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_Syzkg6A7EeuqkpDnuik1sg" points="[-34, 0, 46, -70]$[-34, 70, 46, 0]"/>
737 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_Sy0LkKA7EeuqkpDnuik1sg" id="(0.6101694915254238,1.0)"/>
738 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_Sy0LkaA7EeuqkpDnuik1sg" id="(0.4067796610169492,0.0)"/>
739 </edges>
740 <edges xmi:type="notation:Edge" xmi:id="_vWH0AKA7EeuqkpDnuik1sg" type="4001" element="_vWBGaKA7EeuqkpDnuik1sg" source="_sdPX0KA6EeuqkpDnuik1sg" target="_rRhWIKA7EeuqkpDnuik1sg">
741 <children xmi:type="notation:Node" xmi:id="_vWH0BKA7EeuqkpDnuik1sg" type="6001">
742 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vWH0BaA7EeuqkpDnuik1sg" x="-13" y="96"/>
743 </children>
744 <children xmi:type="notation:Node" xmi:id="_vWH0BqA7EeuqkpDnuik1sg" type="6002">
745 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vWH0B6A7EeuqkpDnuik1sg" y="10"/>
746 </children>
747 <children xmi:type="notation:Node" xmi:id="_vWH0CKA7EeuqkpDnuik1sg" type="6003">
748 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vWH0CaA7EeuqkpDnuik1sg" x="66" y="-47"/>
749 </children>
750 <styles xmi:type="notation:ConnectorStyle" xmi:id="_vWH0AaA7EeuqkpDnuik1sg" routing="Tree"/>
751 <styles xmi:type="notation:FontStyle" xmi:id="_vWH0AqA7EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
752 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_vWH0A6A7EeuqkpDnuik1sg" points="[0, -10, -215, 191]$[0, -125, -215, 76]$[185, -125, -30, 76]$[185, -152, -30, 49]"/>
753 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_vWIbEKA7EeuqkpDnuik1sg" id="(0.5169491525423728,0.10204081632653061)"/>
754 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_vWIbEaA7EeuqkpDnuik1sg" id="(0.5,0.5)"/>
755 </edges>
756 <edges xmi:type="notation:Edge" xmi:id="_yCqSwKA7EeuqkpDnuik1sg" type="4001" element="_yCjlHKA7EeuqkpDnuik1sg" source="_V6pfMKA7EeuqkpDnuik1sg" target="_rRhWIKA7EeuqkpDnuik1sg">
757 <children xmi:type="notation:Node" xmi:id="_yCqSxKA7EeuqkpDnuik1sg" type="6001">
758 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_yCqSxaA7EeuqkpDnuik1sg" x="114" y="-10"/>
759 </children>
760 <children xmi:type="notation:Node" xmi:id="_yCqSxqA7EeuqkpDnuik1sg" type="6002">
761 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_yCqSx6A7EeuqkpDnuik1sg" x="34" y="10"/>
762 </children>
763 <children xmi:type="notation:Node" xmi:id="_yCqSyKA7EeuqkpDnuik1sg" type="6003">
764 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_yCqSyaA7EeuqkpDnuik1sg" x="-59" y="-15"/>
765 </children>
766 <styles xmi:type="notation:ConnectorStyle" xmi:id="_yCqSwaA7EeuqkpDnuik1sg" routing="Tree"/>
767 <styles xmi:type="notation:FontStyle" xmi:id="_yCqSwqA7EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
768 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_yCqSw6A7EeuqkpDnuik1sg" points="[-1, -5, -56, 683]$[-1, -527, -56, 161]$[25, -527, -30, 161]$[25, -639, -30, 49]"/>
769 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_yCqSyqA7EeuqkpDnuik1sg" id="(0.7288135593220338,0.030612244897959183)"/>
770 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_yCqSy6A7EeuqkpDnuik1sg" id="(0.5,0.5)"/>
771 </edges>
772 <edges xmi:type="notation:Edge" xmi:id="_0U2-4KA7EeuqkpDnuik1sg" type="4001" element="_0UtN5qA7EeuqkpDnuik1sg" source="_rRhWIKA7EeuqkpDnuik1sg" target="_-O-UEKA6EeuqkpDnuik1sg">
773 <children xmi:type="notation:Node" xmi:id="_0U3l8KA7EeuqkpDnuik1sg" type="6001">
774 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_0U3l8aA7EeuqkpDnuik1sg" x="-438" y="-60"/>
775 </children>
776 <children xmi:type="notation:Node" xmi:id="_0U3l8qA7EeuqkpDnuik1sg" type="6002">
777 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_0U3l86A7EeuqkpDnuik1sg" x="-119" y="10"/>
778 </children>
779 <children xmi:type="notation:Node" xmi:id="_0U3l9KA7EeuqkpDnuik1sg" type="6003">
780 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_0U3l9aA7EeuqkpDnuik1sg" x="-673" y="10"/>
781 </children>
782 <styles xmi:type="notation:ConnectorStyle" xmi:id="_0U2-4aA7EeuqkpDnuik1sg" routing="Rectilinear"/>
783 <styles xmi:type="notation:FontStyle" xmi:id="_0U2-4qA7EeuqkpDnuik1sg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
784 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_0U2-46A7EeuqkpDnuik1sg" points="[-1, 0, -23, -934]$[-1, 934, -23, 0]"/>
785 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_0U3l9qA7EeuqkpDnuik1sg" id="(0.696551724137931,1.0)"/>
786 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_0U3l96A7EeuqkpDnuik1sg" id="(0.7372881355932204,0.0)"/>
787 </edges>
788 <edges xmi:type="notation:Edge" xmi:id="_D9xNcKA8EeuqkpDnuik1sg" type="4001" element="_D9lnQKA8EeuqkpDnuik1sg" source="_6KEUMKA6EeuqkpDnuik1sg" target="_4k5GIKA6EeuqkpDnuik1sg">
789 <children xmi:type="notation:Node" xmi:id="_D9x0gKA8EeuqkpDnuik1sg" type="6001">
790 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_D9x0gaA8EeuqkpDnuik1sg" y="-10"/>
791 </children>
792 <children xmi:type="notation:Node" xmi:id="_D9x0gqA8EeuqkpDnuik1sg" type="6002">
793 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_D9x0g6A8EeuqkpDnuik1sg" y="10"/>
794 </children>
795 <children xmi:type="notation:Node" xmi:id="_D9x0hKA8EeuqkpDnuik1sg" type="6003">
796 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_D9x0haA8EeuqkpDnuik1sg" y="10"/>
797 </children>
798 <styles xmi:type="notation:ConnectorStyle" xmi:id="_D9xNcaA8EeuqkpDnuik1sg" routing="Tree"/>
799 <styles xmi:type="notation:FontStyle" xmi:id="_D9xNcqA8EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
800 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_D9xNc6A8EeuqkpDnuik1sg" points="[0, -2, -128, 156]$[0, -42, -128, 116]$[137, -42, 9, 116]$[137, -60, 9, 98]"/>
801 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_D9x0hqA8EeuqkpDnuik1sg" id="(0.4915254237288136,0.08163265306122448)"/>
802 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_D9x0h6A8EeuqkpDnuik1sg" id="(0.4067796610169492,0.0)"/>
803 </edges>
804 <edges xmi:type="notation:Edge" xmi:id="_Vj1sQKA8EeuqkpDnuik1sg" type="4001" element="_VjwMzqA8EeuqkpDnuik1sg" source="_6KEUMKA6EeuqkpDnuik1sg" target="_EfWNUKA4EeuqkpDnuik1sg">
805 <children xmi:type="notation:Node" xmi:id="_Vj1sRKA8EeuqkpDnuik1sg" type="6001">
806 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Vj1sRaA8EeuqkpDnuik1sg" x="-42" y="526"/>
807 </children>
808 <children xmi:type="notation:Node" xmi:id="_Vj1sRqA8EeuqkpDnuik1sg" type="6002">
809 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Vj1sR6A8EeuqkpDnuik1sg" x="174" y="-10"/>
810 </children>
811 <children xmi:type="notation:Node" xmi:id="_Vj1sSKA8EeuqkpDnuik1sg" type="6003">
812 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Vj1sSaA8EeuqkpDnuik1sg" x="-51" y="10"/>
813 </children>
814 <styles xmi:type="notation:ConnectorStyle" xmi:id="_Vj1sQaA8EeuqkpDnuik1sg" routing="Rectilinear"/>
815 <styles xmi:type="notation:FontStyle" xmi:id="_Vj1sQqA8EeuqkpDnuik1sg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
816 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_Vj1sQ6A8EeuqkpDnuik1sg" points="[-48, 24, 553, 682]$[-720, 24, -119, 682]$[-720, -708, -119, -50]$[-660, -708, -59, -50]"/>
817 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_Vj1sSqA8EeuqkpDnuik1sg" id="(0.4067796610169492,0.0)"/>
818 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_Vj1sS6A8EeuqkpDnuik1sg" id="(0.5,1.0)"/>
819 </edges>
820 <edges xmi:type="notation:Edge" xmi:id="_w-zMkKA8EeuqkpDnuik1sg" type="4001" element="_w-iG36A8EeuqkpDnuik1sg" source="_p9wywKA8EeuqkpDnuik1sg" target="_EfWNUKA4EeuqkpDnuik1sg">
821 <children xmi:type="notation:Node" xmi:id="_w-zMlKA8EeuqkpDnuik1sg" type="6001">
822 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_w-zMlaA8EeuqkpDnuik1sg" x="-564" y="-83"/>
823 </children>
824 <children xmi:type="notation:Node" xmi:id="_w-zzoKA8EeuqkpDnuik1sg" type="6002">
825 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_w-zzoaA8EeuqkpDnuik1sg" x="554" y="79"/>
826 </children>
827 <children xmi:type="notation:Node" xmi:id="_w-zzoqA8EeuqkpDnuik1sg" type="6003">
828 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_w-zzo6A8EeuqkpDnuik1sg" x="-230" y="72"/>
829 </children>
830 <styles xmi:type="notation:ConnectorStyle" xmi:id="_w-zMkaA8EeuqkpDnuik1sg" routing="Rectilinear"/>
831 <styles xmi:type="notation:FontStyle" xmi:id="_w-zMkqA8EeuqkpDnuik1sg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
832 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_w-zMk6A8EeuqkpDnuik1sg" points="[0, 6, 1421, 6]$[-379, 6, 1042, 6]$[-379, -63, 1042, -63]$[-1034, -63, 387, -63]$[-1034, -1, 387, -1]$[-1421, -1, 0, -1]"/>
833 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_w-zzpKA8EeuqkpDnuik1sg" id="(0.0,0.44954128440366975)"/>
834 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_w-zzpaA8EeuqkpDnuik1sg" id="(1.0,0.5)"/>
835 </edges>
836 <edges xmi:type="notation:Edge" xmi:id="_4eaYwKA8EeuqkpDnuik1sg" type="4001" element="_4eU5TqA8EeuqkpDnuik1sg" source="_D1D6MKA4EeuqkpDnuik1sg" target="_xsq_MKA8EeuqkpDnuik1sg">
837 <children xmi:type="notation:Node" xmi:id="_4ea_06A8EeuqkpDnuik1sg" type="6001">
838 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_4ea_1KA8EeuqkpDnuik1sg" x="-36" y="-17"/>
839 </children>
840 <children xmi:type="notation:Node" xmi:id="_4ea_1aA8EeuqkpDnuik1sg" type="6002">
841 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_4ea_1qA8EeuqkpDnuik1sg" x="8" y="-14"/>
842 </children>
843 <children xmi:type="notation:Node" xmi:id="_4ea_16A8EeuqkpDnuik1sg" type="6003">
844 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_4ea_2KA8EeuqkpDnuik1sg" x="-124" y="10"/>
845 </children>
846 <styles xmi:type="notation:ConnectorStyle" xmi:id="_4ea_0KA8EeuqkpDnuik1sg" routing="Rectilinear"/>
847 <styles xmi:type="notation:FontStyle" xmi:id="_4ea_0aA8EeuqkpDnuik1sg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
848 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_4ea_0qA8EeuqkpDnuik1sg" points="[-21, 50, -159, -58]$[-34, 84, -172, -24]$[137, 84, -1, -24]$[137, 107, -1, -1]"/>
849 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_4ea_2aA8EeuqkpDnuik1sg" id="(1.0,0.4897959183673469)"/>
850 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_4ea_2qA8EeuqkpDnuik1sg" id="(0.3389830508474576,0.0)"/>
851 </edges>
852 <edges xmi:type="notation:Edge" xmi:id="_hU_wsKA9EeuqkpDnuik1sg" type="4001" element="_hU64ZqA9EeuqkpDnuik1sg" source="_D1D6MKA4EeuqkpDnuik1sg" target="_e73WIKA9EeuqkpDnuik1sg">
853 <children xmi:type="notation:Node" xmi:id="_hU_wtKA9EeuqkpDnuik1sg" type="6001">
854 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_hU_wtaA9EeuqkpDnuik1sg" x="-12" y="64"/>
855 </children>
856 <children xmi:type="notation:Node" xmi:id="_hU_wtqA9EeuqkpDnuik1sg" type="6002">
857 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_hU_wt6A9EeuqkpDnuik1sg" x="10"/>
858 </children>
859 <children xmi:type="notation:Node" xmi:id="_hU_wuKA9EeuqkpDnuik1sg" type="6003">
860 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_hU_wuaA9EeuqkpDnuik1sg" x="-10" y="9"/>
861 </children>
862 <styles xmi:type="notation:ConnectorStyle" xmi:id="_hU_wsaA9EeuqkpDnuik1sg" routing="Rectilinear"/>
863 <styles xmi:type="notation:FontStyle" xmi:id="_hU_wsqA9EeuqkpDnuik1sg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
864 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_hU_ws6A9EeuqkpDnuik1sg" points="[0, 0, 0, -58]$[0, 58, 0, 0]"/>
865 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_hU_wuqA9EeuqkpDnuik1sg" id="(0.5,1.0)"/>
866 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_hU_wu6A9EeuqkpDnuik1sg" id="(0.5,0.0)"/>
867 </edges>
868 <edges xmi:type="notation:Edge" xmi:id="_mQXegKA9EeuqkpDnuik1sg" type="4001" element="_mQFKsqA9EeuqkpDnuik1sg" source="_p9wywKA8EeuqkpDnuik1sg" target="_e73WIKA9EeuqkpDnuik1sg">
869 <children xmi:type="notation:Node" xmi:id="_mQYFkKA9EeuqkpDnuik1sg" type="6001">
870 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_mQYFkaA9EeuqkpDnuik1sg" x="-88" y="-10"/>
871 </children>
872 <children xmi:type="notation:Node" xmi:id="_mQYFkqA9EeuqkpDnuik1sg" type="6002">
873 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_mQYFk6A9EeuqkpDnuik1sg" x="-88" y="10"/>
874 </children>
875 <children xmi:type="notation:Node" xmi:id="_mQYFlKA9EeuqkpDnuik1sg" type="6003">
876 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_mQYFlaA9EeuqkpDnuik1sg" x="-88" y="10"/>
877 </children>
878 <styles xmi:type="notation:ConnectorStyle" xmi:id="_mQXegaA9EeuqkpDnuik1sg" routing="Tree"/>
879 <styles xmi:type="notation:FontStyle" xmi:id="_mQXegqA9EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
880 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_mQXeg6A9EeuqkpDnuik1sg" points="[0, -4, 1313, 142]$[0, -51, 1313, 95]$[-1312, -51, 1, 95]$[-1312, -97, 1, 49]"/>
881 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_mQYFlqA9EeuqkpDnuik1sg" id="(0.4745762711864407,0.03669724770642201)"/>
882 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_mQYFl6A9EeuqkpDnuik1sg" id="(0.5,0.5)"/>
883 </edges>
884 <edges xmi:type="notation:Edge" xmi:id="_pldLAKA9EeuqkpDnuik1sg" type="4001" element="_plK3JqA9EeuqkpDnuik1sg" source="_JT0o8KA4EeuqkpDnuik1sg" target="_e73WIKA9EeuqkpDnuik1sg">
885 <children xmi:type="notation:Node" xmi:id="_pldLBKA9EeuqkpDnuik1sg" type="6001">
886 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_pldLBaA9EeuqkpDnuik1sg" y="-10"/>
887 </children>
888 <children xmi:type="notation:Node" xmi:id="_pldLBqA9EeuqkpDnuik1sg" type="6002">
889 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_pldLB6A9EeuqkpDnuik1sg" y="10"/>
890 </children>
891 <children xmi:type="notation:Node" xmi:id="_pldyEKA9EeuqkpDnuik1sg" type="6003">
892 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_pldyEaA9EeuqkpDnuik1sg" y="10"/>
893 </children>
894 <styles xmi:type="notation:ConnectorStyle" xmi:id="_pldLAaA9EeuqkpDnuik1sg" routing="Tree"/>
895 <styles xmi:type="notation:FontStyle" xmi:id="_pldLAqA9EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
896 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_pldLA6A9EeuqkpDnuik1sg" points="[0, 0, -84, 286]$[84, -286, 0, 0]"/>
897 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_pldyEqA9EeuqkpDnuik1sg" id="(0.8389830508474576,0.02040816326530612)"/>
898 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_pldyE6A9EeuqkpDnuik1sg" id="(0.5,0.0)"/>
899 </edges>
900 <edges xmi:type="notation:Edge" xmi:id="_vd7aQKA9EeuqkpDnuik1sg" type="4001" element="_vdptgqA9EeuqkpDnuik1sg" source="_QUDYMKA9EeuqkpDnuik1sg" target="_e73WIKA9EeuqkpDnuik1sg">
901 <children xmi:type="notation:Node" xmi:id="_vd8BUKA9EeuqkpDnuik1sg" type="6001">
902 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vd8BUaA9EeuqkpDnuik1sg" x="-28" y="-10"/>
903 </children>
904 <children xmi:type="notation:Node" xmi:id="_vd8BUqA9EeuqkpDnuik1sg" type="6002">
905 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vd8BU6A9EeuqkpDnuik1sg" x="-28" y="6"/>
906 </children>
907 <children xmi:type="notation:Node" xmi:id="_vd8BVKA9EeuqkpDnuik1sg" type="6003">
908 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vd8BVaA9EeuqkpDnuik1sg" x="-28" y="10"/>
909 </children>
910 <styles xmi:type="notation:ConnectorStyle" xmi:id="_vd7aQaA9EeuqkpDnuik1sg" routing="Tree"/>
911 <styles xmi:type="notation:FontStyle" xmi:id="_vd7aQqA9EeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
912 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_vd7aQ6A9EeuqkpDnuik1sg" points="[0, -3, -476, 192]$[0, -51, -476, 144]$[477, -51, 1, 144]$[477, -97, 1, 98]"/>
913 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_vd8BVqA9EeuqkpDnuik1sg" id="(0.4322033898305085,0.030612244897959183)"/>
914 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_vd8BV6A9EeuqkpDnuik1sg" id="(0.5,0.0)"/>
915 </edges>
916 <edges xmi:type="notation:Edge" xmi:id="_2ko-QKA9EeuqkpDnuik1sg" type="4001" element="_2kWqbKA9EeuqkpDnuik1sg" source="_zaq8oKA9EeuqkpDnuik1sg" target="_JT0o8KA4EeuqkpDnuik1sg">
917 <children xmi:type="notation:Node" xmi:id="_2ko-RKA9EeuqkpDnuik1sg" type="6001">
918 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_2ko-RaA9EeuqkpDnuik1sg" x="-53" y="-17"/>
919 </children>
920 <children xmi:type="notation:Node" xmi:id="_2ko-RqA9EeuqkpDnuik1sg" type="6002">
921 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_2ko-R6A9EeuqkpDnuik1sg" x="21" y="10"/>
922 </children>
923 <children xmi:type="notation:Node" xmi:id="_2ko-SKA9EeuqkpDnuik1sg" type="6003">
924 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_2ko-SaA9EeuqkpDnuik1sg" x="3" y="10"/>
925 </children>
926 <styles xmi:type="notation:ConnectorStyle" xmi:id="_2ko-QaA9EeuqkpDnuik1sg" routing="Rectilinear"/>
927 <styles xmi:type="notation:FontStyle" xmi:id="_2ko-QqA9EeuqkpDnuik1sg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
928 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_2ko-Q6A9EeuqkpDnuik1sg" points="[0, 36, -218, 36]$[218, 36, 0, 36]"/>
929 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_2ko-SqA9EeuqkpDnuik1sg" id="(1.0,0.24742268041237114)"/>
930 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_2ko-S6A9EeuqkpDnuik1sg" id="(0.0,0.24489795918367346)"/>
931 </edges>
932 <edges xmi:type="notation:Edge" xmi:id="_F27ckKA-EeuqkpDnuik1sg" type="4001" element="_F2vPU6A-EeuqkpDnuik1sg" source="_QUDYMKA9EeuqkpDnuik1sg" target="_zaq8oKA9EeuqkpDnuik1sg">
933 <children xmi:type="notation:Node" xmi:id="_F28DoKA-EeuqkpDnuik1sg" type="6001">
934 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_F28DoaA-EeuqkpDnuik1sg" x="-6" y="-56"/>
935 </children>
936 <children xmi:type="notation:Node" xmi:id="_F28DoqA-EeuqkpDnuik1sg" type="6002">
937 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_F28Do6A-EeuqkpDnuik1sg" x="-2" y="10"/>
938 </children>
939 <children xmi:type="notation:Node" xmi:id="_F28DpKA-EeuqkpDnuik1sg" type="6003">
940 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_F28DpaA-EeuqkpDnuik1sg" x="-10" y="10"/>
941 </children>
942 <styles xmi:type="notation:ConnectorStyle" xmi:id="_F27ckaA-EeuqkpDnuik1sg" routing="Rectilinear"/>
943 <styles xmi:type="notation:FontStyle" xmi:id="_F27ckqA-EeuqkpDnuik1sg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
944 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_F27ck6A-EeuqkpDnuik1sg" points="[0, 0, 0, -94]$[0, 94, 0, 0]"/>
945 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_F28DpqA-EeuqkpDnuik1sg" id="(0.5,1.0)"/>
946 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_F28Dp6A-EeuqkpDnuik1sg" id="(0.5,0.0)"/>
947 </edges>
948 <edges xmi:type="notation:Edge" xmi:id="_c_hu0KBJEeuqkpDnuik1sg" type="4001" element="_c_boQqBJEeuqkpDnuik1sg" source="_Tx6IsKBJEeuqkpDnuik1sg" target="_Ren3cKBJEeuqkpDnuik1sg">
949 <children xmi:type="notation:Node" xmi:id="_c_hu1KBJEeuqkpDnuik1sg" type="6001">
950 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_c_hu1aBJEeuqkpDnuik1sg" x="-16" y="-10"/>
951 </children>
952 <children xmi:type="notation:Node" xmi:id="_c_hu1qBJEeuqkpDnuik1sg" type="6002">
953 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_c_hu16BJEeuqkpDnuik1sg" y="10"/>
954 </children>
955 <children xmi:type="notation:Node" xmi:id="_c_hu2KBJEeuqkpDnuik1sg" type="6003">
956 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_c_hu2aBJEeuqkpDnuik1sg" x="-17" y="-9"/>
957 </children>
958 <styles xmi:type="notation:ConnectorStyle" xmi:id="_c_hu0aBJEeuqkpDnuik1sg" routing="Tree"/>
959 <styles xmi:type="notation:FontStyle" xmi:id="_c_hu0qBJEeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
960 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_c_hu06BJEeuqkpDnuik1sg" points="[0, 0, -108, 119]$[0, -36, -108, 83]$[109, -36, 1, 83]$[109, -70, 1, 49]"/>
961 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_c_hu2qBJEeuqkpDnuik1sg" id="(0.5,0.0)"/>
962 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_c_hu26BJEeuqkpDnuik1sg" id="(0.5,0.5)"/>
963 </edges>
964 <edges xmi:type="notation:Edge" xmi:id="_doLqQKBJEeuqkpDnuik1sg" type="4001" element="_dn26MqBJEeuqkpDnuik1sg" source="_aPNjUKBJEeuqkpDnuik1sg" target="_Ren3cKBJEeuqkpDnuik1sg">
965 <children xmi:type="notation:Node" xmi:id="_doLqRKBJEeuqkpDnuik1sg" type="6001">
966 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_doLqRaBJEeuqkpDnuik1sg" y="-10"/>
967 </children>
968 <children xmi:type="notation:Node" xmi:id="_doLqRqBJEeuqkpDnuik1sg" type="6002">
969 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_doLqR6BJEeuqkpDnuik1sg" y="10"/>
970 </children>
971 <children xmi:type="notation:Node" xmi:id="_doLqSKBJEeuqkpDnuik1sg" type="6003">
972 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_doLqSaBJEeuqkpDnuik1sg" y="10"/>
973 </children>
974 <styles xmi:type="notation:ConnectorStyle" xmi:id="_doLqQaBJEeuqkpDnuik1sg" routing="Tree"/>
975 <styles xmi:type="notation:FontStyle" xmi:id="_doLqQqBJEeuqkpDnuik1sg" fontName="Noto Sans" fontHeight="8"/>
976 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_doLqQ6BJEeuqkpDnuik1sg" points="[0, 0, 108, 70]$[-108, -70, 0, 0]"/>
977 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_doLqSqBJEeuqkpDnuik1sg" id="(0.711864406779661,0.0)"/>
978 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_doLqS6BJEeuqkpDnuik1sg" id="(0.5,0.5)"/>
979 </edges>
980 <edges xmi:type="notation:Edge" xmi:id="_n5uykKBJEeuqkpDnuik1sg" type="4001" element="_n5fiHqBJEeuqkpDnuik1sg" source="_zaq8oKA9EeuqkpDnuik1sg" target="_Ren3cKBJEeuqkpDnuik1sg">
981 <children xmi:type="notation:Node" xmi:id="_n5uylKBJEeuqkpDnuik1sg" type="6001">
982 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_n5uylaBJEeuqkpDnuik1sg" x="-5" y="-55"/>
983 </children>
984 <children xmi:type="notation:Node" xmi:id="_n5uylqBJEeuqkpDnuik1sg" type="6002">
985 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_n5uyl6BJEeuqkpDnuik1sg" y="10"/>
986 </children>
987 <children xmi:type="notation:Node" xmi:id="_n5uymKBJEeuqkpDnuik1sg" type="6003">
988 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_n5uymaBJEeuqkpDnuik1sg" y="10"/>
989 </children>
990 <styles xmi:type="notation:ConnectorStyle" xmi:id="_n5uykaBJEeuqkpDnuik1sg" routing="Rectilinear"/>
991 <styles xmi:type="notation:FontStyle" xmi:id="_n5uykqBJEeuqkpDnuik1sg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
992 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_n5uyk6BJEeuqkpDnuik1sg" points="[0, 0, 0, -143]$[0, 143, 0, 0]"/>
993 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_n5uymqBJEeuqkpDnuik1sg" id="(0.4067796610169492,1.0)"/>
994 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_n5uym6BJEeuqkpDnuik1sg" id="(0.4067796610169492,0.0)"/>
995 </edges>
996 <edges xmi:type="notation:Edge" xmi:id="_p1cRUKBJEeuqkpDnuik1sg" type="4001" element="_p1JWcqBJEeuqkpDnuik1sg" source="_c-HCQKA4EeuqkpDnuik1sg" target="_Ren3cKBJEeuqkpDnuik1sg">
997 <children xmi:type="notation:Node" xmi:id="_p1c4YKBJEeuqkpDnuik1sg" type="6001">
998 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_p1c4YaBJEeuqkpDnuik1sg" x="10" y="-21"/>
999 </children>
1000 <children xmi:type="notation:Node" xmi:id="_p1c4YqBJEeuqkpDnuik1sg" type="6002">
1001 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_p1c4Y6BJEeuqkpDnuik1sg" y="10"/>
1002 </children>
1003 <children xmi:type="notation:Node" xmi:id="_p1c4ZKBJEeuqkpDnuik1sg" type="6003">
1004 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_p1c4ZaBJEeuqkpDnuik1sg" y="10"/>
1005 </children>
1006 <styles xmi:type="notation:ConnectorStyle" xmi:id="_p1cRUaBJEeuqkpDnuik1sg" routing="Rectilinear"/>
1007 <styles xmi:type="notation:FontStyle" xmi:id="_p1cRUqBJEeuqkpDnuik1sg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1008 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_p1cRU6BJEeuqkpDnuik1sg" points="[0, -10, 134, -10]$[-134, -10, 0, -10]"/>
1009 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_p1c4ZqBJEeuqkpDnuik1sg" id="(0.0,0.5918367346938775)"/>
1010 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_p1c4Z6BJEeuqkpDnuik1sg" id="(1.0,0.5918367346938775)"/>
1011 </edges>
1012 <edges xmi:type="notation:Edge" xmi:id="_m_tfgNXtEeuF_d0WEhR3Xw" type="4001" element="_m-6OTNXtEeuF_d0WEhR3Xw" source="_D1D6MKA4EeuqkpDnuik1sg" target="_RzZA0KA5EeuqkpDnuik1sg">
1013 <children xmi:type="notation:Node" xmi:id="_m_wi0NXtEeuF_d0WEhR3Xw" type="6001">
1014 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_m_wi0dXtEeuF_d0WEhR3Xw" x="73" y="-10"/>
1015 </children>
1016 <children xmi:type="notation:Node" xmi:id="_m_xJ4NXtEeuF_d0WEhR3Xw" type="6002">
1017 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_m_xJ4dXtEeuF_d0WEhR3Xw" x="-5" y="-9"/>
1018 </children>
1019 <children xmi:type="notation:Node" xmi:id="_m_xw8NXtEeuF_d0WEhR3Xw" type="6003">
1020 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_m_xw8dXtEeuF_d0WEhR3Xw" x="-4" y="24"/>
1021 </children>
1022 <styles xmi:type="notation:ConnectorStyle" xmi:id="_m_tfgdXtEeuF_d0WEhR3Xw" routing="Tree"/>
1023 <styles xmi:type="notation:FontStyle" xmi:id="_m_tfgtXtEeuF_d0WEhR3Xw" fontName="Noto Sans" fontHeight="8"/>
1024 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_m_tfg9XtEeuF_d0WEhR3Xw" points="[0, 0, 205, 107]$[0, -24, 205, 83]$[-204, -24, 1, 83]$[-204, -58, 1, 49]"/>
1025 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_m_00QNXtEeuF_d0WEhR3Xw" id="(0.711864406779661,0.0)"/>
1026 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_m_00QdXtEeuF_d0WEhR3Xw" id="(0.5,0.5)"/>
1027 </edges>
1028 <edges xmi:type="notation:Edge" xmi:id="_HUhwENYBEeuF_d0WEhR3Xw" type="4001" element="_HUJ8o9YBEeuF_d0WEhR3Xw" source="_D-lH8NYBEeuF_d0WEhR3Xw" target="_Ren3cKBJEeuqkpDnuik1sg">
1029 <children xmi:type="notation:Node" xmi:id="_HUhwFNYBEeuF_d0WEhR3Xw" type="6001">
1030 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_HUhwFdYBEeuF_d0WEhR3Xw" y="-10"/>
1031 </children>
1032 <children xmi:type="notation:Node" xmi:id="_HUhwFtYBEeuF_d0WEhR3Xw" type="6002">
1033 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_HUhwF9YBEeuF_d0WEhR3Xw" y="10"/>
1034 </children>
1035 <children xmi:type="notation:Node" xmi:id="_HUhwGNYBEeuF_d0WEhR3Xw" type="6003">
1036 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_HUhwGdYBEeuF_d0WEhR3Xw" y="10"/>
1037 </children>
1038 <styles xmi:type="notation:ConnectorStyle" xmi:id="_HUhwEdYBEeuF_d0WEhR3Xw" routing="Tree"/>
1039 <styles xmi:type="notation:FontStyle" xmi:id="_HUhwEtYBEeuF_d0WEhR3Xw" fontName="Noto Sans" fontHeight="8"/>
1040 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_HUhwE9YBEeuF_d0WEhR3Xw" points="[0, 0, -228, 144]$[228, -144, 0, 0]"/>
1041 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_HUhwGtYBEeuF_d0WEhR3Xw" id="(0.41379310344827586,0.0)"/>
1042 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_HUhwG9YBEeuF_d0WEhR3Xw" id="(0.5,0.5)"/>
1043 </edges>
1044 <edges xmi:type="notation:Edge" xmi:id="_byTTcNYPEeuF_d0WEhR3Xw" type="4001" element="_bx-jYtYPEeuF_d0WEhR3Xw" source="_IwsqwNYPEeuF_d0WEhR3Xw" target="_-O-UEKA6EeuqkpDnuik1sg">
1045 <children xmi:type="notation:Node" xmi:id="_byTTdNYPEeuF_d0WEhR3Xw" type="6001">
1046 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_byTTddYPEeuF_d0WEhR3Xw" x="25" y="-10"/>
1047 </children>
1048 <children xmi:type="notation:Node" xmi:id="_byTTdtYPEeuF_d0WEhR3Xw" type="6002">
1049 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_byTTd9YPEeuF_d0WEhR3Xw" x="12" y="-447"/>
1050 </children>
1051 <children xmi:type="notation:Node" xmi:id="_byTTeNYPEeuF_d0WEhR3Xw" type="6003">
1052 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_byTTedYPEeuF_d0WEhR3Xw" x="8" y="10"/>
1053 </children>
1054 <styles xmi:type="notation:ConnectorStyle" xmi:id="_byTTcdYPEeuF_d0WEhR3Xw" routing="Rectilinear"/>
1055 <styles xmi:type="notation:FontStyle" xmi:id="_byTTctYPEeuF_d0WEhR3Xw" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1056 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_byTTc9YPEeuF_d0WEhR3Xw" points="[19, 98, -508, -145]$[19, 181, -508, -62]$[479, 181, -48, -62]"/>
1057 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_byT6gNYPEeuF_d0WEhR3Xw" id="(0.3821656050955414,0.0)"/>
1058 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_byT6gdYPEeuF_d0WEhR3Xw" id="(0.4067796610169492,1.0)"/>
1059 </edges>
1060 <edges xmi:type="notation:Edge" xmi:id="_-X6xcNawEeuymriYTNxK2g" type="4001" element="_-XLKltawEeuymriYTNxK2g" source="_JT0o8KA4EeuqkpDnuik1sg" target="_xsq_MKA8EeuqkpDnuik1sg">
1061 <children xmi:type="notation:Node" xmi:id="_-X_C4NawEeuymriYTNxK2g" type="6001">
1062 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_-X_C4dawEeuymriYTNxK2g" x="11" y="-334"/>
1063 </children>
1064 <children xmi:type="notation:Node" xmi:id="_-X_p8NawEeuymriYTNxK2g" type="6002">
1065 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_-X_p8dawEeuymriYTNxK2g" x="-6" y="10"/>
1066 </children>
1067 <children xmi:type="notation:Node" xmi:id="_-YARANawEeuymriYTNxK2g" type="6003">
1068 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_-YARAdawEeuymriYTNxK2g" x="-312" y="-166"/>
1069 </children>
1070 <styles xmi:type="notation:ConnectorStyle" xmi:id="_-X6xcdawEeuymriYTNxK2g" routing="Rectilinear"/>
1071 <styles xmi:type="notation:FontStyle" xmi:id="_-X6xctawEeuymriYTNxK2g" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1072 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_-X6xc9awEeuymriYTNxK2g" points="[-58, 86, -288, 422]$[-58, 132, -288, 468]$[314, 132, 84, 468]$[314, -298, 84, 38]"/>
1073 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_-YCtQNawEeuymriYTNxK2g" id="(1.0,0.12244897959183673)"/>
1074 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_-YCtQdawEeuymriYTNxK2g" id="(0.0,0.6122448979591837)"/>
1075 </edges>
1076 <edges xmi:type="notation:Edge" xmi:id="_W89dcNbFEeuymriYTNxK2g" type="4001" element="_Z7FrQKA6EeuqkpDnuik1sg" source="_QKLK0KA6EeuqkpDnuik1sg" target="_EfWNUKA4EeuqkpDnuik1sg">
1077 <children xmi:type="notation:Node" xmi:id="_W89ddNbFEeuymriYTNxK2g" type="6001">
1078 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_W89dddbFEeuymriYTNxK2g" x="-338" y="-67"/>
1079 </children>
1080 <children xmi:type="notation:Node" xmi:id="_W8-EgNbFEeuymriYTNxK2g" type="6002">
1081 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_W8-EgdbFEeuymriYTNxK2g" x="-22" y="3"/>
1082 </children>
1083 <children xmi:type="notation:Node" xmi:id="_W8-EgtbFEeuymriYTNxK2g" type="6003">
1084 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_W8-Eg9bFEeuymriYTNxK2g" x="29" y="10"/>
1085 </children>
1086 <styles xmi:type="notation:ConnectorStyle" xmi:id="_W89dcdbFEeuymriYTNxK2g" routing="Rectilinear"/>
1087 <styles xmi:type="notation:FontStyle" xmi:id="_W89dctbFEeuymriYTNxK2g" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1088 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_W89dc9bFEeuymriYTNxK2g" points="[-75, 50, 505, 414]$[-182, 50, 398, 414]$[-182, -384, 398, -20]$[-569, -384, 11, -20]"/>
1089 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_W8_SoNbFEeuymriYTNxK2g" id="(0.635593220338983,0.0)"/>
1090 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_W8_SodbFEeuymriYTNxK2g" id="(0.9067796610169492,0.6938775510204082)"/>
1091 </edges>
1092 <edges xmi:type="notation:Edge" xmi:id="_ev0VwNbFEeuymriYTNxK2g" type="4001" element="_ufJ3IKA4EeuqkpDnuik1sg" source="_c-HCQKA4EeuqkpDnuik1sg" target="_EfWNUKA4EeuqkpDnuik1sg">
1093 <children xmi:type="notation:Node" xmi:id="_ev0VxNbFEeuymriYTNxK2g" type="6001">
1094 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_ev0VxdbFEeuymriYTNxK2g" x="-218" y="-22"/>
1095 </children>
1096 <children xmi:type="notation:Node" xmi:id="_ev0VxtbFEeuymriYTNxK2g" type="6002">
1097 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_ev0Vx9bFEeuymriYTNxK2g" x="2" y="10"/>
1098 </children>
1099 <children xmi:type="notation:Node" xmi:id="_ev0VyNbFEeuymriYTNxK2g" type="6003">
1100 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_ev0VydbFEeuymriYTNxK2g" x="-2" y="10"/>
1101 </children>
1102 <styles xmi:type="notation:ConnectorStyle" xmi:id="_ev0VwdbFEeuymriYTNxK2g" routing="Rectilinear"/>
1103 <styles xmi:type="notation:FontStyle" xmi:id="_ev0VwtbFEeuymriYTNxK2g" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1104 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_ev0Vw9bFEeuymriYTNxK2g" points="[37, 36, -4, 419]$[156, 36, 115, 419]$[156, -372, 115, 11]$[46, -372, 5, 11]"/>
1105 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_ev0VytbFEeuymriYTNxK2g" id="(0.7448275862068966,0.0)"/>
1106 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_ev080NbFEeuymriYTNxK2g" id="(0.9576271186440678,0.5)"/>
1107 </edges>
1108 <edges xmi:type="notation:Edge" xmi:id="_f-FfsNbFEeuymriYTNxK2g" type="4001" element="_VtPctqA4EeuqkpDnuik1sg" source="_JT0o8KA4EeuqkpDnuik1sg" target="_EfWNUKA4EeuqkpDnuik1sg">
1109 <children xmi:type="notation:Node" xmi:id="_f-FftNbFEeuymriYTNxK2g" type="6001">
1110 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_f-FftdbFEeuymriYTNxK2g" x="6" y="31"/>
1111 </children>
1112 <children xmi:type="notation:Node" xmi:id="_f-FfttbFEeuymriYTNxK2g" type="6002">
1113 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_f-Fft9bFEeuymriYTNxK2g" x="-10"/>
1114 </children>
1115 <children xmi:type="notation:Node" xmi:id="_f-FfuNbFEeuymriYTNxK2g" type="6003">
1116 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_f-FfudbFEeuymriYTNxK2g" x="-10"/>
1117 </children>
1118 <styles xmi:type="notation:ConnectorStyle" xmi:id="_f-FfsdbFEeuymriYTNxK2g" routing="Rectilinear"/>
1119 <styles xmi:type="notation:FontStyle" xmi:id="_f-FfstbFEeuymriYTNxK2g" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1120 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_f-Ffs9bFEeuymriYTNxK2g" points="[24, -98, -17, 129]$[24, -192, -17, 35]"/>
1121 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_f-GGwNbFEeuymriYTNxK2g" id="(0.2033898305084746,1.0)"/>
1122 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_f-GGwdbFEeuymriYTNxK2g" id="(0.9576271186440678,0.6428571428571429)"/>
1123 </edges>
1124 <edges xmi:type="notation:Edge" xmi:id="_UwuZ8NbGEeuymriYTNxK2g" type="4001" element="_UwbfHtbGEeuymriYTNxK2g" source="_M6O-0NbGEeuymriYTNxK2g" target="_EfWNUKA4EeuqkpDnuik1sg">
1125 <children xmi:type="notation:Node" xmi:id="_UwuZ9NbGEeuymriYTNxK2g" type="6001">
1126 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_UwuZ9dbGEeuymriYTNxK2g" y="-10"/>
1127 </children>
1128 <children xmi:type="notation:Node" xmi:id="_UwuZ9tbGEeuymriYTNxK2g" type="6002">
1129 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_UwuZ99bGEeuymriYTNxK2g" y="10"/>
1130 </children>
1131 <children xmi:type="notation:Node" xmi:id="_UwuZ-NbGEeuymriYTNxK2g" type="6003">
1132 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_UwuZ-dbGEeuymriYTNxK2g" y="10"/>
1133 </children>
1134 <styles xmi:type="notation:ConnectorStyle" xmi:id="_UwuZ8dbGEeuymriYTNxK2g" routing="Tree"/>
1135 <styles xmi:type="notation:FontStyle" xmi:id="_UwuZ8tbGEeuymriYTNxK2g" fontName="Noto Sans" fontHeight="8"/>
1136 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_UwuZ89bGEeuymriYTNxK2g" points="[0, 0, 220, 180]$[-220, -180, 0, 0]"/>
1137 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_UwvBANbGEeuymriYTNxK2g" id="(0.5,0.0)"/>
1138 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_UwvBAdbGEeuymriYTNxK2g" id="(0.5,0.5)"/>
1139 </edges>
1140 <edges xmi:type="notation:Edge" xmi:id="_WYV4ANbGEeuymriYTNxK2g" type="4001" element="_WX_5w9bGEeuymriYTNxK2g" source="_M6O-0NbGEeuymriYTNxK2g" target="_e73WIKA9EeuqkpDnuik1sg">
1141 <children xmi:type="notation:Node" xmi:id="_WYV4BNbGEeuymriYTNxK2g" type="6001">
1142 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_WYV4BdbGEeuymriYTNxK2g" y="-10"/>
1143 </children>
1144 <children xmi:type="notation:Node" xmi:id="_WYV4BtbGEeuymriYTNxK2g" type="6002">
1145 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_WYV4B9bGEeuymriYTNxK2g" y="10"/>
1146 </children>
1147 <children xmi:type="notation:Node" xmi:id="_WYV4CNbGEeuymriYTNxK2g" type="6003">
1148 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_WYV4CdbGEeuymriYTNxK2g" y="10"/>
1149 </children>
1150 <styles xmi:type="notation:ConnectorStyle" xmi:id="_WYV4AdbGEeuymriYTNxK2g" routing="Tree"/>
1151 <styles xmi:type="notation:FontStyle" xmi:id="_WYV4AtbGEeuymriYTNxK2g" fontName="Noto Sans" fontHeight="8"/>
1152 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_WYV4A9bGEeuymriYTNxK2g" points="[0, 0, 99, 384]$[0, -240, 99, 144]$[-98, -240, 1, 144]$[-98, -286, 1, 98]"/>
1153 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_WYV4CtbGEeuymriYTNxK2g" id="(0.2627118644067797,0.01020408163265306)"/>
1154 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_WYV4C9bGEeuymriYTNxK2g" id="(0.5,0.0)"/>
1155 </edges>
1156 <edges xmi:type="notation:Edge" xmi:id="_gRXLkNbGEeuymriYTNxK2g" type="4001" element="_gRDCgNbGEeuymriYTNxK2g" source="_M6O-0NbGEeuymriYTNxK2g" target="_xsq_MKA8EeuqkpDnuik1sg">
1157 <children xmi:type="notation:Node" xmi:id="_gRXLlNbGEeuymriYTNxK2g" type="6001">
1158 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_gRXLldbGEeuymriYTNxK2g" x="-2" y="-41"/>
1159 </children>
1160 <children xmi:type="notation:Node" xmi:id="_gRXyoNbGEeuymriYTNxK2g" type="6002">
1161 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_gRXyodbGEeuymriYTNxK2g" x="98" y="10"/>
1162 </children>
1163 <children xmi:type="notation:Node" xmi:id="_gRXyotbGEeuymriYTNxK2g" type="6003">
1164 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_gRXyo9bGEeuymriYTNxK2g" x="7" y="10"/>
1165 </children>
1166 <styles xmi:type="notation:ConnectorStyle" xmi:id="_gRXLkdbGEeuymriYTNxK2g" routing="Rectilinear"/>
1167 <styles xmi:type="notation:FontStyle" xmi:id="_gRXLktbGEeuymriYTNxK2g" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1168 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_gRXLk9bGEeuymriYTNxK2g" points="[25, 48, -35, 334]$[82, 48, 22, 334]$[82, -286, 22, 0]"/>
1169 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_gRXypNbGEeuymriYTNxK2g" id="(0.788135593220339,0.0)"/>
1170 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_gRXypdbGEeuymriYTNxK2g" id="(0.3050847457627119,1.0)"/>
1171 </edges>
1172 <edges xmi:type="notation:Edge" xmi:id="_Ql_asNbNEeuymriYTNxK2g" type="4001" element="_QlymotbNEeuymriYTNxK2g" source="_xsq_MKA8EeuqkpDnuik1sg" target="_Kw-vINbNEeuymriYTNxK2g">
1173 <children xmi:type="notation:Node" xmi:id="_Ql_atNbNEeuymriYTNxK2g" type="6001">
1174 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Ql_atdbNEeuymriYTNxK2g" y="-10"/>
1175 </children>
1176 <children xmi:type="notation:Node" xmi:id="_QmABwNbNEeuymriYTNxK2g" type="6002">
1177 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_QmABwdbNEeuymriYTNxK2g" y="10"/>
1178 </children>
1179 <children xmi:type="notation:Node" xmi:id="_QmABwtbNEeuymriYTNxK2g" type="6003">
1180 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_QmABw9bNEeuymriYTNxK2g" y="10"/>
1181 </children>
1182 <styles xmi:type="notation:ConnectorStyle" xmi:id="_Ql_asdbNEeuymriYTNxK2g" routing="Tree"/>
1183 <styles xmi:type="notation:FontStyle" xmi:id="_Ql_astbNEeuymriYTNxK2g" fontName="Noto Sans" fontHeight="8"/>
1184 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_Ql_as9bNEeuymriYTNxK2g" points="[0, 0, -84, 58]$[84, -58, 0, 0]"/>
1185 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_QmABxNbNEeuymriYTNxK2g" id="(0.7796610169491526,0.01020408163265306)"/>
1186 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_QmABxdbNEeuymriYTNxK2g" id="(0.5,0.5)"/>
1187 </edges>
1188 <edges xmi:type="notation:Edge" xmi:id="_SmujYNbNEeuymriYTNxK2g" type="4001" element="_Smi9eNbNEeuymriYTNxK2g" source="_Kw-vINbNEeuymriYTNxK2g" target="_RzZA0KA5EeuqkpDnuik1sg">
1189 <children xmi:type="notation:Node" xmi:id="_SmvKcNbNEeuymriYTNxK2g" type="6001">
1190 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_SmvKcdbNEeuymriYTNxK2g" y="-10"/>
1191 </children>
1192 <children xmi:type="notation:Node" xmi:id="_SmvKctbNEeuymriYTNxK2g" type="6002">
1193 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_SmvKc9bNEeuymriYTNxK2g" y="10"/>
1194 </children>
1195 <children xmi:type="notation:Node" xmi:id="_SmvKdNbNEeuymriYTNxK2g" type="6003">
1196 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_SmvKddbNEeuymriYTNxK2g" y="10"/>
1197 </children>
1198 <styles xmi:type="notation:ConnectorStyle" xmi:id="_SmujYdbNEeuymriYTNxK2g" routing="Tree"/>
1199 <styles xmi:type="notation:FontStyle" xmi:id="_SmujYtbNEeuymriYTNxK2g" fontName="Noto Sans" fontHeight="8"/>
1200 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_SmujY9bNEeuymriYTNxK2g" points="[0, 0, 84, 58]$[-84, -58, 0, 0]"/>
1201 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_SmvKdtbNEeuymriYTNxK2g" id="(0.5423728813559322,0.07142857142857142)"/>
1202 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_SmvKd9bNEeuymriYTNxK2g" id="(0.5,0.5)"/>
1203 </edges>
1204 <edges xmi:type="notation:Edge" xmi:id="_TuLToNbNEeuymriYTNxK2g" type="4001" element="_Tt9RRtbNEeuymriYTNxK2g" source="_jP6FkKA6EeuqkpDnuik1sg" target="_Kw-vINbNEeuymriYTNxK2g">
1205 <children xmi:type="notation:Node" xmi:id="_TuLTpNbNEeuymriYTNxK2g" type="6001">
1206 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_TuLTpdbNEeuymriYTNxK2g" y="-10"/>
1207 </children>
1208 <children xmi:type="notation:Node" xmi:id="_TuL6sNbNEeuymriYTNxK2g" type="6002">
1209 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_TuL6sdbNEeuymriYTNxK2g" y="10"/>
1210 </children>
1211 <children xmi:type="notation:Node" xmi:id="_TuL6stbNEeuymriYTNxK2g" type="6003">
1212 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_TuL6s9bNEeuymriYTNxK2g" y="10"/>
1213 </children>
1214 <styles xmi:type="notation:ConnectorStyle" xmi:id="_TuLTodbNEeuymriYTNxK2g" routing="Tree"/>
1215 <styles xmi:type="notation:FontStyle" xmi:id="_TuLTotbNEeuymriYTNxK2g" fontName="Noto Sans" fontHeight="8"/>
1216 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_TuLTo9bNEeuymriYTNxK2g" points="[0, 0, 96, 442]$[-96, -442, 0, 0]"/>
1217 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_TuL6tNbNEeuymriYTNxK2g" id="(0.4067796610169492,0.0)"/>
1218 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_TuL6tdbNEeuymriYTNxK2g" id="(0.5,0.5)"/>
1219 </edges>
1220 <edges xmi:type="notation:Edge" xmi:id="_UosbINbNEeuymriYTNxK2g" type="4001" element="_T1fN5tYPEeuF_d0WEhR3Xw" source="_IwsqwNYPEeuF_d0WEhR3Xw" target="_Kw-vINbNEeuymriYTNxK2g">
1221 <children xmi:type="notation:Node" xmi:id="_UosbJNbNEeuymriYTNxK2g" type="6001">
1222 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_UosbJdbNEeuymriYTNxK2g" x="-600" y="6"/>
1223 </children>
1224 <children xmi:type="notation:Node" xmi:id="_UotCMNbNEeuymriYTNxK2g" type="6002">
1225 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_UotCMdbNEeuymriYTNxK2g" x="-50" y="10"/>
1226 </children>
1227 <children xmi:type="notation:Node" xmi:id="_UotCMtbNEeuymriYTNxK2g" type="6003">
1228 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_UotCM9bNEeuymriYTNxK2g" x="-9" y="10"/>
1229 </children>
1230 <styles xmi:type="notation:ConnectorStyle" xmi:id="_UosbIdbNEeuymriYTNxK2g" routing="Rectilinear"/>
1231 <styles xmi:type="notation:FontStyle" xmi:id="_UosbItbNEeuymriYTNxK2g" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1232 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_UosbI9bNEeuymriYTNxK2g" points="[18, -18, 16, 1340]$[18, -1347, 16, 11]"/>
1233 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_UotCNNbNEeuymriYTNxK2g" id="(0.2356687898089172,0.1836734693877551)"/>
1234 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_UotCNdbNEeuymriYTNxK2g" id="(0.16101694915254236,0.8877551020408163)"/>
1235 </edges>
1236 <edges xmi:type="notation:Edge" xmi:id="_Vs_14Nd_EeufiOvRR5sVhg" type="4001" element="_Vsm0XNd_EeufiOvRR5sVhg" source="_KdsE0Nd_EeufiOvRR5sVhg" target="_IsM5ENd_EeufiOvRR5sVhg">
1237 <children xmi:type="notation:Node" xmi:id="_VtAc8Nd_EeufiOvRR5sVhg" type="6001">
1238 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_VtBEANd_EeufiOvRR5sVhg" y="-10"/>
1239 </children>
1240 <children xmi:type="notation:Node" xmi:id="_VtBEAdd_EeufiOvRR5sVhg" type="6002">
1241 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_VtBEAtd_EeufiOvRR5sVhg" y="10"/>
1242 </children>
1243 <children xmi:type="notation:Node" xmi:id="_VtBrENd_EeufiOvRR5sVhg" type="6003">
1244 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_VtBrEdd_EeufiOvRR5sVhg" y="10"/>
1245 </children>
1246 <styles xmi:type="notation:ConnectorStyle" xmi:id="_Vs_14dd_EeufiOvRR5sVhg" routing="Tree"/>
1247 <styles xmi:type="notation:FontStyle" xmi:id="_Vs_14td_EeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
1248 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_Vs_149d_EeufiOvRR5sVhg" points="[0, 0, -72, 120]$[72, -120, 0, 0]"/>
1249 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_VtEHUNd_EeufiOvRR5sVhg" id="(0.6101694915254238,0.0)"/>
1250 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_VtEHUdd_EeufiOvRR5sVhg" id="(0.5,0.5)"/>
1251 </edges>
1252 <edges xmi:type="notation:Edge" xmi:id="_WEehwNd_EeufiOvRR5sVhg" type="4001" element="_WEGuVtd_EeufiOvRR5sVhg" source="_MAkM0Nd_EeufiOvRR5sVhg" target="_IsM5ENd_EeufiOvRR5sVhg">
1253 <children xmi:type="notation:Node" xmi:id="_WEehxNd_EeufiOvRR5sVhg" type="6001">
1254 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_WEehxdd_EeufiOvRR5sVhg" y="-10"/>
1255 </children>
1256 <children xmi:type="notation:Node" xmi:id="_WEehxtd_EeufiOvRR5sVhg" type="6002">
1257 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_WEehx9d_EeufiOvRR5sVhg" y="10"/>
1258 </children>
1259 <children xmi:type="notation:Node" xmi:id="_WEehyNd_EeufiOvRR5sVhg" type="6003">
1260 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_WEehydd_EeufiOvRR5sVhg" y="10"/>
1261 </children>
1262 <styles xmi:type="notation:ConnectorStyle" xmi:id="_WEehwdd_EeufiOvRR5sVhg" routing="Tree"/>
1263 <styles xmi:type="notation:FontStyle" xmi:id="_WEehwtd_EeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
1264 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_WEehw9d_EeufiOvRR5sVhg" points="[0, 0, 0, 46]$[0, -46, 0, 0]"/>
1265 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_WEfI0Nd_EeufiOvRR5sVhg" id="(0.6101694915254238,0.0)"/>
1266 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_WEfI0dd_EeufiOvRR5sVhg" id="(0.5,0.5)"/>
1267 </edges>
1268 <edges xmi:type="notation:Edge" xmi:id="_Wa3BcNd_EeufiOvRR5sVhg" type="4001" element="_Waktldd_EeufiOvRR5sVhg" source="_RwPFYNd_EeufiOvRR5sVhg" target="_IsM5ENd_EeufiOvRR5sVhg">
1269 <children xmi:type="notation:Node" xmi:id="_Wa3ogNd_EeufiOvRR5sVhg" type="6001">
1270 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Wa3ogdd_EeufiOvRR5sVhg" y="-10"/>
1271 </children>
1272 <children xmi:type="notation:Node" xmi:id="_Wa3ogtd_EeufiOvRR5sVhg" type="6002">
1273 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Wa3og9d_EeufiOvRR5sVhg" y="10"/>
1274 </children>
1275 <children xmi:type="notation:Node" xmi:id="_Wa3ohNd_EeufiOvRR5sVhg" type="6003">
1276 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Wa3ohdd_EeufiOvRR5sVhg" y="10"/>
1277 </children>
1278 <styles xmi:type="notation:ConnectorStyle" xmi:id="_Wa3Bcdd_EeufiOvRR5sVhg" routing="Tree"/>
1279 <styles xmi:type="notation:FontStyle" xmi:id="_Wa3Bctd_EeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
1280 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_Wa3Bc9d_EeufiOvRR5sVhg" points="[0, 0, 85, 132]$[-85, -132, 0, 0]"/>
1281 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_Wa3ohtd_EeufiOvRR5sVhg" id="(0.5,0.0)"/>
1282 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_Wa3oh9d_EeufiOvRR5sVhg" id="(0.5,0.5)"/>
1283 </edges>
1284 <edges xmi:type="notation:Edge" xmi:id="_tXLzQNd_EeufiOvRR5sVhg" type="4001" element="_KmxbkNYPEeuF_d0WEhR3Xw" source="_6KEUMKA6EeuqkpDnuik1sg" target="_rwMAQNd_EeufiOvRR5sVhg">
1285 <children xmi:type="notation:Node" xmi:id="_tXMaUNd_EeufiOvRR5sVhg" type="6001">
1286 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_tXMaUdd_EeufiOvRR5sVhg" y="-10"/>
1287 </children>
1288 <children xmi:type="notation:Node" xmi:id="_tXMaUtd_EeufiOvRR5sVhg" type="6002">
1289 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_tXMaU9d_EeufiOvRR5sVhg" y="10"/>
1290 </children>
1291 <children xmi:type="notation:Node" xmi:id="_tXMaVNd_EeufiOvRR5sVhg" type="6003">
1292 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_tXMaVdd_EeufiOvRR5sVhg" x="-11" y="10"/>
1293 </children>
1294 <styles xmi:type="notation:ConnectorStyle" xmi:id="_tXLzQdd_EeufiOvRR5sVhg" routing="Rectilinear"/>
1295 <styles xmi:type="notation:FontStyle" xmi:id="_tXLzQtd_EeufiOvRR5sVhg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1296 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_tXLzQ9d_EeufiOvRR5sVhg" points="[39, 38, -21, -81]$[39, 108, -21, -11]"/>
1297 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_tXNocNd_EeufiOvRR5sVhg" id="(0.0,0.6122448979591837)"/>
1298 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_tXNocdd_EeufiOvRR5sVhg" id="(0.5084745762711864,0.11224489795918367)"/>
1299 </edges>
1300 <edges xmi:type="notation:Edge" xmi:id="_zhTMINd_EeufiOvRR5sVhg" type="4001" element="_zhINQtd_EeufiOvRR5sVhg" source="_mCh54Nd_EeufiOvRR5sVhg" target="_rwMAQNd_EeufiOvRR5sVhg">
1301 <children xmi:type="notation:Node" xmi:id="_zhTzMNd_EeufiOvRR5sVhg" type="6001">
1302 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_zhTzMdd_EeufiOvRR5sVhg" x="-7" y="-10"/>
1303 </children>
1304 <children xmi:type="notation:Node" xmi:id="_zhTzMtd_EeufiOvRR5sVhg" type="6002">
1305 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_zhTzM9d_EeufiOvRR5sVhg" y="10"/>
1306 </children>
1307 <children xmi:type="notation:Node" xmi:id="_zhTzNNd_EeufiOvRR5sVhg" type="6003">
1308 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_zhTzNdd_EeufiOvRR5sVhg" x="-7" y="17"/>
1309 </children>
1310 <styles xmi:type="notation:ConnectorStyle" xmi:id="_zhTMIdd_EeufiOvRR5sVhg" routing="Tree"/>
1311 <styles xmi:type="notation:FontStyle" xmi:id="_zhTMItd_EeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
1312 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_zhTMI9d_EeufiOvRR5sVhg" points="[0, 0, 121, 106]$[0, -32, 121, 74]$[-120, -32, 1, 74]$[-120, -57, 1, 49]"/>
1313 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_zhTzNtd_EeufiOvRR5sVhg" id="(0.5037593984962406,0.0)"/>
1314 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_zhTzN9d_EeufiOvRR5sVhg" id="(0.5,0.5)"/>
1315 </edges>
1316 <edges xmi:type="notation:Edge" xmi:id="_1HiKINd_EeufiOvRR5sVhg" type="4001" element="_1HYZXNd_EeufiOvRR5sVhg" source="_IwsqwNYPEeuF_d0WEhR3Xw" target="_rwMAQNd_EeufiOvRR5sVhg">
1317 <children xmi:type="notation:Node" xmi:id="_1HixMNd_EeufiOvRR5sVhg" type="6001">
1318 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_1HixMdd_EeufiOvRR5sVhg" x="-7" y="-10"/>
1319 </children>
1320 <children xmi:type="notation:Node" xmi:id="_1HixMtd_EeufiOvRR5sVhg" type="6002">
1321 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_1HixM9d_EeufiOvRR5sVhg" y="10"/>
1322 </children>
1323 <children xmi:type="notation:Node" xmi:id="_1HixNNd_EeufiOvRR5sVhg" type="6003">
1324 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_1HixNdd_EeufiOvRR5sVhg" x="-13" y="-4"/>
1325 </children>
1326 <styles xmi:type="notation:ConnectorStyle" xmi:id="_1HiKIdd_EeufiOvRR5sVhg" routing="Tree"/>
1327 <styles xmi:type="notation:FontStyle" xmi:id="_1HiKItd_EeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
1328 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_1HiKI9d_EeufiOvRR5sVhg" points="[0, 0, -85, 144]$[0, -32, -85, 112]$[85, -32, 0, 112]$[85, -57, 0, 87]"/>
1329 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_yI__ACraEeyyC-O0_LlY9w" id="(0.5,0.5)"/>
1330 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_1HixN9d_EeufiOvRR5sVhg" id="(0.5084745762711864,0.11224489795918367)"/>
1331 </edges>
1332 <edges xmi:type="notation:Edge" xmi:id="_YAyoANeAEeufiOvRR5sVhg" type="4001" element="_YAoQHdeAEeufiOvRR5sVhg" source="_p9wywKA8EeuqkpDnuik1sg" target="_VikSENeAEeufiOvRR5sVhg">
1333 <children xmi:type="notation:Node" xmi:id="_YAyoBNeAEeufiOvRR5sVhg" type="6001">
1334 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_YAyoBdeAEeufiOvRR5sVhg" y="-10"/>
1335 </children>
1336 <children xmi:type="notation:Node" xmi:id="_YAyoBteAEeufiOvRR5sVhg" type="6002">
1337 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_YAyoB9eAEeufiOvRR5sVhg" y="10"/>
1338 </children>
1339 <children xmi:type="notation:Node" xmi:id="_YAyoCNeAEeufiOvRR5sVhg" type="6003">
1340 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_YAyoCdeAEeufiOvRR5sVhg" y="10"/>
1341 </children>
1342 <styles xmi:type="notation:ConnectorStyle" xmi:id="_YAyoAdeAEeufiOvRR5sVhg" routing="Rectilinear"/>
1343 <styles xmi:type="notation:FontStyle" xmi:id="_YAyoAteAEeufiOvRR5sVhg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1344 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_YAyoA9eAEeufiOvRR5sVhg" points="[0, 0, 0, -82]$[0, 82, 0, 0]"/>
1345 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_YAzPENeAEeufiOvRR5sVhg" id="(0.4406779661016949,0.8990825688073395)"/>
1346 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_YAzPEdeAEeufiOvRR5sVhg" id="(0.45112781954887216,0.0)"/>
1347 </edges>
1348 <edges xmi:type="notation:Edge" xmi:id="_dDz18NeAEeufiOvRR5sVhg" type="4001" element="_dDd3wteAEeufiOvRR5sVhg" source="_OWhiINeAEeufiOvRR5sVhg" target="_VikSENeAEeufiOvRR5sVhg">
1349 <children xmi:type="notation:Node" xmi:id="_dDz19NeAEeufiOvRR5sVhg" type="6001">
1350 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_dDz19deAEeufiOvRR5sVhg" x="-4" y="-10"/>
1351 </children>
1352 <children xmi:type="notation:Node" xmi:id="_dDz19teAEeufiOvRR5sVhg" type="6002">
1353 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_dDz199eAEeufiOvRR5sVhg" x="-1" y="10"/>
1354 </children>
1355 <children xmi:type="notation:Node" xmi:id="_dDz1-NeAEeufiOvRR5sVhg" type="6003">
1356 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_dDz1-deAEeufiOvRR5sVhg" x="-7" y="10"/>
1357 </children>
1358 <styles xmi:type="notation:ConnectorStyle" xmi:id="_dDz18deAEeufiOvRR5sVhg" routing="Tree"/>
1359 <styles xmi:type="notation:FontStyle" xmi:id="_dDz18teAEeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
1360 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_dDz189eAEeufiOvRR5sVhg" points="[-1, 0, 109, 203]$[-1, -136, 109, 67]$[-117, -136, -7, 67]$[-117, -154, -7, 49]"/>
1361 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_dDz1-teAEeufiOvRR5sVhg" id="(0.38961038961038963,0.0)"/>
1362 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_dDz1-9eAEeufiOvRR5sVhg" id="(0.5,0.5)"/>
1363 </edges>
1364 <edges xmi:type="notation:Edge" xmi:id="_g_pZYNeAEeufiOvRR5sVhg" type="4001" element="_g_aI1teAEeufiOvRR5sVhg" source="_dZlRoNeAEeufiOvRR5sVhg" target="_VikSENeAEeufiOvRR5sVhg">
1365 <children xmi:type="notation:Node" xmi:id="_g_qAcNeAEeufiOvRR5sVhg" type="6001">
1366 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_g_qAcdeAEeufiOvRR5sVhg" y="-10"/>
1367 </children>
1368 <children xmi:type="notation:Node" xmi:id="_g_qActeAEeufiOvRR5sVhg" type="6002">
1369 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_g_qAc9eAEeufiOvRR5sVhg" y="10"/>
1370 </children>
1371 <children xmi:type="notation:Node" xmi:id="_g_qAdNeAEeufiOvRR5sVhg" type="6003">
1372 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_g_qAddeAEeufiOvRR5sVhg" y="10"/>
1373 </children>
1374 <styles xmi:type="notation:ConnectorStyle" xmi:id="_g_pZYdeAEeufiOvRR5sVhg" routing="Tree"/>
1375 <styles xmi:type="notation:FontStyle" xmi:id="_g_pZYteAEeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
1376 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_g_pZY9eAEeufiOvRR5sVhg" points="[0, 0, -132, 154]$[132, -154, 0, 0]"/>
1377 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_g_qAdteAEeufiOvRR5sVhg" id="(0.27956989247311825,0.05102040816326531)"/>
1378 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_g_qAd9eAEeufiOvRR5sVhg" id="(0.45112781954887216,0.0)"/>
1379 </edges>
1380 <edges xmi:type="notation:Edge" xmi:id="_nucMoNeAEeufiOvRR5sVhg" type="4001" element="_nuG1dteAEeufiOvRR5sVhg" source="_mCh54Nd_EeufiOvRR5sVhg" target="_IsM5ENd_EeufiOvRR5sVhg">
1381 <children xmi:type="notation:Node" xmi:id="_nucMpNeAEeufiOvRR5sVhg" type="6001">
1382 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_nucMpdeAEeufiOvRR5sVhg" x="-329" y="-11"/>
1383 </children>
1384 <children xmi:type="notation:Node" xmi:id="_nucMpteAEeufiOvRR5sVhg" type="6002">
1385 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_nucMp9eAEeufiOvRR5sVhg" x="33" y="10"/>
1386 </children>
1387 <children xmi:type="notation:Node" xmi:id="_nucMqNeAEeufiOvRR5sVhg" type="6003">
1388 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_nucMqdeAEeufiOvRR5sVhg" x="-215" y="10"/>
1389 </children>
1390 <styles xmi:type="notation:ConnectorStyle" xmi:id="_nucModeAEeufiOvRR5sVhg" routing="Rectilinear"/>
1391 <styles xmi:type="notation:FontStyle" xmi:id="_nucMoteAEeufiOvRR5sVhg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1392 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_nucMo9eAEeufiOvRR5sVhg" points="[0, 10, -776, 23]$[776, 10, 0, 23]"/>
1393 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_nuczsNeAEeufiOvRR5sVhg" id="(1.0,0.6224489795918368)"/>
1394 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_nuczsdeAEeufiOvRR5sVhg" id="(0.0,0.4897959183673469)"/>
1395 </edges>
1396 <edges xmi:type="notation:Edge" xmi:id="_pMxL0NeAEeufiOvRR5sVhg" type="4001" element="_pMdCzNeAEeufiOvRR5sVhg" source="_dZlRoNeAEeufiOvRR5sVhg" target="_IsM5ENd_EeufiOvRR5sVhg">
1397 <children xmi:type="notation:Node" xmi:id="_pMxL1NeAEeufiOvRR5sVhg" type="6001">
1398 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_pMxL1deAEeufiOvRR5sVhg" x="-109" y="288"/>
1399 </children>
1400 <children xmi:type="notation:Node" xmi:id="_pMxL1teAEeufiOvRR5sVhg" type="6002">
1401 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_pMxL19eAEeufiOvRR5sVhg" x="117" y="10"/>
1402 </children>
1403 <children xmi:type="notation:Node" xmi:id="_pMxL2NeAEeufiOvRR5sVhg" type="6003">
1404 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_pMxL2deAEeufiOvRR5sVhg" x="93" y="25"/>
1405 </children>
1406 <styles xmi:type="notation:ConnectorStyle" xmi:id="_pMxL0deAEeufiOvRR5sVhg" routing="Rectilinear"/>
1407 <styles xmi:type="notation:FontStyle" xmi:id="_pMxL0teAEeufiOvRR5sVhg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1408 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_pMxL09eAEeufiOvRR5sVhg" points="[1, 0, -188, -549]$[1, 31, -188, -518]$[334, 31, 145, -518]$[334, 576, 145, 27]$[271, 576, 82, 27]"/>
1409 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_pMxy4NeAEeufiOvRR5sVhg" id="(0.5,1.0)"/>
1410 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_pMxy4deAEeufiOvRR5sVhg" id="(0.3050847457627119,0.0)"/>
1411 </edges>
1412 <edges xmi:type="notation:Edge" xmi:id="_vRxbMNeAEeufiOvRR5sVhg" type="4001" element="_vRo4VteAEeufiOvRR5sVhg" source="_OWhiINeAEeufiOvRR5sVhg" target="_xsq_MKA8EeuqkpDnuik1sg">
1413 <children xmi:type="notation:Node" xmi:id="_vRxbNNeAEeufiOvRR5sVhg" type="6001">
1414 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vRxbNdeAEeufiOvRR5sVhg" x="-341" y="-535"/>
1415 </children>
1416 <children xmi:type="notation:Node" xmi:id="_vRyCQNeAEeufiOvRR5sVhg" type="6002">
1417 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vRyCQdeAEeufiOvRR5sVhg" x="-43" y="10"/>
1418 </children>
1419 <children xmi:type="notation:Node" xmi:id="_vRyCQteAEeufiOvRR5sVhg" type="6003">
1420 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vRyCQ9eAEeufiOvRR5sVhg" x="43" y="10"/>
1421 </children>
1422 <styles xmi:type="notation:ConnectorStyle" xmi:id="_vRxbMdeAEeufiOvRR5sVhg" routing="Rectilinear"/>
1423 <styles xmi:type="notation:FontStyle" xmi:id="_vRxbMteAEeufiOvRR5sVhg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1424 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_vRxbM9eAEeufiOvRR5sVhg" points="[0, 0, 1241, 564]$[0, -564, 1241, 0]$[-1241, -564, 0, 0]"/>
1425 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_vRyCRNeAEeufiOvRR5sVhg" id="(0.7012987012987013,0.0)"/>
1426 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_vRyCRdeAEeufiOvRR5sVhg" id="(1.0,0.6122448979591837)"/>
1427 </edges>
1428 <edges xmi:type="notation:Edge" xmi:id="_AmNXcNeBEeufiOvRR5sVhg" type="4001" element="_AmBxQNeBEeufiOvRR5sVhg" source="_9Tu6ENeAEeufiOvRR5sVhg" target="_xsq_MKA8EeuqkpDnuik1sg">
1429 <children xmi:type="notation:Node" xmi:id="_AmN-gNeBEeufiOvRR5sVhg" type="6001">
1430 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_AmN-gdeBEeufiOvRR5sVhg" x="-580" y="-139"/>
1431 </children>
1432 <children xmi:type="notation:Node" xmi:id="_AmN-gteBEeufiOvRR5sVhg" type="6002">
1433 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_AmN-g9eBEeufiOvRR5sVhg" x="427" y="10"/>
1434 </children>
1435 <children xmi:type="notation:Node" xmi:id="_AmN-hNeBEeufiOvRR5sVhg" type="6003">
1436 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_AmN-hdeBEeufiOvRR5sVhg" x="46" y="10"/>
1437 </children>
1438 <styles xmi:type="notation:ConnectorStyle" xmi:id="_AmNXcdeBEeufiOvRR5sVhg" routing="Rectilinear"/>
1439 <styles xmi:type="notation:FontStyle" xmi:id="_AmNXcteBEeufiOvRR5sVhg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1440 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_AmNXc9eBEeufiOvRR5sVhg" points="[70, -12, 1316, 155]$[70, -167, 1316, 0]$[-1246, -167, 0, 0]"/>
1441 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_AmN-hteBEeufiOvRR5sVhg" id="(0.0,0.12244897959183673)"/>
1442 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_AmN-h9eBEeufiOvRR5sVhg" id="(1.0,0.3673469387755102)"/>
1443 </edges>
1444 <edges xmi:type="notation:Edge" xmi:id="_DlH_oNeBEeufiOvRR5sVhg" type="4001" element="_DkzPhteBEeufiOvRR5sVhg" source="_9Tu6ENeAEeufiOvRR5sVhg" target="_e73WIKA9EeuqkpDnuik1sg">
1445 <children xmi:type="notation:Node" xmi:id="_DlH_pNeBEeufiOvRR5sVhg" type="6001">
1446 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_DlH_pdeBEeufiOvRR5sVhg" x="-12" y="-10"/>
1447 </children>
1448 <children xmi:type="notation:Node" xmi:id="_DlH_pteBEeufiOvRR5sVhg" type="6002">
1449 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_DlH_p9eBEeufiOvRR5sVhg" x="-12" y="10"/>
1450 </children>
1451 <children xmi:type="notation:Node" xmi:id="_DlH_qNeBEeufiOvRR5sVhg" type="6003">
1452 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_DlH_qdeBEeufiOvRR5sVhg" x="-12" y="10"/>
1453 </children>
1454 <styles xmi:type="notation:ConnectorStyle" xmi:id="_DlH_odeBEeufiOvRR5sVhg" routing="Tree"/>
1455 <styles xmi:type="notation:FontStyle" xmi:id="_DlH_oteBEeufiOvRR5sVhg" fontName="Noto Sans" fontHeight="8"/>
1456 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_DlH_o9eBEeufiOvRR5sVhg" points="[0, -6, 1293, 192]$[0, -42, 1293, 156]$[-1292, -42, 1, 156]$[-1292, -100, 1, 98]"/>
1457 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_DlH_qteBEeufiOvRR5sVhg" id="(0.14084507042253522,0.061224489795918366)"/>
1458 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_DlH_q9eBEeufiOvRR5sVhg" id="(0.5,0.0)"/>
1459 </edges>
1460 <edges xmi:type="notation:Edge" xmi:id="_WiUCYNeBEeufiOvRR5sVhg" type="4001" element="_WiKRoteBEeufiOvRR5sVhg" source="_9Tu6ENeAEeufiOvRR5sVhg" target="_IsM5ENd_EeufiOvRR5sVhg">
1461 <children xmi:type="notation:Node" xmi:id="_WiUCZNeBEeufiOvRR5sVhg" type="6001">
1462 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_WiUCZdeBEeufiOvRR5sVhg" x="-532" y="2"/>
1463 </children>
1464 <children xmi:type="notation:Node" xmi:id="_WiUCZteBEeufiOvRR5sVhg" type="6002">
1465 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_WiUCZ9eBEeufiOvRR5sVhg" x="-7" y="10"/>
1466 </children>
1467 <children xmi:type="notation:Node" xmi:id="_WiUCaNeBEeufiOvRR5sVhg" type="6003">
1468 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_WiUCadeBEeufiOvRR5sVhg" x="-134" y="10"/>
1469 </children>
1470 <styles xmi:type="notation:ConnectorStyle" xmi:id="_WiUCYdeBEeufiOvRR5sVhg" routing="Rectilinear"/>
1471 <styles xmi:type="notation:FontStyle" xmi:id="_WiUCYteBEeufiOvRR5sVhg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1472 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_WiUCY9eBEeufiOvRR5sVhg" points="[39, 0, 115, -982]$[39, 1030, 115, 48]$[-54, 1030, 22, 48]"/>
1473 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_WiUCateBEeufiOvRR5sVhg" id="(0.5070422535211268,1.0)"/>
1474 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_WiUCa9eBEeufiOvRR5sVhg" id="(0.8135593220338984,0.0)"/>
1475 </edges>
1476 <edges xmi:type="notation:Edge" xmi:id="_s7wgoAGyEey7cfH5K6RyCw" type="4001" element="_s68oXAGyEey7cfH5K6RyCw" source="_pdJrwAGyEey7cfH5K6RyCw" target="_e73WIKA9EeuqkpDnuik1sg">
1477 <children xmi:type="notation:Node" xmi:id="_s7xuwAGyEey7cfH5K6RyCw" type="6001">
1478 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_s7xuwQGyEey7cfH5K6RyCw" y="-10"/>
1479 </children>
1480 <children xmi:type="notation:Node" xmi:id="_s7yV0AGyEey7cfH5K6RyCw" type="6002">
1481 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_s7yV0QGyEey7cfH5K6RyCw" y="10"/>
1482 </children>
1483 <children xmi:type="notation:Node" xmi:id="_s7yV0gGyEey7cfH5K6RyCw" type="6003">
1484 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_s7yV0wGyEey7cfH5K6RyCw" y="10"/>
1485 </children>
1486 <styles xmi:type="notation:ConnectorStyle" xmi:id="_s7wgoQGyEey7cfH5K6RyCw" routing="Tree"/>
1487 <styles xmi:type="notation:FontStyle" xmi:id="_s7wgogGyEey7cfH5K6RyCw" fontName="Noto Sans" fontHeight="8"/>
1488 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_s7wgowGyEey7cfH5K6RyCw" points="[0, -9, -684, 192]$[0, -45, -684, 156]$[685, -45, 1, 156]$[685, -103, 1, 98]"/>
1489 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_s71ZIAGyEey7cfH5K6RyCw" id="(0.5310344827586206,0.061224489795918366)"/>
1490 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_s71ZIQGyEey7cfH5K6RyCw" id="(0.5,0.0)"/>
1491 </edges>
1492 <edges xmi:type="notation:Edge" xmi:id="_zKMY0AGyEey7cfH5K6RyCw" type="4001" element="_zJpmRgGyEey7cfH5K6RyCw" source="_pdJrwAGyEey7cfH5K6RyCw" target="_xsq_MKA8EeuqkpDnuik1sg">
1493 <children xmi:type="notation:Node" xmi:id="_zKM_4AGyEey7cfH5K6RyCw" type="6001">
1494 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_zKM_4QGyEey7cfH5K6RyCw" x="-463" y="19"/>
1495 </children>
1496 <children xmi:type="notation:Node" xmi:id="_zKM_4gGyEey7cfH5K6RyCw" type="6002">
1497 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_zKM_4wGyEey7cfH5K6RyCw" x="-91" y="10"/>
1498 </children>
1499 <children xmi:type="notation:Node" xmi:id="_zKM_5AGyEey7cfH5K6RyCw" type="6003">
1500 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_zKM_5QGyEey7cfH5K6RyCw" x="-88" y="10"/>
1501 </children>
1502 <styles xmi:type="notation:ConnectorStyle" xmi:id="_zKMY0QGyEey7cfH5K6RyCw" routing="Rectilinear"/>
1503 <styles xmi:type="notation:FontStyle" xmi:id="_zKMY0gGyEey7cfH5K6RyCw" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1504 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_zKMY0wGyEey7cfH5K6RyCw" points="[-7, -15, -830, 120]$[-7, -63, -830, 72]$[859, -63, 36, 72]$[859, -109, 36, 26]"/>
1505 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_zKNm8AGyEey7cfH5K6RyCw" id="(0.4482758620689655,0.15306122448979592)"/>
1506 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_zKNm8QGyEey7cfH5K6RyCw" id="(0.0,0.7346938775510204)"/>
1507 </edges>
1508 <edges xmi:type="notation:Edge" xmi:id="_WAr2kAGzEey7cfH5K6RyCw" type="4001" element="_WAUqNgGzEey7cfH5K6RyCw" source="_SNlYYAGzEey7cfH5K6RyCw" target="_VikSENeAEeufiOvRR5sVhg">
1509 <children xmi:type="notation:Node" xmi:id="_WAsdoAGzEey7cfH5K6RyCw" type="6001">
1510 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_WAsdoQGzEey7cfH5K6RyCw" x="3" y="-10"/>
1511 </children>
1512 <children xmi:type="notation:Node" xmi:id="_WAsdogGzEey7cfH5K6RyCw" type="6002">
1513 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_WAsdowGzEey7cfH5K6RyCw" x="1" y="10"/>
1514 </children>
1515 <children xmi:type="notation:Node" xmi:id="_WAsdpAGzEey7cfH5K6RyCw" type="6003">
1516 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_WAsdpQGzEey7cfH5K6RyCw" x="-1" y="10"/>
1517 </children>
1518 <styles xmi:type="notation:ConnectorStyle" xmi:id="_WAr2kQGzEey7cfH5K6RyCw" routing="Tree"/>
1519 <styles xmi:type="notation:FontStyle" xmi:id="_WAr2kgGzEey7cfH5K6RyCw" fontName="Noto Sans" fontHeight="8"/>
1520 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_WAr2kwGzEey7cfH5K6RyCw" points="[-1, -7, -43, 132]$[-1, -23, -43, 116]$[41, -23, -1, 116]$[41, -41, -1, 98]"/>
1521 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_WAsdpgGzEey7cfH5K6RyCw" id="(0.25842696629213485,0.07142857142857142)"/>
1522 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_WAsdpwGzEey7cfH5K6RyCw" id="(0.45112781954887216,0.0)"/>
1523 </edges>
1524 <edges xmi:type="notation:Edge" xmi:id="_deCBQCrZEeyyC-O0_LlY9w" type="4001" element="_ddmjcCrZEeyyC-O0_LlY9w" source="_fit3kKA5EeuqkpDnuik1sg" target="_A9YrQCrZEeyyC-O0_LlY9w">
1525 <children xmi:type="notation:Node" xmi:id="_deCoUCrZEeyyC-O0_LlY9w" type="6001">
1526 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_deCoUSrZEeyyC-O0_LlY9w" x="-18" y="-10"/>
1527 </children>
1528 <children xmi:type="notation:Node" xmi:id="_deCoUirZEeyyC-O0_LlY9w" type="6002">
1529 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_deCoUyrZEeyyC-O0_LlY9w" x="5" y="8"/>
1530 </children>
1531 <children xmi:type="notation:Node" xmi:id="_deDPYCrZEeyyC-O0_LlY9w" type="6003">
1532 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_deDPYSrZEeyyC-O0_LlY9w" x="-20" y="-8"/>
1533 </children>
1534 <styles xmi:type="notation:ConnectorStyle" xmi:id="_deCBQSrZEeyyC-O0_LlY9w" routing="Tree"/>
1535 <styles xmi:type="notation:FontStyle" xmi:id="_deCBQirZEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
1536 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_deCBQyrZEeyyC-O0_LlY9w" points="[0, -13, -185, 137]$[0, -49, -185, 101]$[186, -49, 1, 101]$[186, -101, 1, 49]"/>
1537 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_deEdgCrZEeyyC-O0_LlY9w" id="(0.7339205882869473,0.1326530612244898)"/>
1538 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_deEdgSrZEeyyC-O0_LlY9w" id="(0.5,0.5)"/>
1539 </edges>
1540 <edges xmi:type="notation:Edge" xmi:id="_eGyDUCrZEeyyC-O0_LlY9w" type="4001" element="_eGZo7irZEeyyC-O0_LlY9w" source="_N0FQ4CrZEeyyC-O0_LlY9w" target="_A9YrQCrZEeyyC-O0_LlY9w">
1541 <children xmi:type="notation:Node" xmi:id="_eGyDVCrZEeyyC-O0_LlY9w" type="6001">
1542 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_eGyDVSrZEeyyC-O0_LlY9w" x="15" y="-10"/>
1543 </children>
1544 <children xmi:type="notation:Node" xmi:id="_eGyDVirZEeyyC-O0_LlY9w" type="6002">
1545 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_eGyDVyrZEeyyC-O0_LlY9w" x="39" y="-1"/>
1546 </children>
1547 <children xmi:type="notation:Node" xmi:id="_eGyDWCrZEeyyC-O0_LlY9w" type="6003">
1548 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_eGyDWSrZEeyyC-O0_LlY9w" x="6" y="-1"/>
1549 </children>
1550 <styles xmi:type="notation:ConnectorStyle" xmi:id="_eGyDUSrZEeyyC-O0_LlY9w" routing="Tree"/>
1551 <styles xmi:type="notation:FontStyle" xmi:id="_eGyDUirZEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
1552 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_eGyDUyrZEeyyC-O0_LlY9w" points="[-1, 0, 161, 137]$[-1, -66, 161, 71]$[-161, -66, 1, 71]$[-161, -88, 1, 49]"/>
1553 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_eGyDWirZEeyyC-O0_LlY9w" id="(0.3440366972477064,0.0)"/>
1554 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_eGyDWyrZEeyyC-O0_LlY9w" id="(0.5,0.5)"/>
1555 </edges>
1556 <edges xmi:type="notation:Edge" xmi:id="_vfpzoCrZEeyyC-O0_LlY9w" type="4001" element="_vfYG6CrZEeyyC-O0_LlY9w" source="_jzknACrZEeyyC-O0_LlY9w" target="_re7JICrZEeyyC-O0_LlY9w">
1557 <children xmi:type="notation:Node" xmi:id="_vfpzpCrZEeyyC-O0_LlY9w" type="6001">
1558 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vfpzpSrZEeyyC-O0_LlY9w" y="-10"/>
1559 </children>
1560 <children xmi:type="notation:Node" xmi:id="_vfpzpirZEeyyC-O0_LlY9w" type="6002">
1561 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vfpzpyrZEeyyC-O0_LlY9w" y="10"/>
1562 </children>
1563 <children xmi:type="notation:Node" xmi:id="_vfpzqCrZEeyyC-O0_LlY9w" type="6003">
1564 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vfpzqSrZEeyyC-O0_LlY9w" y="10"/>
1565 </children>
1566 <styles xmi:type="notation:ConnectorStyle" xmi:id="_vfpzoSrZEeyyC-O0_LlY9w" routing="Rectilinear"/>
1567 <styles xmi:type="notation:FontStyle" xmi:id="_vfpzoirZEeyyC-O0_LlY9w" fontColor="7490599" fontName="Segoe UI" fontHeight="8"/>
1568 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_vfpzoyrZEeyyC-O0_LlY9w" points="[0, 0, 0, -226]$[0, 226, 0, 0]"/>
1569 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_vfqasCrZEeyyC-O0_LlY9w" id="(0.4915254237288136,1.0)"/>
1570 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_vfqasSrZEeyyC-O0_LlY9w" id="(0.4915254237288136,0.0)"/>
1571 </edges>
1572 <edges xmi:type="notation:Edge" xmi:id="_KrtVQCraEeyyC-O0_LlY9w" type="4001" element="_KrcPniraEeyyC-O0_LlY9w" source="_3aLaMCrZEeyyC-O0_LlY9w" target="_re7JICrZEeyyC-O0_LlY9w">
1573 <children xmi:type="notation:Node" xmi:id="_KrtVRCraEeyyC-O0_LlY9w" type="6001">
1574 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_KrtVRSraEeyyC-O0_LlY9w" x="-11" y="6"/>
1575 </children>
1576 <children xmi:type="notation:Node" xmi:id="_KrtVRiraEeyyC-O0_LlY9w" type="6002">
1577 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_KrtVRyraEeyyC-O0_LlY9w" y="10"/>
1578 </children>
1579 <children xmi:type="notation:Node" xmi:id="_KrtVSCraEeyyC-O0_LlY9w" type="6003">
1580 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_KrtVSSraEeyyC-O0_LlY9w" y="10"/>
1581 </children>
1582 <styles xmi:type="notation:ConnectorStyle" xmi:id="_KrtVQSraEeyyC-O0_LlY9w" routing="Tree"/>
1583 <styles xmi:type="notation:FontStyle" xmi:id="_KrtVQiraEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
1584 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_KrtVQyraEeyyC-O0_LlY9w" points="[-1, -3, -17, 119]$[-1, -46, -17, 76]$[17, -46, 1, 76]$[17, -73, 1, 49]"/>
1585 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_KrtVSiraEeyyC-O0_LlY9w" id="(0.4347727324353179,0.030612244897959183)"/>
1586 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_KrtVSyraEeyyC-O0_LlY9w" id="(0.5,0.5)"/>
1587 </edges>
1588 <edges xmi:type="notation:Edge" xmi:id="_LMhjUCraEeyyC-O0_LlY9w" type="4001" element="_LMS50CraEeyyC-O0_LlY9w" source="_9mWokCrZEeyyC-O0_LlY9w" target="_re7JICrZEeyyC-O0_LlY9w">
1589 <children xmi:type="notation:Node" xmi:id="_LMiKYCraEeyyC-O0_LlY9w" type="6001">
1590 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_LMiKYSraEeyyC-O0_LlY9w" y="-10"/>
1591 </children>
1592 <children xmi:type="notation:Node" xmi:id="_LMiKYiraEeyyC-O0_LlY9w" type="6002">
1593 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_LMiKYyraEeyyC-O0_LlY9w" y="10"/>
1594 </children>
1595 <children xmi:type="notation:Node" xmi:id="_LMiKZCraEeyyC-O0_LlY9w" type="6003">
1596 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_LMiKZSraEeyyC-O0_LlY9w" y="10"/>
1597 </children>
1598 <styles xmi:type="notation:ConnectorStyle" xmi:id="_LMhjUSraEeyyC-O0_LlY9w" routing="Tree"/>
1599 <styles xmi:type="notation:FontStyle" xmi:id="_LMhjUiraEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
1600 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_LMhjUyraEeyyC-O0_LlY9w" points="[0, 0, 195, 70]$[-195, -70, 0, 0]"/>
1601 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_LMiKZiraEeyyC-O0_LlY9w" id="(0.5,0.20408163265306123)"/>
1602 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_LMiKZyraEeyyC-O0_LlY9w" id="(0.4915254237288136,0.0)"/>
1603 </edges>
1604 <edges xmi:type="notation:Edge" xmi:id="_Lf3WECraEeyyC-O0_LlY9w" type="4001" element="_Lfp6vCraEeyyC-O0_LlY9w" source="_Av5zcCraEeyyC-O0_LlY9w" target="_re7JICrZEeyyC-O0_LlY9w">
1605 <children xmi:type="notation:Node" xmi:id="_Lf39ICraEeyyC-O0_LlY9w" type="6001">
1606 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Lf39ISraEeyyC-O0_LlY9w" x="-9" y="-10"/>
1607 </children>
1608 <children xmi:type="notation:Node" xmi:id="_Lf39IiraEeyyC-O0_LlY9w" type="6002">
1609 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Lf39IyraEeyyC-O0_LlY9w" x="-9" y="10"/>
1610 </children>
1611 <children xmi:type="notation:Node" xmi:id="_Lf39JCraEeyyC-O0_LlY9w" type="6003">
1612 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Lf39JSraEeyyC-O0_LlY9w" x="-9" y="10"/>
1613 </children>
1614 <styles xmi:type="notation:ConnectorStyle" xmi:id="_Lf3WESraEeyyC-O0_LlY9w" routing="Tree"/>
1615 <styles xmi:type="notation:FontStyle" xmi:id="_Lf3WEiraEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
1616 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_Lf3WEyraEeyyC-O0_LlY9w" points="[-1, -49, 314, 168]$[-1, -92, 314, 125]$[-313, -92, 2, 125]$[-313, -119, 2, 98]"/>
1617 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_VZY7UCtbEeySS4mYSornnA" id="(0.5,0.5)"/>
1618 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_Lf39JyraEeyyC-O0_LlY9w" id="(0.4915254237288136,0.0)"/>
1619 </edges>
1620 <edges xmi:type="notation:Edge" xmi:id="_NYC_sCraEeyyC-O0_LlY9w" type="4001" element="_NXzvMiraEeyyC-O0_LlY9w" source="_Av5zcCraEeyyC-O0_LlY9w" target="_FmJJoCraEeyyC-O0_LlY9w">
1621 <children xmi:type="notation:Node" xmi:id="_NYC_tCraEeyyC-O0_LlY9w" type="6001">
1622 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_NYC_tSraEeyyC-O0_LlY9w" x="2" y="-14"/>
1623 </children>
1624 <children xmi:type="notation:Node" xmi:id="_NYC_tiraEeyyC-O0_LlY9w" type="6002">
1625 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_NYC_tyraEeyyC-O0_LlY9w" x="-3" y="10"/>
1626 </children>
1627 <children xmi:type="notation:Node" xmi:id="_NYC_uCraEeyyC-O0_LlY9w" type="6003">
1628 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_NYC_uSraEeyyC-O0_LlY9w" x="-16" y="10"/>
1629 </children>
1630 <styles xmi:type="notation:ConnectorStyle" xmi:id="_NYC_sSraEeyyC-O0_LlY9w" routing="Rectilinear"/>
1631 <styles xmi:type="notation:FontStyle" xmi:id="_NYC_siraEeyyC-O0_LlY9w" fontColor="7490599" fontName="Segoe UI" fontHeight="8"/>
1632 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_NYC_syraEeyyC-O0_LlY9w" points="[0, 0, -78, 0]$[78, 0, 0, 0]"/>
1633 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_NYC_uiraEeyyC-O0_LlY9w" id="(1.0,0.4387755102040816)"/>
1634 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_NYC_uyraEeyyC-O0_LlY9w" id="(0.0,0.4387755102040816)"/>
1635 </edges>
1636 <edges xmi:type="notation:Edge" xmi:id="_Y5paoCraEeyyC-O0_LlY9w" type="4001" element="_UJJy0qBDEeuqkpDnuik1sg" source="_XLYiACraEeyyC-O0_LlY9w" target="_6KEUMKA6EeuqkpDnuik1sg">
1637 <children xmi:type="notation:Node" xmi:id="_Y5papCraEeyyC-O0_LlY9w" type="6001">
1638 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Y5papSraEeyyC-O0_LlY9w" x="-11" y="11"/>
1639 </children>
1640 <children xmi:type="notation:Node" xmi:id="_Y5papiraEeyyC-O0_LlY9w" type="6002">
1641 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Y5papyraEeyyC-O0_LlY9w" x="-16" y="4"/>
1642 </children>
1643 <children xmi:type="notation:Node" xmi:id="_Y5paqCraEeyyC-O0_LlY9w" type="6003">
1644 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Y5paqSraEeyyC-O0_LlY9w" x="-72" y="-10"/>
1645 </children>
1646 <styles xmi:type="notation:ConnectorStyle" xmi:id="_Y5paoSraEeyyC-O0_LlY9w" routing="Rectilinear"/>
1647 <styles xmi:type="notation:FontStyle" xmi:id="_Y5paoiraEeyyC-O0_LlY9w" fontColor="7490599" fontName="Segoe UI" fontHeight="8"/>
1648 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_Y5paoyraEeyyC-O0_LlY9w" points="[-26, -19, 116, 36]$[-142, -19, 0, 36]"/>
1649 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_Y5qBsCraEeyyC-O0_LlY9w" id="(0.22033898305084745,0.6836734693877551)"/>
1650 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_Y5qBsSraEeyyC-O0_LlY9w" id="(1.0,0.12244897959183673)"/>
1651 </edges>
1652 <edges xmi:type="notation:Edge" xmi:id="_ZSW1kCraEeyyC-O0_LlY9w" type="4001" element="_9OddBqA7EeuqkpDnuik1sg" source="_XLYiACraEeyyC-O0_LlY9w" target="_4k5GIKA6EeuqkpDnuik1sg">
1653 <children xmi:type="notation:Node" xmi:id="_ZSW1lCraEeyyC-O0_LlY9w" type="6001">
1654 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_ZSW1lSraEeyyC-O0_LlY9w" x="4" y="-10"/>
1655 </children>
1656 <children xmi:type="notation:Node" xmi:id="_ZSW1liraEeyyC-O0_LlY9w" type="6002">
1657 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_ZSW1lyraEeyyC-O0_LlY9w" x="1" y="10"/>
1658 </children>
1659 <children xmi:type="notation:Node" xmi:id="_ZSW1mCraEeyyC-O0_LlY9w" type="6003">
1660 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_ZSW1mSraEeyyC-O0_LlY9w" x="-1" y="10"/>
1661 </children>
1662 <styles xmi:type="notation:ConnectorStyle" xmi:id="_ZSW1kSraEeyyC-O0_LlY9w" routing="Tree"/>
1663 <styles xmi:type="notation:FontStyle" xmi:id="_ZSW1kiraEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
1664 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_ZSW1kyraEeyyC-O0_LlY9w" points="[-1, -9, 100, 156]$[-1, -49, 100, 116]$[-92, -49, 9, 116]$[-92, -67, 9, 98]"/>
1665 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_ZSW1miraEeyyC-O0_LlY9w" id="(0.3305084745762712,0.09183673469387756)"/>
1666 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_ZSW1myraEeyyC-O0_LlY9w" id="(0.4067796610169492,0.0)"/>
1667 </edges>
1668 <edges xmi:type="notation:Edge" xmi:id="_5CQ84CraEeyyC-O0_LlY9w" type="4001" element="_5CBsdCraEeyyC-O0_LlY9w" source="_jjhjYCraEeyyC-O0_LlY9w" target="_XLYiACraEeyyC-O0_LlY9w">
1669 <children xmi:type="notation:Node" xmi:id="_5CQ85CraEeyyC-O0_LlY9w" type="6001">
1670 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_5CQ85SraEeyyC-O0_LlY9w" y="-10"/>
1671 </children>
1672 <children xmi:type="notation:Node" xmi:id="_5CQ85iraEeyyC-O0_LlY9w" type="6002">
1673 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_5CQ85yraEeyyC-O0_LlY9w" y="10"/>
1674 </children>
1675 <children xmi:type="notation:Node" xmi:id="_5CRj8CraEeyyC-O0_LlY9w" type="6003">
1676 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_5CRj8SraEeyyC-O0_LlY9w" y="10"/>
1677 </children>
1678 <styles xmi:type="notation:ConnectorStyle" xmi:id="_5CQ84SraEeyyC-O0_LlY9w" routing="Tree"/>
1679 <styles xmi:type="notation:FontStyle" xmi:id="_5CQ84iraEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
1680 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_5CQ84yraEeyyC-O0_LlY9w" points="[0, 0, -94, 70]$[94, -70, 0, 0]"/>
1681 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_4zEN0CrbEeyyC-O0_LlY9w" id="(0.5,0.5)"/>
1682 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_5CRj8yraEeyyC-O0_LlY9w" id="(0.5,0.5)"/>
1683 </edges>
1684 <edges xmi:type="notation:Edge" xmi:id="_5jTNYCraEeyyC-O0_LlY9w" type="4001" element="_5jFyVCraEeyyC-O0_LlY9w" source="_V6pfMKA7EeuqkpDnuik1sg" target="_XLYiACraEeyyC-O0_LlY9w">
1685 <children xmi:type="notation:Node" xmi:id="_5jTNZCraEeyyC-O0_LlY9w" type="6001">
1686 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_5jTNZSraEeyyC-O0_LlY9w" x="-13" y="-10"/>
1687 </children>
1688 <children xmi:type="notation:Node" xmi:id="_5jTNZiraEeyyC-O0_LlY9w" type="6002">
1689 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_5jTNZyraEeyyC-O0_LlY9w" y="10"/>
1690 </children>
1691 <children xmi:type="notation:Node" xmi:id="_5jTNaCraEeyyC-O0_LlY9w" type="6003">
1692 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_5jTNaSraEeyyC-O0_LlY9w" x="6" y="14"/>
1693 </children>
1694 <styles xmi:type="notation:ConnectorStyle" xmi:id="_5jTNYSraEeyyC-O0_LlY9w" routing="Tree"/>
1695 <styles xmi:type="notation:FontStyle" xmi:id="_5jTNYiraEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
1696 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_5jTNYyraEeyyC-O0_LlY9w" points="[-1, -12, 71, 119]$[-1, -55, 71, 76]$[-71, -55, 1, 76]$[-71, -82, 1, 49]"/>
1697 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_5jTNairaEeyyC-O0_LlY9w" id="(0.5169491525423728,0.12244897959183673)"/>
1698 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_5jTNayraEeyyC-O0_LlY9w" id="(0.5,0.5)"/>
1699 </edges>
1700 <edges xmi:type="notation:Edge" xmi:id="_MiYGgCrbEeyyC-O0_LlY9w" type="4001" element="_MiDWeCrbEeyyC-O0_LlY9w" source="_3aLaMCrZEeyyC-O0_LlY9w" target="_6KEUMKA6EeuqkpDnuik1sg">
1701 <children xmi:type="notation:Node" xmi:id="_MiYtkCrbEeyyC-O0_LlY9w" type="6001">
1702 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_MiYtkSrbEeyyC-O0_LlY9w" x="-93" y="-247"/>
1703 </children>
1704 <children xmi:type="notation:Node" xmi:id="_MiYtkirbEeyyC-O0_LlY9w" type="6002">
1705 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_MiYtkyrbEeyyC-O0_LlY9w" x="-53" y="21"/>
1706 </children>
1707 <children xmi:type="notation:Node" xmi:id="_MiYtlCrbEeyyC-O0_LlY9w" type="6003">
1708 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_MiYtlSrbEeyyC-O0_LlY9w" x="-47" y="10"/>
1709 </children>
1710 <styles xmi:type="notation:ConnectorStyle" xmi:id="_MiYGgSrbEeyyC-O0_LlY9w" routing="Rectilinear"/>
1711 <styles xmi:type="notation:FontStyle" xmi:id="_MiYGgirbEeyyC-O0_LlY9w" fontColor="7490599" fontName="Segoe UI" fontHeight="8"/>
1712 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_MiYGgyrbEeyyC-O0_LlY9w" points="[0, -25, 441, 140]$[-46, -25, 395, 140]$[-46, -261, 395, -96]$[-473, -261, -32, -96]$[-473, -241, -32, -76]"/>
1713 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_MiYtlirbEeyyC-O0_LlY9w" id="(0.0,0.7448979591836735)"/>
1714 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_MiYtlyrbEeyyC-O0_LlY9w" id="(1.0,0.7755102040816326)"/>
1715 </edges>
1716 <edges xmi:type="notation:Edge" xmi:id="_btzH0CrbEeyyC-O0_LlY9w" type="4001" element="_btj3UirbEeyyC-O0_LlY9w" source="_9mWokCrZEeyyC-O0_LlY9w" target="_Kw-vINbNEeuymriYTNxK2g">
1717 <children xmi:type="notation:Node" xmi:id="_btzH1CrbEeyyC-O0_LlY9w" type="6001">
1718 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_btzH1SrbEeyyC-O0_LlY9w" x="-897" y="29"/>
1719 </children>
1720 <children xmi:type="notation:Node" xmi:id="_btzH1irbEeyyC-O0_LlY9w" type="6002">
1721 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_btzH1yrbEeyyC-O0_LlY9w" x="5" y="10"/>
1722 </children>
1723 <children xmi:type="notation:Node" xmi:id="_btzH2CrbEeyyC-O0_LlY9w" type="6003">
1724 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_btzH2SrbEeyyC-O0_LlY9w" x="146" y="106"/>
1725 </children>
1726 <styles xmi:type="notation:ConnectorStyle" xmi:id="_btzH0SrbEeyyC-O0_LlY9w" routing="Rectilinear"/>
1727 <styles xmi:type="notation:FontStyle" xmi:id="_btzH0irbEeyyC-O0_LlY9w" fontColor="7490599" fontName="Segoe UI" fontHeight="8"/>
1728 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_btzH0yrbEeyyC-O0_LlY9w" points="[-71, 0, 794, 1174]$[-71, -1032, 794, 142]$[-883, -1032, -18, 142]$[-883, -1174, -18, 0]"/>
1729 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_btzH2irbEeyyC-O0_LlY9w" id="(0.7627118644067796,0.0)"/>
1730 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_btzH2yrbEeyyC-O0_LlY9w" id="(0.4491525423728814,1.0)"/>
1731 </edges>
1732 <edges xmi:type="notation:Edge" xmi:id="_teMnUCrbEeyyC-O0_LlY9w" type="4001" element="_td_zEirbEeyyC-O0_LlY9w" source="_jjhjYCraEeyyC-O0_LlY9w" target="_q-1B4CrbEeyyC-O0_LlY9w">
1733 <children xmi:type="notation:Node" xmi:id="_teNOYCrbEeyyC-O0_LlY9w" type="6001">
1734 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_teNOYSrbEeyyC-O0_LlY9w" x="-226" y="-13"/>
1735 </children>
1736 <children xmi:type="notation:Node" xmi:id="_teN1cCrbEeyyC-O0_LlY9w" type="6002">
1737 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_teN1cSrbEeyyC-O0_LlY9w" x="84" y="10"/>
1738 </children>
1739 <children xmi:type="notation:Node" xmi:id="_teOcgCrbEeyyC-O0_LlY9w" type="6003">
1740 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_teOcgSrbEeyyC-O0_LlY9w" x="13"/>
1741 </children>
1742 <styles xmi:type="notation:ConnectorStyle" xmi:id="_teMnUSrbEeyyC-O0_LlY9w" routing="Rectilinear"/>
1743 <styles xmi:type="notation:FontStyle" xmi:id="_teMnUirbEeyyC-O0_LlY9w" fontColor="7490599" fontName="Segoe UI" fontHeight="8"/>
1744 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_teMnUyrbEeyyC-O0_LlY9w" points="[-42, 49, -382, -220]$[-42, 83, -382, -186]$[397, 83, 57, -186]$[397, 250, 57, -19]"/>
1745 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_teOcgirbEeyyC-O0_LlY9w" id="(1.0,0.5)"/>
1746 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_teOcgyrbEeyyC-O0_LlY9w" id="(0.0,0.19387755102040816)"/>
1747 </edges>
1748 <edges xmi:type="notation:Edge" xmi:id="_WU57ECrcEeyyC-O0_LlY9w" type="4001" element="_WUsgHCrcEeyyC-O0_LlY9w" source="_N0FQ4CrZEeyyC-O0_LlY9w" target="_RzZA0KA5EeuqkpDnuik1sg">
1749 <children xmi:type="notation:Node" xmi:id="_WU6iICrcEeyyC-O0_LlY9w" type="6001">
1750 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_WU6iISrcEeyyC-O0_LlY9w" y="-10"/>
1751 </children>
1752 <children xmi:type="notation:Node" xmi:id="_WU6iIircEeyyC-O0_LlY9w" type="6002">
1753 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_WU6iIyrcEeyyC-O0_LlY9w" y="10"/>
1754 </children>
1755 <children xmi:type="notation:Node" xmi:id="_WU6iJCrcEeyyC-O0_LlY9w" type="6003">
1756 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_WU6iJSrcEeyyC-O0_LlY9w" y="10"/>
1757 </children>
1758 <styles xmi:type="notation:ConnectorStyle" xmi:id="_WU57ESrcEeyyC-O0_LlY9w" routing="Tree"/>
1759 <styles xmi:type="notation:FontStyle" xmi:id="_WU57EircEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
1760 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_WU57EyrcEeyyC-O0_LlY9w" points="[0, 0, 680, 598]$[-680, -598, 0, 0]"/>
1761 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_WU6iJircEeyyC-O0_LlY9w" id="(0.5504587155963302,0.0)"/>
1762 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_WU6iJyrcEeyyC-O0_LlY9w" id="(0.5,0.5)"/>
1763 </edges>
1764 <edges xmi:type="notation:Edge" xmi:id="_dVu5gCrcEeyyC-O0_LlY9w" type="4001" element="_vDscvKA6EeuqkpDnuik1sg" source="_A9YrQCrZEeyyC-O0_LlY9w" target="_sdPX0KA6EeuqkpDnuik1sg">
1765 <children xmi:type="notation:Node" xmi:id="_dVu5hCrcEeyyC-O0_LlY9w" type="6001">
1766 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_dVu5hSrcEeyyC-O0_LlY9w" x="-134" y="-88"/>
1767 </children>
1768 <children xmi:type="notation:Node" xmi:id="_dVu5hircEeyyC-O0_LlY9w" type="6002">
1769 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_dVu5hyrcEeyyC-O0_LlY9w" x="56" y="-8"/>
1770 </children>
1771 <children xmi:type="notation:Node" xmi:id="_dVu5iCrcEeyyC-O0_LlY9w" type="6003">
1772 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_dVu5iSrcEeyyC-O0_LlY9w" x="-3"/>
1773 </children>
1774 <styles xmi:type="notation:ConnectorStyle" xmi:id="_dVu5gSrcEeyyC-O0_LlY9w" routing="Rectilinear"/>
1775 <styles xmi:type="notation:FontStyle" xmi:id="_dVu5gircEeyyC-O0_LlY9w" fontColor="7490599" fontName="Segoe UI" fontHeight="8"/>
1776 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_dVu5gyrcEeyyC-O0_LlY9w" points="[-38, 49, 189, -328]$[-38, 117, 189, -260]$[-89, 117, 138, -260]$[-89, 427, 138, 50]$[-149, 427, 78, 50]"/>
1777 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_dVu5iyrcEeyyC-O0_LlY9w" id="(0.3389830508474576,0.0)"/>
1778 </edges>
1779 <edges xmi:type="notation:Edge" xmi:id="_vndkcCrcEeyyC-O0_LlY9w" type="4001" element="_Uy4bWaA6EeuqkpDnuik1sg" source="_A9YrQCrZEeyyC-O0_LlY9w" target="_QKLK0KA6EeuqkpDnuik1sg">
1780 <children xmi:type="notation:Node" xmi:id="_vndkdCrcEeyyC-O0_LlY9w" type="6001">
1781 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vndkdSrcEeyyC-O0_LlY9w" x="-224" y="-10"/>
1782 </children>
1783 <children xmi:type="notation:Node" xmi:id="_vndkdircEeyyC-O0_LlY9w" type="6002">
1784 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vndkdyrcEeyyC-O0_LlY9w" x="-10" y="-10"/>
1785 </children>
1786 <children xmi:type="notation:Node" xmi:id="_vndkeCrcEeyyC-O0_LlY9w" type="6003">
1787 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vndkeSrcEeyyC-O0_LlY9w" x="-205" y="75"/>
1788 </children>
1789 <styles xmi:type="notation:ConnectorStyle" xmi:id="_vndkcSrcEeyyC-O0_LlY9w" routing="Rectilinear"/>
1790 <styles xmi:type="notation:FontStyle" xmi:id="_vndkcircEeyyC-O0_LlY9w" fontColor="7490599" fontName="Segoe UI" fontHeight="8"/>
1791 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_vndkcyrcEeyyC-O0_LlY9w" points="[-19, 9, 342, -328]$[-19, 57, 342, -280]$[-79, 57, 282, -280]$[-79, 282, 282, -55]$[-328, 282, 33, -55]$[-328, 337, 33, 0]"/>
1792 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_vndkeircEeyyC-O0_LlY9w" id="(0.2602739726027397,0.9081632653061225)"/>
1793 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_vneLgCrcEeyyC-O0_LlY9w" id="(0.4322033898305085,0.0)"/>
1794 </edges>
1795 <edges xmi:type="notation:Edge" xmi:id="_0K6FACrdEeyyC-O0_LlY9w" type="4001" element="_0KiRnCrdEeyyC-O0_LlY9w" source="_FmJJoCraEeyyC-O0_LlY9w" target="_jP6FkKA6EeuqkpDnuik1sg">
1796 <children xmi:type="notation:Node" xmi:id="_0K6FBCrdEeyyC-O0_LlY9w" type="6001">
1797 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_0K6FBSrdEeyyC-O0_LlY9w" x="76" y="-164"/>
1798 </children>
1799 <children xmi:type="notation:Node" xmi:id="_0K6FBirdEeyyC-O0_LlY9w" type="6002">
1800 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_0K6FByrdEeyyC-O0_LlY9w" x="161" y="191"/>
1801 </children>
1802 <children xmi:type="notation:Node" xmi:id="_0K6FCCrdEeyyC-O0_LlY9w" type="6003">
1803 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_0K6FCSrdEeyyC-O0_LlY9w" x="16" y="10"/>
1804 </children>
1805 <styles xmi:type="notation:ConnectorStyle" xmi:id="_0K6FASrdEeyyC-O0_LlY9w" routing="Rectilinear"/>
1806 <styles xmi:type="notation:FontStyle" xmi:id="_0K6FAirdEeyyC-O0_LlY9w" fontName="Segoe UI" fontHeight="8"/>
1807 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_0K6FAyrdEeyyC-O0_LlY9w" points="[20, 0, 1080, 683]$[20, -62, 1080, 621]$[-345, -62, 715, 621]$[-345, -577, 715, 106]$[-1059, -577, 1, 106]$[-1059, -634, 1, 49]"/>
1808 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_0K6FCirdEeyyC-O0_LlY9w" id="(0.3389830508474576,0.0)"/>
1809 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_0K6FCyrdEeyyC-O0_LlY9w" id="(0.5,0.5)"/>
1810 </edges>
1811 <edges xmi:type="notation:Edge" xmi:id="_s4W8oCtaEeySS4mYSornnA" type="4001" element="_mzziwKA9EeuqkpDnuik1sg" source="_A9YrQCrZEeyyC-O0_LlY9w" target="_e73WIKA9EeuqkpDnuik1sg">
1812 <children xmi:type="notation:Node" xmi:id="_s4dqUCtaEeySS4mYSornnA" type="6001">
1813 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_s4dqUStaEeySS4mYSornnA" y="-10"/>
1814 </children>
1815 <children xmi:type="notation:Node" xmi:id="_s4eRYCtaEeySS4mYSornnA" type="6002">
1816 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_s4eRYStaEeySS4mYSornnA" y="10"/>
1817 </children>
1818 <children xmi:type="notation:Node" xmi:id="_s4ffgCtaEeySS4mYSornnA" type="6003">
1819 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_s4ffgStaEeySS4mYSornnA" y="10"/>
1820 </children>
1821 <styles xmi:type="notation:ConnectorStyle" xmi:id="_s4W8oStaEeySS4mYSornnA" routing="Tree"/>
1822 <styles xmi:type="notation:FontStyle" xmi:id="_s4W8oitaEeySS4mYSornnA" fontName="Segoe UI" fontHeight="8"/>
1823 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_s4W8oytaEeySS4mYSornnA" points="[-1, -49, 819, 198]$[-1, -103, 819, 144]$[-819, -103, 1, 144]$[-819, -149, 1, 98]"/>
1824 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_s4kYACtaEeySS4mYSornnA" id="(0.5,0.0)"/>
1825 </edges>
1826 <edges xmi:type="notation:Edge" xmi:id="_-iZVcCtaEeySS4mYSornnA" type="4001" element="_-hzfnCtaEeySS4mYSornnA" source="_N0FQ4CrZEeyyC-O0_LlY9w" target="_jzknACrZEeyyC-O0_LlY9w">
1827 <children xmi:type="notation:Node" xmi:id="_-iZ8gCtaEeySS4mYSornnA" type="6001">
1828 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_-iZ8gStaEeySS4mYSornnA" x="-43" y="-36"/>
1829 </children>
1830 <children xmi:type="notation:Node" xmi:id="_-iZ8gitaEeySS4mYSornnA" type="6002">
1831 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_-iZ8gytaEeySS4mYSornnA" y="10"/>
1832 </children>
1833 <children xmi:type="notation:Node" xmi:id="_-iZ8hCtaEeySS4mYSornnA" type="6003">
1834 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_-iZ8hStaEeySS4mYSornnA" y="10"/>
1835 </children>
1836 <styles xmi:type="notation:ConnectorStyle" xmi:id="_-iZVcStaEeySS4mYSornnA" routing="Rectilinear"/>
1837 <styles xmi:type="notation:FontStyle" xmi:id="_-iZVcitaEeySS4mYSornnA" fontColor="7490599" fontName="Segoe UI" fontHeight="8"/>
1838 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_-iZVcytaEeySS4mYSornnA" points="[0, 0, 0, -142]$[0, 142, 0, 0]"/>
1839 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_-iZ8hitaEeySS4mYSornnA" id="(0.4461538461538462,1.0)"/>
1840 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_-iZ8hytaEeySS4mYSornnA" id="(0.4915254237288136,0.0)"/>
1841 </edges>
1842 </data>
1843 </ownedAnnotationEntries>
1844 <ownedAnnotationEntries xmi:type="description:AnnotationEntry" uid="_Csiy8KA4EeuqkpDnuik1sg" source="DANNOTATION_CUSTOMIZATION_KEY">
1845 <data xmi:type="diagram:ComputedStyleDescriptionRegistry" uid="_Csiy8aA4EeuqkpDnuik1sg">
1846 <computedStyleDescriptions xmi:type="style:EdgeStyleDescription" xmi:id="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan">
1847 <strokeColor xmi:type="description:SystemColor" href="environment:/viewpoint#//@systemColors/@entries[name='black']"/>
1848 <centerLabelStyleDescription xmi:type="style:CenterLabelStyleDescription" xmi:id="_L-JhMaA4EeuqkpDnuik1sg" showIcon="false" labelExpression="service:render">
1849 <labelColor xmi:type="description:SystemColor" href="environment:/viewpoint#//@systemColors/@entries[name='black']"/>
1850 </centerLabelStyleDescription>
1851 <endLabelStyleDescription xmi:type="style:EndLabelStyleDescription" xmi:id="_L-JhMqA4EeuqkpDnuik1sg" labelSize="6" showIcon="false" labelExpression="service:eKeysLabel">
1852 <labelColor xmi:type="description:SystemColor" href="environment:/viewpoint#//@systemColors/@entries[name='dark_blue']"/>
1853 </endLabelStyleDescription>
1854 </computedStyleDescriptions>
1855 </data>
1856 </ownedAnnotationEntries>
1857 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_D05iIKA4EeuqkpDnuik1sg" name="Problem" tooltipText="" outgoingEdges="_4eU5TqA8EeuqkpDnuik1sg _hU64ZqA9EeuqkpDnuik1sg _m-6OTNXtEeuF_d0WEhR3Xw" width="12" height="10">
1858 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Problem"/>
1859 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Problem"/>
1860 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
1861 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
1862 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
1863 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_D06wQKA4EeuqkpDnuik1sg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
1864 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
1865 </ownedStyle>
1866 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
1867 </ownedDiagramElements>
1868 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_EfNqcKA4EeuqkpDnuik1sg" name="Relation" tooltipText="" outgoingEdges="_XWbypqA5EeuqkpDnuik1sg" incomingEdges="_ODPlcKA4EeuqkpDnuik1sg _gRoBcKA4EeuqkpDnuik1sg _rUgDkKA5EeuqkpDnuik1sg _VjwMzqA8EeuqkpDnuik1sg _w-iG36A8EeuqkpDnuik1sg _Z7FrQKA6EeuqkpDnuik1sg _ufJ3IKA4EeuqkpDnuik1sg _VtPctqA4EeuqkpDnuik1sg _UwbfHtbGEeuymriYTNxK2g" width="12" height="10">
1869 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Relation"/>
1870 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Relation"/>
1871 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
1872 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
1873 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
1874 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_Hjy1waA4EeuqkpDnuik1sg" iconPath="/org.eclipse.emf.ecoretools.design/icons/full/obj16/EClass_abstract.gif" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="228,228,228">
1875 <labelFormat>italic</labelFormat>
1876 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@conditionnalStyles.1/@style"/>
1877 </ownedStyle>
1878 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
1879 </ownedDiagramElements>
1880 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_JTstIKA4EeuqkpDnuik1sg" name="ClassDeclaration" tooltipText="" outgoingEdges="_ODPlcKA4EeuqkpDnuik1sg _VtPctqA4EeuqkpDnuik1sg _jk6-PKA4EeuqkpDnuik1sg _plK3JqA9EeuqkpDnuik1sg _-XLKltawEeuymriYTNxK2g" incomingEdges="_2kWqbKA9EeuqkpDnuik1sg" width="12" height="10">
1881 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ClassDeclaration"/>
1882 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ClassDeclaration"/>
1883 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
1884 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
1885 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
1886 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_T_dFNaA4EeuqkpDnuik1sg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
1887 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
1888 </ownedStyle>
1889 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
1890 <ownedElements xmi:type="diagram:DNodeListElement" uid="_SrSuoKA4EeuqkpDnuik1sg" name="abstract : EBoolean = false" tooltipText="">
1891 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//ClassDeclaration/abstract"/>
1892 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//ClassDeclaration/abstract"/>
1893 <ownedStyle xmi:type="diagram:BundledImage" uid="_T_eTUaA4EeuqkpDnuik1sg" labelAlignment="LEFT">
1894 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
1895 </ownedStyle>
1896 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
1897 </ownedElements>
1898 </ownedDiagramElements>
1899 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_ODPlcKA4EeuqkpDnuik1sg" sourceNode="_JTstIKA4EeuqkpDnuik1sg" targetNode="_EfNqcKA4EeuqkpDnuik1sg">
1900 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ClassDeclaration"/>
1901 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ClassDeclaration"/>
1902 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_T_fhcKA4EeuqkpDnuik1sg" targetArrow="InputClosedArrow" routingStyle="tree">
1903 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
1904 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_T_fhcaA4EeuqkpDnuik1sg" showIcon="false">
1905 <labelFormat>italic</labelFormat>
1906 </beginLabelStyle>
1907 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_T_fhcqA4EeuqkpDnuik1sg" showIcon="false"/>
1908 </ownedStyle>
1909 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
1910 </ownedDiagramElements>
1911 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_VtPctqA4EeuqkpDnuik1sg" name="[0..*] superTypes" sourceNode="_JTstIKA4EeuqkpDnuik1sg" targetNode="_EfNqcKA4EeuqkpDnuik1sg">
1912 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ClassDeclaration/superTypes"/>
1913 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ClassDeclaration/superTypes"/>
1914 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_VtQDwKA4EeuqkpDnuik1sg" routingStyle="manhattan" strokeColor="0,0,0">
1915 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']/@style"/>
1916 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_VtQDwqA4EeuqkpDnuik1sg" showIcon="false">
1917 <customFeatures>labelSize</customFeatures>
1918 </centerLabelStyle>
1919 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_VtQDwaA4EeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
1920 <customFeatures>labelSize</customFeatures>
1921 </endLabelStyle>
1922 </ownedStyle>
1923 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
1924 </ownedDiagramElements>
1925 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_c-A7oKA4EeuqkpDnuik1sg" name="ReferenceDeclaration" tooltipText="" outgoingEdges="_gRoBcKA4EeuqkpDnuik1sg _ufJ3IKA4EeuqkpDnuik1sg _0V3L1qA4EeuqkpDnuik1sg _p1JWcqBJEeuqkpDnuik1sg" incomingEdges="_jk6-PKA4EeuqkpDnuik1sg _0V3L1qA4EeuqkpDnuik1sg" width="12" height="10">
1926 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ReferenceDeclaration"/>
1927 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ReferenceDeclaration"/>
1928 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
1929 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
1930 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
1931 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_IMjpcqA5EeuqkpDnuik1sg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
1932 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
1933 </ownedStyle>
1934 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
1935 <ownedElements xmi:type="diagram:DNodeListElement" uid="_HjDZcKA5EeuqkpDnuik1sg" name="containment : EBoolean = false" tooltipText="">
1936 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//ReferenceDeclaration/containment"/>
1937 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//ReferenceDeclaration/containment"/>
1938 <ownedStyle xmi:type="diagram:BundledImage" uid="_IMmswaA5EeuqkpDnuik1sg" labelAlignment="LEFT">
1939 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
1940 </ownedStyle>
1941 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
1942 </ownedElements>
1943 </ownedDiagramElements>
1944 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_gRoBcKA4EeuqkpDnuik1sg" sourceNode="_c-A7oKA4EeuqkpDnuik1sg" targetNode="_EfNqcKA4EeuqkpDnuik1sg">
1945 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ReferenceDeclaration"/>
1946 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ReferenceDeclaration"/>
1947 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_IMsMUKA5EeuqkpDnuik1sg" targetArrow="InputClosedArrow" routingStyle="tree">
1948 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
1949 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_IMsMUaA5EeuqkpDnuik1sg" showIcon="false">
1950 <labelFormat>italic</labelFormat>
1951 </beginLabelStyle>
1952 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_IMsMUqA5EeuqkpDnuik1sg" showIcon="false"/>
1953 </ownedStyle>
1954 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
1955 </ownedDiagramElements>
1956 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_jk6-PKA4EeuqkpDnuik1sg" name="[0..*] referenceDeclarations" sourceNode="_JTstIKA4EeuqkpDnuik1sg" targetNode="_c-A7oKA4EeuqkpDnuik1sg">
1957 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ClassDeclaration/referenceDeclarations"/>
1958 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ClassDeclaration/referenceDeclarations"/>
1959 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_jk7lQKA4EeuqkpDnuik1sg" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
1960 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_jk7lQqA4EeuqkpDnuik1sg" showIcon="false">
1961 <customFeatures>labelSize</customFeatures>
1962 </centerLabelStyle>
1963 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_jk7lQaA4EeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
1964 <customFeatures>labelSize</customFeatures>
1965 </endLabelStyle>
1966 </ownedStyle>
1967 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
1968 </ownedDiagramElements>
1969 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_ufJ3IKA4EeuqkpDnuik1sg" name="[0..1] referenceType" sourceNode="_c-A7oKA4EeuqkpDnuik1sg" targetNode="_EfNqcKA4EeuqkpDnuik1sg">
1970 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ReferenceDeclaration/referenceType"/>
1971 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ReferenceDeclaration/referenceType"/>
1972 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_ufLFQKA4EeuqkpDnuik1sg" routingStyle="manhattan" strokeColor="0,0,0">
1973 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']/@style"/>
1974 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_ufLFQqA4EeuqkpDnuik1sg" showIcon="false">
1975 <customFeatures>labelSize</customFeatures>
1976 </centerLabelStyle>
1977 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_ufLFQaA4EeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
1978 <customFeatures>labelSize</customFeatures>
1979 </endLabelStyle>
1980 </ownedStyle>
1981 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
1982 </ownedDiagramElements>
1983 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_0V3L1qA4EeuqkpDnuik1sg" name="[0..1] opposite" sourceNode="_c-A7oKA4EeuqkpDnuik1sg" targetNode="_c-A7oKA4EeuqkpDnuik1sg">
1984 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ReferenceDeclaration/opposite"/>
1985 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ReferenceDeclaration/opposite"/>
1986 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_0V3y4KA4EeuqkpDnuik1sg" routingStyle="manhattan" strokeColor="0,0,0">
1987 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']/@style"/>
1988 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_0V3y4qA4EeuqkpDnuik1sg" showIcon="false">
1989 <customFeatures>labelSize</customFeatures>
1990 </centerLabelStyle>
1991 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_0V3y4aA4EeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
1992 <customFeatures>labelSize</customFeatures>
1993 </endLabelStyle>
1994 </ownedStyle>
1995 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
1996 </ownedDiagramElements>
1997 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_RzK-YKA5EeuqkpDnuik1sg" name="NamedElement" tooltipText="" incomingEdges="_XWbypqA5EeuqkpDnuik1sg _m-6OTNXtEeuF_d0WEhR3Xw _Smi9eNbNEeuymriYTNxK2g _WUsgHCrcEeyyC-O0_LlY9w" width="12" height="10">
1998 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NamedElement"/>
1999 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NamedElement"/>
2000 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2001 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2002 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2003 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_TYmrIaA5EeuqkpDnuik1sg" iconPath="/org.eclipse.emf.ecoretools.design/icons/full/obj16/EClass_abstract.gif" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="228,228,228">
2004 <labelFormat>italic</labelFormat>
2005 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@conditionnalStyles.1/@style"/>
2006 </ownedStyle>
2007 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2008 <ownedElements xmi:type="diagram:DNodeListElement" uid="_S-lBkKA5EeuqkpDnuik1sg" name="name : EString" tooltipText="">
2009 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//NamedElement/name"/>
2010 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//NamedElement/name"/>
2011 <ownedStyle xmi:type="diagram:BundledImage" uid="_TYqVgaA5EeuqkpDnuik1sg" labelAlignment="LEFT">
2012 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
2013 </ownedStyle>
2014 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
2015 </ownedElements>
2016 </ownedDiagramElements>
2017 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_XWbypqA5EeuqkpDnuik1sg" sourceNode="_EfNqcKA4EeuqkpDnuik1sg" targetNode="_RzK-YKA5EeuqkpDnuik1sg">
2018 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Relation"/>
2019 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Relation"/>
2020 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_XWbyp6A5EeuqkpDnuik1sg" targetArrow="InputClosedArrow" routingStyle="tree">
2021 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2022 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_XWbyqKA5EeuqkpDnuik1sg" showIcon="false">
2023 <labelFormat>italic</labelFormat>
2024 </beginLabelStyle>
2025 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_XWbyqaA5EeuqkpDnuik1sg" showIcon="false"/>
2026 </ownedStyle>
2027 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2028 </ownedDiagramElements>
2029 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_fihqUKA5EeuqkpDnuik1sg" name="PredicateDefinition" tooltipText="" outgoingEdges="_rUgDkKA5EeuqkpDnuik1sg _ddmjcCrZEeyyC-O0_LlY9w" width="12" height="10">
2030 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//PredicateDefinition"/>
2031 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//PredicateDefinition"/>
2032 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2033 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2034 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2035 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_-2UUSyrcEeyyC-O0_LlY9w" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2036 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2037 </ownedStyle>
2038 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2039 <ownedElements xmi:type="diagram:DNodeListElement" uid="_svs2QKA5EeuqkpDnuik1sg" name="error : EBoolean = false" tooltipText="">
2040 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//PredicateDefinition/error"/>
2041 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//PredicateDefinition/error"/>
2042 <ownedStyle xmi:type="diagram:BundledImage" uid="_tWLAYaA5EeuqkpDnuik1sg" labelAlignment="LEFT">
2043 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
2044 </ownedStyle>
2045 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
2046 </ownedElements>
2047 <ownedElements xmi:type="diagram:DNodeListElement" uid="_7sQzECrcEeyyC-O0_LlY9w" name="kind : PredicateKind = PARTIAL" tooltipText="">
2048 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//PredicateDefinition/kind"/>
2049 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//PredicateDefinition/kind"/>
2050 <ownedStyle xmi:type="diagram:BundledImage" uid="_-2ViayrcEeyyC-O0_LlY9w" labelAlignment="LEFT">
2051 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
2052 </ownedStyle>
2053 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
2054 </ownedElements>
2055 </ownedDiagramElements>
2056 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_rUgDkKA5EeuqkpDnuik1sg" sourceNode="_fihqUKA5EeuqkpDnuik1sg" targetNode="_EfNqcKA4EeuqkpDnuik1sg">
2057 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//PredicateDefinition"/>
2058 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//PredicateDefinition"/>
2059 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_-2aa_ircEeyyC-O0_LlY9w" targetArrow="InputClosedArrow" routingStyle="tree">
2060 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2061 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_-2aa_yrcEeyyC-O0_LlY9w" showIcon="false">
2062 <labelFormat>italic</labelFormat>
2063 </beginLabelStyle>
2064 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_-2abACrcEeyyC-O0_LlY9w" showIcon="false"/>
2065 </ownedStyle>
2066 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2067 </ownedDiagramElements>
2068 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_QKD2EKA6EeuqkpDnuik1sg" name="Parameter" tooltipText="" outgoingEdges="_Z7FrQKA6EeuqkpDnuik1sg _oni4rKA6EeuqkpDnuik1sg" incomingEdges="_Uy4bWaA6EeuqkpDnuik1sg" width="12" height="10">
2069 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Parameter"/>
2070 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Parameter"/>
2071 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2072 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2073 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2074 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_QKD2EaA6EeuqkpDnuik1sg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2075 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2076 </ownedStyle>
2077 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2078 </ownedDiagramElements>
2079 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_Uy4bWaA6EeuqkpDnuik1sg" name="[0..*] parameters" sourceNode="_A8hIkCrZEeyyC-O0_LlY9w" targetNode="_QKD2EKA6EeuqkpDnuik1sg">
2080 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ParametricDefinition/parameters"/>
2081 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ParametricDefinition/parameters"/>
2082 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_Uy5pcKA6EeuqkpDnuik1sg" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
2083 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_Uy5pcqA6EeuqkpDnuik1sg" showIcon="false">
2084 <customFeatures>labelSize</customFeatures>
2085 </centerLabelStyle>
2086 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_Uy5pcaA6EeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
2087 <customFeatures>labelSize</customFeatures>
2088 </endLabelStyle>
2089 </ownedStyle>
2090 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2091 </ownedDiagramElements>
2092 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_Z7FrQKA6EeuqkpDnuik1sg" name="[0..1] parameterType" sourceNode="_QKD2EKA6EeuqkpDnuik1sg" targetNode="_EfNqcKA4EeuqkpDnuik1sg">
2093 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Parameter/parameterType"/>
2094 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Parameter/parameterType"/>
2095 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_Z7GSUKA6EeuqkpDnuik1sg" routingStyle="manhattan" strokeColor="0,0,0">
2096 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']/@style"/>
2097 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_Z7GSUqA6EeuqkpDnuik1sg" showIcon="false">
2098 <customFeatures>labelSize</customFeatures>
2099 </centerLabelStyle>
2100 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_Z7GSUaA6EeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
2101 <customFeatures>labelSize</customFeatures>
2102 </endLabelStyle>
2103 </ownedStyle>
2104 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2105 </ownedDiagramElements>
2106 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_jPpm4KA6EeuqkpDnuik1sg" name="Variable" tooltipText="" outgoingEdges="_Tt9RRtbNEeuymriYTNxK2g" incomingEdges="_oni4rKA6EeuqkpDnuik1sg _BVyUAKA7EeuqkpDnuik1sg _0KiRnCrdEeyyC-O0_LlY9w" width="12" height="10">
2107 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Variable"/>
2108 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Variable"/>
2109 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2110 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2111 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2112 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_n6Wbd6A6EeuqkpDnuik1sg" iconPath="/org.eclipse.emf.ecoretools.design/icons/full/obj16/EClass_abstract.gif" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="228,228,228">
2113 <labelFormat>italic</labelFormat>
2114 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@conditionnalStyles.1/@style"/>
2115 </ownedStyle>
2116 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2117 </ownedDiagramElements>
2118 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_oni4rKA6EeuqkpDnuik1sg" sourceNode="_QKD2EKA6EeuqkpDnuik1sg" targetNode="_jPpm4KA6EeuqkpDnuik1sg">
2119 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Parameter"/>
2120 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Parameter"/>
2121 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_onjfsKA6EeuqkpDnuik1sg" targetArrow="InputClosedArrow" routingStyle="tree">
2122 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2123 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_onjfsaA6EeuqkpDnuik1sg" showIcon="false">
2124 <labelFormat>italic</labelFormat>
2125 </beginLabelStyle>
2126 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_onjfsqA6EeuqkpDnuik1sg" showIcon="false"/>
2127 </ownedStyle>
2128 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2129 </ownedDiagramElements>
2130 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_sc_gMKA6EeuqkpDnuik1sg" name="Conjunction" tooltipText="" outgoingEdges="_SykT9qA7EeuqkpDnuik1sg _vWBGaKA7EeuqkpDnuik1sg" incomingEdges="_vDscvKA6EeuqkpDnuik1sg" width="12" height="10">
2131 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Conjunction"/>
2132 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Conjunction"/>
2133 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2134 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2135 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2136 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_sdAHQKA6EeuqkpDnuik1sg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2137 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2138 </ownedStyle>
2139 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2140 </ownedDiagramElements>
2141 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_vDscvKA6EeuqkpDnuik1sg" name="[0..*] bodies" sourceNode="_A8hIkCrZEeyyC-O0_LlY9w" targetNode="_sc_gMKA6EeuqkpDnuik1sg">
2142 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ParametricDefinition/bodies"/>
2143 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ParametricDefinition/bodies"/>
2144 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_vDtDwKA6EeuqkpDnuik1sg" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
2145 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_vDtDwqA6EeuqkpDnuik1sg" showIcon="false">
2146 <customFeatures>labelSize</customFeatures>
2147 </centerLabelStyle>
2148 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_vDtDwaA6EeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
2149 <customFeatures>labelSize</customFeatures>
2150 </endLabelStyle>
2151 </ownedStyle>
2152 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2153 </ownedDiagramElements>
2154 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_4k00sKA6EeuqkpDnuik1sg" name="Literal" tooltipText="" incomingEdges="_SykT9qA7EeuqkpDnuik1sg _9OddBqA7EeuqkpDnuik1sg _D9lnQKA8EeuqkpDnuik1sg" width="12" height="10">
2155 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Literal"/>
2156 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Literal"/>
2157 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2158 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2159 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2160 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_W9SoAqA7EeuqkpDnuik1sg" iconPath="/org.eclipse.emf.ecoretools.design/icons/full/obj16/EClass_abstract.gif" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="228,228,228">
2161 <labelFormat>italic</labelFormat>
2162 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@conditionnalStyles.1/@style"/>
2163 </ownedStyle>
2164 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2165 </ownedDiagramElements>
2166 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_6J_bsKA6EeuqkpDnuik1sg" name="Atom" tooltipText="" outgoingEdges="_D9lnQKA8EeuqkpDnuik1sg _VjwMzqA8EeuqkpDnuik1sg _KmxbkNYPEeuF_d0WEhR3Xw" incomingEdges="_UJJy0qBDEeuqkpDnuik1sg _MiDWeCrbEeyyC-O0_LlY9w" width="12" height="10">
2167 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Atom"/>
2168 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Atom"/>
2169 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2170 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2171 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2172 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_Uy6TtqA-EeuqkpDnuik1sg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2173 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2174 </ownedStyle>
2175 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2176 <ownedElements xmi:type="diagram:DNodeListElement" uid="_UAle4KA-EeuqkpDnuik1sg" name="transitiveClosure : EBoolean = false" tooltipText="">
2177 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//Atom/transitiveClosure"/>
2178 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//Atom/transitiveClosure"/>
2179 <ownedStyle xmi:type="diagram:BundledImage" uid="_Uy9XAaA-EeuqkpDnuik1sg" labelAlignment="LEFT">
2180 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
2181 </ownedStyle>
2182 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
2183 </ownedElements>
2184 </ownedDiagramElements>
2185 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_-O6CoKA6EeuqkpDnuik1sg" name="ImplicitVariable" tooltipText="" outgoingEdges="_BVyUAKA7EeuqkpDnuik1sg" incomingEdges="_0UtN5qA7EeuqkpDnuik1sg _bx-jYtYPEeuF_d0WEhR3Xw" width="12" height="10">
2186 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ImplicitVariable"/>
2187 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ImplicitVariable"/>
2188 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2189 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2190 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2191 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_-O6CoaA6EeuqkpDnuik1sg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2192 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2193 </ownedStyle>
2194 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2195 </ownedDiagramElements>
2196 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_BVyUAKA7EeuqkpDnuik1sg" sourceNode="_-O6CoKA6EeuqkpDnuik1sg" targetNode="_jPpm4KA6EeuqkpDnuik1sg">
2197 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ImplicitVariable"/>
2198 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ImplicitVariable"/>
2199 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_BVyUAaA7EeuqkpDnuik1sg" targetArrow="InputClosedArrow" routingStyle="tree">
2200 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2201 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_BVyUAqA7EeuqkpDnuik1sg" showIcon="false">
2202 <labelFormat>italic</labelFormat>
2203 </beginLabelStyle>
2204 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_BVyUA6A7EeuqkpDnuik1sg" showIcon="false"/>
2205 </ownedStyle>
2206 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2207 </ownedDiagramElements>
2208 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_SykT9qA7EeuqkpDnuik1sg" name="[0..*] literals" sourceNode="_sc_gMKA6EeuqkpDnuik1sg" targetNode="_4k00sKA6EeuqkpDnuik1sg">
2209 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Conjunction/literals"/>
2210 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Conjunction/literals"/>
2211 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_Syk7AKA7EeuqkpDnuik1sg" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
2212 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_Syk7AqA7EeuqkpDnuik1sg" showIcon="false">
2213 <customFeatures>labelSize</customFeatures>
2214 </centerLabelStyle>
2215 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_Syk7AaA7EeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
2216 <customFeatures>labelSize</customFeatures>
2217 </endLabelStyle>
2218 </ownedStyle>
2219 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2220 </ownedDiagramElements>
2221 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_V6YZcKA7EeuqkpDnuik1sg" name="NegativeLiteral" tooltipText="" outgoingEdges="_yCjlHKA7EeuqkpDnuik1sg _5jFyVCraEeyyC-O0_LlY9w" width="12" height="10">
2222 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NegativeLiteral"/>
2223 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NegativeLiteral"/>
2224 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2225 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2226 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2227 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_V6ZAgKA7EeuqkpDnuik1sg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2228 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2229 </ownedStyle>
2230 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2231 </ownedDiagramElements>
2232 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_rRcdoKA7EeuqkpDnuik1sg" name="ExistentialQuantifier" tooltipText="" outgoingEdges="_0UtN5qA7EeuqkpDnuik1sg" incomingEdges="_vWBGaKA7EeuqkpDnuik1sg _yCjlHKA7EeuqkpDnuik1sg" width="12" height="10">
2233 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ExistentialQuantifier"/>
2234 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ExistentialQuantifier"/>
2235 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2236 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2237 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2238 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_-1RCnqA7EeuqkpDnuik1sg" iconPath="/org.eclipse.emf.ecoretools.design/icons/full/obj16/EClass_interface.gif" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="228,228,228">
2239 <labelFormat>italic</labelFormat>
2240 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@conditionnalStyles.0/@style"/>
2241 </ownedStyle>
2242 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2243 </ownedDiagramElements>
2244 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_vWBGaKA7EeuqkpDnuik1sg" sourceNode="_sc_gMKA6EeuqkpDnuik1sg" targetNode="_rRcdoKA7EeuqkpDnuik1sg">
2245 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Conjunction"/>
2246 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Conjunction"/>
2247 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_-1Te06A7EeuqkpDnuik1sg" lineStyle="dash" targetArrow="InputClosedArrow" routingStyle="tree">
2248 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@conditionnalStyles.0/@style"/>
2249 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_-1Te1KA7EeuqkpDnuik1sg" showIcon="false">
2250 <labelFormat>italic</labelFormat>
2251 </beginLabelStyle>
2252 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_-1Te1aA7EeuqkpDnuik1sg" showIcon="false"/>
2253 </ownedStyle>
2254 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2255 </ownedDiagramElements>
2256 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_yCjlHKA7EeuqkpDnuik1sg" sourceNode="_V6YZcKA7EeuqkpDnuik1sg" targetNode="_rRcdoKA7EeuqkpDnuik1sg">
2257 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NegativeLiteral"/>
2258 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NegativeLiteral"/>
2259 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_-1Te36A7EeuqkpDnuik1sg" lineStyle="dash" targetArrow="InputClosedArrow" routingStyle="tree">
2260 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@conditionnalStyles.0/@style"/>
2261 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_-1Te4KA7EeuqkpDnuik1sg" showIcon="false">
2262 <labelFormat>italic</labelFormat>
2263 </beginLabelStyle>
2264 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_-1Te4aA7EeuqkpDnuik1sg" showIcon="false"/>
2265 </ownedStyle>
2266 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2267 </ownedDiagramElements>
2268 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_0UtN5qA7EeuqkpDnuik1sg" name="[0..*] implicitVariables" sourceNode="_rRcdoKA7EeuqkpDnuik1sg" targetNode="_-O6CoKA6EeuqkpDnuik1sg">
2269 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ExistentialQuantifier/implicitVariables"/>
2270 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ExistentialQuantifier/implicitVariables"/>
2271 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_0UucAKA7EeuqkpDnuik1sg" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
2272 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_0UucAqA7EeuqkpDnuik1sg" showIcon="false">
2273 <customFeatures>labelSize</customFeatures>
2274 </centerLabelStyle>
2275 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_0UucAaA7EeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
2276 <customFeatures>labelSize</customFeatures>
2277 </endLabelStyle>
2278 </ownedStyle>
2279 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2280 </ownedDiagramElements>
2281 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_9OddBqA7EeuqkpDnuik1sg" sourceNode="_XLJ4gCraEeyyC-O0_LlY9w" targetNode="_4k00sKA6EeuqkpDnuik1sg">
2282 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//CompoundLiteral"/>
2283 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//CompoundLiteral"/>
2284 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_9OeD4KA7EeuqkpDnuik1sg" targetArrow="InputClosedArrow" routingStyle="tree">
2285 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2286 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_9OeD4aA7EeuqkpDnuik1sg" showIcon="false">
2287 <labelFormat>italic</labelFormat>
2288 </beginLabelStyle>
2289 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_9OeD4qA7EeuqkpDnuik1sg" showIcon="false"/>
2290 </ownedStyle>
2291 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2292 </ownedDiagramElements>
2293 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_D9lnQKA8EeuqkpDnuik1sg" sourceNode="_6J_bsKA6EeuqkpDnuik1sg" targetNode="_4k00sKA6EeuqkpDnuik1sg">
2294 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Atom"/>
2295 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Atom"/>
2296 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_UzDduKA-EeuqkpDnuik1sg" targetArrow="InputClosedArrow" routingStyle="tree">
2297 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2298 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_UzDduaA-EeuqkpDnuik1sg" showIcon="false">
2299 <labelFormat>italic</labelFormat>
2300 </beginLabelStyle>
2301 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_UzDduqA-EeuqkpDnuik1sg" showIcon="false"/>
2302 </ownedStyle>
2303 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2304 </ownedDiagramElements>
2305 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_VjwMzqA8EeuqkpDnuik1sg" name="[0..1] relation" sourceNode="_6J_bsKA6EeuqkpDnuik1sg" targetNode="_EfNqcKA4EeuqkpDnuik1sg">
2306 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Atom/relation"/>
2307 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Atom/relation"/>
2308 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_VjwMz6A8EeuqkpDnuik1sg" routingStyle="manhattan" strokeColor="0,0,0">
2309 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']/@style"/>
2310 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_VjwM0aA8EeuqkpDnuik1sg" showIcon="false">
2311 <customFeatures>labelSize</customFeatures>
2312 </centerLabelStyle>
2313 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_VjwM0KA8EeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
2314 <customFeatures>labelSize</customFeatures>
2315 </endLabelStyle>
2316 </ownedStyle>
2317 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2318 </ownedDiagramElements>
2319 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_p9d30KA8EeuqkpDnuik1sg" name="Assertion" tooltipText="" outgoingEdges="_w-iG36A8EeuqkpDnuik1sg _mQFKsqA9EeuqkpDnuik1sg _YAoQHdeAEeufiOvRR5sVhg" width="12" height="10">
2320 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Assertion"/>
2321 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Assertion"/>
2322 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2323 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2324 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2325 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_Zdc4dwGzEey7cfH5K6RyCw" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2326 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2327 </ownedStyle>
2328 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2329 <ownedElements xmi:type="diagram:DNodeListElement" uid="_NAnRgKA9EeuqkpDnuik1sg" name="value : LogicValue = TRUE" tooltipText="">
2330 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//Assertion/value"/>
2331 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//Assertion/value"/>
2332 <ownedStyle xmi:type="diagram:BundledImage" uid="_NgfEZ6A9EeuqkpDnuik1sg" labelAlignment="LEFT">
2333 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
2334 </ownedStyle>
2335 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
2336 </ownedElements>
2337 <ownedElements xmi:type="diagram:DNodeListElement" uid="_Y2m60AGzEey7cfH5K6RyCw" name="default : EBoolean = false" tooltipText="">
2338 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//Assertion/default"/>
2339 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//Assertion/default"/>
2340 <ownedStyle xmi:type="diagram:BundledImage" uid="_ZdetkwGzEey7cfH5K6RyCw" labelAlignment="LEFT">
2341 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
2342 </ownedStyle>
2343 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
2344 </ownedElements>
2345 </ownedDiagramElements>
2346 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_w-iG36A8EeuqkpDnuik1sg" name="[0..1] relation" sourceNode="_p9d30KA8EeuqkpDnuik1sg" targetNode="_EfNqcKA4EeuqkpDnuik1sg">
2347 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Assertion/relation"/>
2348 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Assertion/relation"/>
2349 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_w-it4KA8EeuqkpDnuik1sg" routingStyle="manhattan" strokeColor="0,0,0">
2350 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']/@style"/>
2351 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_w-it4qA8EeuqkpDnuik1sg" showIcon="false">
2352 <customFeatures>labelSize</customFeatures>
2353 </centerLabelStyle>
2354 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_w-it4aA8EeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
2355 <customFeatures>labelSize</customFeatures>
2356 </endLabelStyle>
2357 </ownedStyle>
2358 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2359 </ownedDiagramElements>
2360 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_xsYrUKA8EeuqkpDnuik1sg" name="Node" tooltipText="" outgoingEdges="_QlymotbNEeuymriYTNxK2g" incomingEdges="_4eU5TqA8EeuqkpDnuik1sg _-XLKltawEeuymriYTNxK2g _gRDCgNbGEeuymriYTNxK2g _vRo4VteAEeufiOvRR5sVhg _AmBxQNeBEeufiOvRR5sVhg _zJpmRgGyEey7cfH5K6RyCw" width="12" height="10">
2361 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Node"/>
2362 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Node"/>
2363 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2364 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2365 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2366 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_McYjKaA9EeuqkpDnuik1sg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2367 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2368 </ownedStyle>
2369 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2370 </ownedDiagramElements>
2371 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_4eU5TqA8EeuqkpDnuik1sg" name="[0..*] nodes" sourceNode="_D05iIKA4EeuqkpDnuik1sg" targetNode="_xsYrUKA8EeuqkpDnuik1sg">
2372 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Problem/nodes"/>
2373 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Problem/nodes"/>
2374 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_4eU5T6A8EeuqkpDnuik1sg" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
2375 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_4eU5UaA8EeuqkpDnuik1sg" showIcon="false">
2376 <customFeatures>labelSize</customFeatures>
2377 </centerLabelStyle>
2378 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_4eU5UKA8EeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
2379 <customFeatures>labelSize</customFeatures>
2380 </endLabelStyle>
2381 </ownedStyle>
2382 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2383 </ownedDiagramElements>
2384 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_BMXnYKA9EeuqkpDnuik1sg" name="LogicValue" tooltipText="" width="12" height="10">
2385 <target xmi:type="ecore:EEnum" href="src/main/resources/model/problem.ecore#//LogicValue"/>
2386 <semanticElements xmi:type="ecore:EEnum" href="src/main/resources/model/problem.ecore#//LogicValue"/>
2387 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2388 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2389 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2390 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_BMXnYaA9EeuqkpDnuik1sg" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="221,236,202">
2391 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@style"/>
2392 </ownedStyle>
2393 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']"/>
2394 <ownedElements xmi:type="diagram:DNodeListElement" uid="_B6IFQKA9EeuqkpDnuik1sg" name="TRUE" tooltipText="">
2395 <target xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//LogicValue/TRUE"/>
2396 <semanticElements xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//LogicValue/TRUE"/>
2397 <ownedStyle xmi:type="diagram:BundledImage" uid="_B6IsUKA9EeuqkpDnuik1sg" labelAlignment="LEFT">
2398 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']/@style"/>
2399 </ownedStyle>
2400 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/>
2401 </ownedElements>
2402 <ownedElements xmi:type="diagram:DNodeListElement" uid="_D8ciUKA9EeuqkpDnuik1sg" name="FALSE" tooltipText="">
2403 <target xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//LogicValue/FALSE"/>
2404 <semanticElements xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//LogicValue/FALSE"/>
2405 <ownedStyle xmi:type="diagram:BundledImage" uid="_D8dJYKA9EeuqkpDnuik1sg" labelAlignment="LEFT">
2406 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']/@style"/>
2407 </ownedStyle>
2408 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/>
2409 </ownedElements>
2410 <ownedElements xmi:type="diagram:DNodeListElement" uid="_JWqvUKA9EeuqkpDnuik1sg" name="UNKNOWN" tooltipText="">
2411 <target xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//LogicValue/UNKNOWN"/>
2412 <semanticElements xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//LogicValue/UNKNOWN"/>
2413 <ownedStyle xmi:type="diagram:BundledImage" uid="_JWrWYKA9EeuqkpDnuik1sg" labelAlignment="LEFT">
2414 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']/@style"/>
2415 </ownedStyle>
2416 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/>
2417 </ownedElements>
2418 <ownedElements xmi:type="diagram:DNodeListElement" uid="_9K7BcAGzEey7cfH5K6RyCw" name="ERROR" tooltipText="">
2419 <target xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//LogicValue/ERROR"/>
2420 <semanticElements xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//LogicValue/ERROR"/>
2421 <ownedStyle xmi:type="diagram:BundledImage" uid="_9K8PkAGzEey7cfH5K6RyCw" labelAlignment="LEFT">
2422 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']/@style"/>
2423 </ownedStyle>
2424 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/>
2425 </ownedElements>
2426 </ownedDiagramElements>
2427 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_QTzgkKA9EeuqkpDnuik1sg" name="ScopeDeclaration" tooltipText="" outgoingEdges="_vdptgqA9EeuqkpDnuik1sg _F2vPU6A-EeuqkpDnuik1sg" width="12" height="10">
2428 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ScopeDeclaration"/>
2429 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ScopeDeclaration"/>
2430 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2431 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2432 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2433 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_QT0HoKA9EeuqkpDnuik1sg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2434 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2435 </ownedStyle>
2436 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2437 </ownedDiagramElements>
2438 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_e7ydoKA9EeuqkpDnuik1sg" name="Statement" tooltipText="" incomingEdges="_hU64ZqA9EeuqkpDnuik1sg _mQFKsqA9EeuqkpDnuik1sg _mzziwKA9EeuqkpDnuik1sg _plK3JqA9EeuqkpDnuik1sg _vdptgqA9EeuqkpDnuik1sg _WX_5w9bGEeuymriYTNxK2g _DkzPhteBEeufiOvRR5sVhg _s68oXAGyEey7cfH5K6RyCw" width="12" height="10">
2439 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Statement"/>
2440 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Statement"/>
2441 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2442 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2443 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2444 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_k_IgQ6A9EeuqkpDnuik1sg" iconPath="/org.eclipse.emf.ecoretools.design/icons/full/obj16/EClass_interface.gif" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="228,228,228">
2445 <labelFormat>italic</labelFormat>
2446 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@conditionnalStyles.0/@style"/>
2447 </ownedStyle>
2448 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2449 </ownedDiagramElements>
2450 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_hU64ZqA9EeuqkpDnuik1sg" name="[0..*] statements" sourceNode="_D05iIKA4EeuqkpDnuik1sg" targetNode="_e7ydoKA9EeuqkpDnuik1sg">
2451 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Problem/statements"/>
2452 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Problem/statements"/>
2453 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_hU7fQKA9EeuqkpDnuik1sg" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
2454 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_hU7fQqA9EeuqkpDnuik1sg" showIcon="false">
2455 <customFeatures>labelSize</customFeatures>
2456 </centerLabelStyle>
2457 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_hU7fQaA9EeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
2458 <customFeatures>labelSize</customFeatures>
2459 </endLabelStyle>
2460 </ownedStyle>
2461 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2462 </ownedDiagramElements>
2463 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_mQFKsqA9EeuqkpDnuik1sg" sourceNode="_p9d30KA8EeuqkpDnuik1sg" targetNode="_e7ydoKA9EeuqkpDnuik1sg">
2464 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Assertion"/>
2465 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Assertion"/>
2466 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_Zdk0XQGzEey7cfH5K6RyCw" lineStyle="dash" targetArrow="InputClosedArrow" routingStyle="tree">
2467 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@conditionnalStyles.0/@style"/>
2468 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_Zdk0XgGzEey7cfH5K6RyCw" showIcon="false">
2469 <labelFormat>italic</labelFormat>
2470 </beginLabelStyle>
2471 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_Zdk0XwGzEey7cfH5K6RyCw" showIcon="false"/>
2472 </ownedStyle>
2473 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2474 </ownedDiagramElements>
2475 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_mzziwKA9EeuqkpDnuik1sg" sourceNode="_A8hIkCrZEeyyC-O0_LlY9w" targetNode="_e7ydoKA9EeuqkpDnuik1sg">
2476 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ParametricDefinition"/>
2477 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ParametricDefinition"/>
2478 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_-2abBCrcEeyyC-O0_LlY9w" lineStyle="dash" targetArrow="InputClosedArrow" routingStyle="tree">
2479 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@conditionnalStyles.0/@style"/>
2480 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_-2abBSrcEeyyC-O0_LlY9w" showIcon="false">
2481 <labelFormat>italic</labelFormat>
2482 </beginLabelStyle>
2483 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_-2abBircEeyyC-O0_LlY9w" showIcon="false"/>
2484 </ownedStyle>
2485 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2486 </ownedDiagramElements>
2487 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_plK3JqA9EeuqkpDnuik1sg" sourceNode="_JTstIKA4EeuqkpDnuik1sg" targetNode="_e7ydoKA9EeuqkpDnuik1sg">
2488 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ClassDeclaration"/>
2489 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ClassDeclaration"/>
2490 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_plK3J6A9EeuqkpDnuik1sg" lineStyle="dash" targetArrow="InputClosedArrow" routingStyle="tree">
2491 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@conditionnalStyles.0/@style"/>
2492 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_plK3KKA9EeuqkpDnuik1sg" showIcon="false">
2493 <labelFormat>italic</labelFormat>
2494 </beginLabelStyle>
2495 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_plK3KaA9EeuqkpDnuik1sg" showIcon="false"/>
2496 </ownedStyle>
2497 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2498 </ownedDiagramElements>
2499 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_vdptgqA9EeuqkpDnuik1sg" sourceNode="_QTzgkKA9EeuqkpDnuik1sg" targetNode="_e7ydoKA9EeuqkpDnuik1sg">
2500 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ScopeDeclaration"/>
2501 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ScopeDeclaration"/>
2502 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_vdqUgKA9EeuqkpDnuik1sg" lineStyle="dash" targetArrow="InputClosedArrow" routingStyle="tree">
2503 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@conditionnalStyles.0/@style"/>
2504 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_vdqUgaA9EeuqkpDnuik1sg" showIcon="false">
2505 <labelFormat>italic</labelFormat>
2506 </beginLabelStyle>
2507 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_vdqUgqA9EeuqkpDnuik1sg" showIcon="false"/>
2508 </ownedStyle>
2509 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2510 </ownedDiagramElements>
2511 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_zac6MKA9EeuqkpDnuik1sg" name="TypeScope" tooltipText="" outgoingEdges="_2kWqbKA9EeuqkpDnuik1sg _n5fiHqBJEeuqkpDnuik1sg" incomingEdges="_F2vPU6A-EeuqkpDnuik1sg" width="12" height="10">
2512 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//TypeScope"/>
2513 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//TypeScope"/>
2514 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2515 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2516 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2517 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_DlBrZqA-EeuqkpDnuik1sg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2518 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2519 </ownedStyle>
2520 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2521 <ownedElements xmi:type="diagram:DNodeListElement" uid="_BtHHgKA-EeuqkpDnuik1sg" name="increment : EBoolean = false" tooltipText="">
2522 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//TypeScope/increment"/>
2523 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//TypeScope/increment"/>
2524 <ownedStyle xmi:type="diagram:BundledImage" uid="_DlEusaA-EeuqkpDnuik1sg" labelAlignment="LEFT">
2525 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
2526 </ownedStyle>
2527 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
2528 </ownedElements>
2529 </ownedDiagramElements>
2530 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_2kWqbKA9EeuqkpDnuik1sg" name="[0..1] targetType" sourceNode="_zac6MKA9EeuqkpDnuik1sg" targetNode="_JTstIKA4EeuqkpDnuik1sg">
2531 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//TypeScope/targetType"/>
2532 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//TypeScope/targetType"/>
2533 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_2kXRcKA9EeuqkpDnuik1sg" routingStyle="manhattan" strokeColor="0,0,0">
2534 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']/@style"/>
2535 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_2kXRcqA9EeuqkpDnuik1sg" showIcon="false">
2536 <customFeatures>labelSize</customFeatures>
2537 </centerLabelStyle>
2538 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_2kXRcaA9EeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
2539 <customFeatures>labelSize</customFeatures>
2540 </endLabelStyle>
2541 </ownedStyle>
2542 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2543 </ownedDiagramElements>
2544 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_F2vPU6A-EeuqkpDnuik1sg" name="[0..*] typeScopes" sourceNode="_QTzgkKA9EeuqkpDnuik1sg" targetNode="_zac6MKA9EeuqkpDnuik1sg">
2545 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ScopeDeclaration/typeScopes"/>
2546 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ScopeDeclaration/typeScopes"/>
2547 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_F2v2YKA-EeuqkpDnuik1sg" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
2548 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_F2v2YqA-EeuqkpDnuik1sg" showIcon="false">
2549 <customFeatures>labelSize</customFeatures>
2550 </centerLabelStyle>
2551 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_F2v2YaA-EeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
2552 <customFeatures>labelSize</customFeatures>
2553 </endLabelStyle>
2554 </ownedStyle>
2555 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2556 </ownedDiagramElements>
2557 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_UJJy0qBDEeuqkpDnuik1sg" name="[0..1] atom" sourceNode="_XLJ4gCraEeyyC-O0_LlY9w" targetNode="_6J_bsKA6EeuqkpDnuik1sg">
2558 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//CompoundLiteral/atom"/>
2559 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//CompoundLiteral/atom"/>
2560 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_UJKZ0KBDEeuqkpDnuik1sg" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
2561 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_UJKZ0qBDEeuqkpDnuik1sg" showIcon="false">
2562 <customFeatures>labelSize</customFeatures>
2563 </centerLabelStyle>
2564 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_UJKZ0aBDEeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
2565 <customFeatures>labelSize</customFeatures>
2566 </endLabelStyle>
2567 </ownedStyle>
2568 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2569 </ownedDiagramElements>
2570 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_ReiX4KBJEeuqkpDnuik1sg" name="Multiplicity" tooltipText="" incomingEdges="_c_boQqBJEeuqkpDnuik1sg _dn26MqBJEeuqkpDnuik1sg _n5fiHqBJEeuqkpDnuik1sg _p1JWcqBJEeuqkpDnuik1sg _HUJ8o9YBEeuF_d0WEhR3Xw" width="12" height="10">
2571 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Multiplicity"/>
2572 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Multiplicity"/>
2573 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2574 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2575 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2576 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_eULNnKBJEeuqkpDnuik1sg" iconPath="/org.eclipse.emf.ecoretools.design/icons/full/obj16/EClass_abstract.gif" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="228,228,228">
2577 <labelFormat>italic</labelFormat>
2578 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@conditionnalStyles.1/@style"/>
2579 </ownedStyle>
2580 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2581 </ownedDiagramElements>
2582 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_Tx0CEKBJEeuqkpDnuik1sg" name="RangeMultiplicity" tooltipText="" outgoingEdges="_c_boQqBJEeuqkpDnuik1sg" width="12" height="10">
2583 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//RangeMultiplicity"/>
2584 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//RangeMultiplicity"/>
2585 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2586 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2587 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2588 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_kxDKYaBJEeuqkpDnuik1sg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2589 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2590 </ownedStyle>
2591 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2592 <ownedElements xmi:type="diagram:DNodeListElement" uid="_iccDcKBJEeuqkpDnuik1sg" name="lowerBound : EInt = 0" tooltipText="">
2593 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//RangeMultiplicity/lowerBound"/>
2594 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//RangeMultiplicity/lowerBound"/>
2595 <ownedStyle xmi:type="diagram:BundledImage" uid="_jDn_QaBJEeuqkpDnuik1sg" labelAlignment="LEFT">
2596 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
2597 </ownedStyle>
2598 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
2599 </ownedElements>
2600 <ownedElements xmi:type="diagram:DNodeListElement" uid="_kKaoMKBJEeuqkpDnuik1sg" name="upperBound : EInt = -1" tooltipText="">
2601 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//RangeMultiplicity/upperBound"/>
2602 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//RangeMultiplicity/upperBound"/>
2603 <ownedStyle xmi:type="diagram:BundledImage" uid="_kxFmoqBJEeuqkpDnuik1sg" labelAlignment="LEFT">
2604 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
2605 </ownedStyle>
2606 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
2607 </ownedElements>
2608 </ownedDiagramElements>
2609 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_aPIDwKBJEeuqkpDnuik1sg" name="ExactMultiplicity" tooltipText="" outgoingEdges="_dn26MqBJEeuqkpDnuik1sg" width="12" height="10">
2610 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ExactMultiplicity"/>
2611 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ExactMultiplicity"/>
2612 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2613 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2614 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2615 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_nIZcRaBJEeuqkpDnuik1sg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2616 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2617 </ownedStyle>
2618 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2619 <ownedElements xmi:type="diagram:DNodeListElement" uid="_lhXNAKBJEeuqkpDnuik1sg" name="exactValue : EInt = 1" tooltipText="">
2620 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//ExactMultiplicity/exactValue"/>
2621 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//ExactMultiplicity/exactValue"/>
2622 <ownedStyle xmi:type="diagram:BundledImage" uid="_nIsXNaBJEeuqkpDnuik1sg" labelAlignment="LEFT">
2623 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
2624 </ownedStyle>
2625 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
2626 </ownedElements>
2627 </ownedDiagramElements>
2628 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_c_boQqBJEeuqkpDnuik1sg" sourceNode="_Tx0CEKBJEeuqkpDnuik1sg" targetNode="_ReiX4KBJEeuqkpDnuik1sg">
2629 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//RangeMultiplicity"/>
2630 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//RangeMultiplicity"/>
2631 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_kxLGYKBJEeuqkpDnuik1sg" targetArrow="InputClosedArrow" routingStyle="tree">
2632 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2633 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_kxLGYaBJEeuqkpDnuik1sg" showIcon="false">
2634 <labelFormat>italic</labelFormat>
2635 </beginLabelStyle>
2636 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_kxLGYqBJEeuqkpDnuik1sg" showIcon="false"/>
2637 </ownedStyle>
2638 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2639 </ownedDiagramElements>
2640 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_dn26MqBJEeuqkpDnuik1sg" sourceNode="_aPIDwKBJEeuqkpDnuik1sg" targetNode="_ReiX4KBJEeuqkpDnuik1sg">
2641 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ExactMultiplicity"/>
2642 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ExactMultiplicity"/>
2643 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_nIwopqBJEeuqkpDnuik1sg" targetArrow="InputClosedArrow" routingStyle="tree">
2644 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2645 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_nIwop6BJEeuqkpDnuik1sg" showIcon="false">
2646 <labelFormat>italic</labelFormat>
2647 </beginLabelStyle>
2648 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_nIwoqKBJEeuqkpDnuik1sg" showIcon="false"/>
2649 </ownedStyle>
2650 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2651 </ownedDiagramElements>
2652 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_n5fiHqBJEeuqkpDnuik1sg" name="[0..1] multiplicity" sourceNode="_zac6MKA9EeuqkpDnuik1sg" targetNode="_ReiX4KBJEeuqkpDnuik1sg">
2653 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//TypeScope/multiplicity"/>
2654 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//TypeScope/multiplicity"/>
2655 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_n5gJEKBJEeuqkpDnuik1sg" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
2656 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_n5gJEqBJEeuqkpDnuik1sg" showIcon="false">
2657 <customFeatures>labelSize</customFeatures>
2658 </centerLabelStyle>
2659 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_n5gJEaBJEeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
2660 <customFeatures>labelSize</customFeatures>
2661 </endLabelStyle>
2662 </ownedStyle>
2663 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2664 </ownedDiagramElements>
2665 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_p1JWcqBJEeuqkpDnuik1sg" name="[0..1] multiplicity" sourceNode="_c-A7oKA4EeuqkpDnuik1sg" targetNode="_ReiX4KBJEeuqkpDnuik1sg">
2666 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ReferenceDeclaration/multiplicity"/>
2667 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ReferenceDeclaration/multiplicity"/>
2668 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_p1J9cKBJEeuqkpDnuik1sg" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
2669 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_p1J9cqBJEeuqkpDnuik1sg" showIcon="false">
2670 <customFeatures>labelSize</customFeatures>
2671 </centerLabelStyle>
2672 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_p1J9caBJEeuqkpDnuik1sg" showIcon="false" labelColor="39,76,114">
2673 <customFeatures>labelSize</customFeatures>
2674 </endLabelStyle>
2675 </ownedStyle>
2676 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2677 </ownedDiagramElements>
2678 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_m-6OTNXtEeuF_d0WEhR3Xw" sourceNode="_D05iIKA4EeuqkpDnuik1sg" targetNode="_RzK-YKA5EeuqkpDnuik1sg">
2679 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Problem"/>
2680 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Problem"/>
2681 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_m-61UNXtEeuF_d0WEhR3Xw" targetArrow="InputClosedArrow" routingStyle="tree">
2682 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2683 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_m-61UdXtEeuF_d0WEhR3Xw" showIcon="false">
2684 <labelFormat>italic</labelFormat>
2685 </beginLabelStyle>
2686 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_m-61UtXtEeuF_d0WEhR3Xw" showIcon="false"/>
2687 </ownedStyle>
2688 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2689 </ownedDiagramElements>
2690 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_D9_5INYBEeuF_d0WEhR3Xw" name="UnboundedMultiplicity" tooltipText="" outgoingEdges="_HUJ8o9YBEeuF_d0WEhR3Xw" width="12" height="10">
2691 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//UnboundedMultiplicity"/>
2692 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//UnboundedMultiplicity"/>
2693 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2694 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2695 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2696 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_D-DjgNYBEeuF_d0WEhR3Xw" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2697 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2698 </ownedStyle>
2699 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2700 </ownedDiagramElements>
2701 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_HUJ8o9YBEeuF_d0WEhR3Xw" sourceNode="_D9_5INYBEeuF_d0WEhR3Xw" targetNode="_ReiX4KBJEeuqkpDnuik1sg">
2702 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//UnboundedMultiplicity"/>
2703 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//UnboundedMultiplicity"/>
2704 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_HUKjsNYBEeuF_d0WEhR3Xw" targetArrow="InputClosedArrow" routingStyle="tree">
2705 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2706 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_HUKjsdYBEeuF_d0WEhR3Xw" showIcon="false">
2707 <labelFormat>italic</labelFormat>
2708 </beginLabelStyle>
2709 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_HUKjstYBEeuF_d0WEhR3Xw" showIcon="false"/>
2710 </ownedStyle>
2711 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2712 </ownedDiagramElements>
2713 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_IwJ4MNYPEeuF_d0WEhR3Xw" name="VariableOrNodeArgument" tooltipText="" outgoingEdges="_T1fN5tYPEeuF_d0WEhR3Xw _bx-jYtYPEeuF_d0WEhR3Xw _1HYZXNd_EeufiOvRR5sVhg" width="12" height="10">
2714 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//VariableOrNodeArgument"/>
2715 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//VariableOrNodeArgument"/>
2716 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2717 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2718 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2719 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_IwLtYNYPEeuF_d0WEhR3Xw" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2720 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2721 </ownedStyle>
2722 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2723 </ownedDiagramElements>
2724 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_KmxbkNYPEeuF_d0WEhR3Xw" name="[0..*] arguments" sourceNode="_6J_bsKA6EeuqkpDnuik1sg" targetNode="_rwC2UNd_EeufiOvRR5sVhg">
2725 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Atom/arguments"/>
2726 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Atom/arguments"/>
2727 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_P-mb4NYPEeuF_d0WEhR3Xw" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
2728 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_P-mb4tYPEeuF_d0WEhR3Xw" showIcon="false">
2729 <customFeatures>labelSize</customFeatures>
2730 </centerLabelStyle>
2731 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_P-mb4dYPEeuF_d0WEhR3Xw" showIcon="false" labelColor="39,76,114">
2732 <customFeatures>labelSize</customFeatures>
2733 </endLabelStyle>
2734 </ownedStyle>
2735 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2736 </ownedDiagramElements>
2737 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_T1fN5tYPEeuF_d0WEhR3Xw" name="[0..1] variableOrNode" sourceNode="_IwJ4MNYPEeuF_d0WEhR3Xw" targetNode="_KwtCUNbNEeuymriYTNxK2g">
2738 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//VariableOrNodeArgument/variableOrNode"/>
2739 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//VariableOrNodeArgument/variableOrNode"/>
2740 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_T1fN59YPEeuF_d0WEhR3Xw" routingStyle="manhattan" strokeColor="0,0,0">
2741 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']/@style"/>
2742 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_T1fN6dYPEeuF_d0WEhR3Xw" showIcon="false">
2743 <customFeatures>labelSize</customFeatures>
2744 </centerLabelStyle>
2745 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_T1fN6NYPEeuF_d0WEhR3Xw" showIcon="false" labelColor="39,76,114">
2746 <customFeatures>labelSize</customFeatures>
2747 </endLabelStyle>
2748 </ownedStyle>
2749 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2750 </ownedDiagramElements>
2751 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_bx-jYtYPEeuF_d0WEhR3Xw" name="[0..1] singletonVariable" sourceNode="_IwJ4MNYPEeuF_d0WEhR3Xw" targetNode="_-O6CoKA6EeuqkpDnuik1sg">
2752 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//VariableOrNodeArgument/singletonVariable"/>
2753 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//VariableOrNodeArgument/singletonVariable"/>
2754 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_dcHJ49YPEeuF_d0WEhR3Xw" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
2755 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_dcHJ5dYPEeuF_d0WEhR3Xw" showIcon="false"/>
2756 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_dcHJ5NYPEeuF_d0WEhR3Xw" labelSize="6" showIcon="false" labelColor="39,76,114"/>
2757 </ownedStyle>
2758 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2759 </ownedDiagramElements>
2760 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_-XLKltawEeuymriYTNxK2g" name="[0..1] newNode" sourceNode="_JTstIKA4EeuqkpDnuik1sg" targetNode="_xsYrUKA8EeuqkpDnuik1sg">
2761 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ClassDeclaration/newNode"/>
2762 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ClassDeclaration/newNode"/>
2763 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_-XMYsNawEeuymriYTNxK2g" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
2764 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_-XMYstawEeuymriYTNxK2g" showIcon="false">
2765 <customFeatures>labelSize</customFeatures>
2766 </centerLabelStyle>
2767 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_-XMYsdawEeuymriYTNxK2g" showIcon="false" labelColor="39,76,114">
2768 <customFeatures>labelSize</customFeatures>
2769 </endLabelStyle>
2770 </ownedStyle>
2771 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2772 </ownedDiagramElements>
2773 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_M58q8NbGEeuymriYTNxK2g" name="EnumDeclaration" tooltipText="" outgoingEdges="_UwbfHtbGEeuymriYTNxK2g _WX_5w9bGEeuymriYTNxK2g _gRDCgNbGEeuymriYTNxK2g" width="12" height="10">
2774 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//EnumDeclaration"/>
2775 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//EnumDeclaration"/>
2776 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2777 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2778 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2779 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_M59SANbGEeuymriYTNxK2g" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2780 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2781 </ownedStyle>
2782 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2783 </ownedDiagramElements>
2784 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_UwbfHtbGEeuymriYTNxK2g" sourceNode="_M58q8NbGEeuymriYTNxK2g" targetNode="_EfNqcKA4EeuqkpDnuik1sg">
2785 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//EnumDeclaration"/>
2786 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//EnumDeclaration"/>
2787 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_UwcGENbGEeuymriYTNxK2g" targetArrow="InputClosedArrow" routingStyle="tree">
2788 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2789 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_UwcGEdbGEeuymriYTNxK2g" showIcon="false">
2790 <labelFormat>italic</labelFormat>
2791 </beginLabelStyle>
2792 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_UwcGEtbGEeuymriYTNxK2g" showIcon="false"/>
2793 </ownedStyle>
2794 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2795 </ownedDiagramElements>
2796 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_WX_5w9bGEeuymriYTNxK2g" sourceNode="_M58q8NbGEeuymriYTNxK2g" targetNode="_e7ydoKA9EeuqkpDnuik1sg">
2797 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//EnumDeclaration"/>
2798 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//EnumDeclaration"/>
2799 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_WYAg0NbGEeuymriYTNxK2g" lineStyle="dash" targetArrow="InputClosedArrow" routingStyle="tree">
2800 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@conditionnalStyles.0/@style"/>
2801 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_WYAg0dbGEeuymriYTNxK2g" showIcon="false">
2802 <labelFormat>italic</labelFormat>
2803 </beginLabelStyle>
2804 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_WYAg0tbGEeuymriYTNxK2g" showIcon="false"/>
2805 </ownedStyle>
2806 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2807 </ownedDiagramElements>
2808 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_gRDCgNbGEeuymriYTNxK2g" name="[0..*] literals" sourceNode="_M58q8NbGEeuymriYTNxK2g" targetNode="_xsYrUKA8EeuqkpDnuik1sg">
2809 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//EnumDeclaration/literals"/>
2810 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//EnumDeclaration/literals"/>
2811 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_gRDpkNbGEeuymriYTNxK2g" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
2812 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_gRDpktbGEeuymriYTNxK2g" showIcon="false">
2813 <customFeatures>labelSize</customFeatures>
2814 </centerLabelStyle>
2815 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_gRDpkdbGEeuymriYTNxK2g" showIcon="false" labelColor="39,76,114">
2816 <customFeatures>labelSize</customFeatures>
2817 </endLabelStyle>
2818 </ownedStyle>
2819 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2820 </ownedDiagramElements>
2821 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_KwtCUNbNEeuymriYTNxK2g" name="VariableOrNode" tooltipText="" outgoingEdges="_Smi9eNbNEeuymriYTNxK2g" incomingEdges="_QlymotbNEeuymriYTNxK2g _Tt9RRtbNEeuymriYTNxK2g _T1fN5tYPEeuF_d0WEhR3Xw _btj3UirbEeyyC-O0_LlY9w" width="12" height="10">
2822 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//VariableOrNode"/>
2823 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//VariableOrNode"/>
2824 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2825 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2826 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2827 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_QMf8vdbNEeuymriYTNxK2g" iconPath="/org.eclipse.emf.ecoretools.design/icons/full/obj16/EClass_abstract.gif" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="228,228,228">
2828 <labelFormat>italic</labelFormat>
2829 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@conditionnalStyles.1/@style"/>
2830 </ownedStyle>
2831 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2832 </ownedDiagramElements>
2833 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_QlymotbNEeuymriYTNxK2g" sourceNode="_xsYrUKA8EeuqkpDnuik1sg" targetNode="_KwtCUNbNEeuymriYTNxK2g">
2834 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Node"/>
2835 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Node"/>
2836 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_QlzNcNbNEeuymriYTNxK2g" targetArrow="InputClosedArrow" routingStyle="tree">
2837 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2838 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_QlzNcdbNEeuymriYTNxK2g" showIcon="false">
2839 <labelFormat>italic</labelFormat>
2840 </beginLabelStyle>
2841 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_QlzNctbNEeuymriYTNxK2g" showIcon="false"/>
2842 </ownedStyle>
2843 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2844 </ownedDiagramElements>
2845 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_Smi9eNbNEeuymriYTNxK2g" sourceNode="_KwtCUNbNEeuymriYTNxK2g" targetNode="_RzK-YKA5EeuqkpDnuik1sg">
2846 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//VariableOrNode"/>
2847 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//VariableOrNode"/>
2848 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_SmjkQNbNEeuymriYTNxK2g" targetArrow="InputClosedArrow" routingStyle="tree">
2849 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2850 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_SmjkQdbNEeuymriYTNxK2g" showIcon="false">
2851 <labelFormat>italic</labelFormat>
2852 </beginLabelStyle>
2853 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_SmjkQtbNEeuymriYTNxK2g" showIcon="false"/>
2854 </ownedStyle>
2855 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2856 </ownedDiagramElements>
2857 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_Tt9RRtbNEeuymriYTNxK2g" sourceNode="_jPpm4KA6EeuqkpDnuik1sg" targetNode="_KwtCUNbNEeuymriYTNxK2g">
2858 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Variable"/>
2859 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Variable"/>
2860 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_Tt94QNbNEeuymriYTNxK2g" targetArrow="InputClosedArrow" routingStyle="tree">
2861 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2862 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_Tt94QdbNEeuymriYTNxK2g" showIcon="false">
2863 <labelFormat>italic</labelFormat>
2864 </beginLabelStyle>
2865 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_Tt94QtbNEeuymriYTNxK2g" showIcon="false"/>
2866 </ownedStyle>
2867 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2868 </ownedDiagramElements>
2869 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_IrcrINd_EeufiOvRR5sVhg" name="Constant" tooltipText="" incomingEdges="_Vsm0XNd_EeufiOvRR5sVhg _WEGuVtd_EeufiOvRR5sVhg _Waktldd_EeufiOvRR5sVhg _nuG1dteAEeufiOvRR5sVhg _pMdCzNeAEeufiOvRR5sVhg _WiKRoteBEeufiOvRR5sVhg" width="12" height="10">
2870 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Constant"/>
2871 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Constant"/>
2872 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2873 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2874 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2875 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_J3H0Fdd_EeufiOvRR5sVhg" iconPath="/org.eclipse.emf.ecoretools.design/icons/full/obj16/EClass_abstract.gif" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="228,228,228">
2876 <labelFormat>italic</labelFormat>
2877 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@conditionnalStyles.1/@style"/>
2878 </ownedStyle>
2879 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2880 </ownedDiagramElements>
2881 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_KdTqUNd_EeufiOvRR5sVhg" name="IntConstant" tooltipText="" outgoingEdges="_Vsm0XNd_EeufiOvRR5sVhg" width="12" height="10">
2882 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//IntConstant"/>
2883 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//IntConstant"/>
2884 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2885 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2886 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2887 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_cid45dd_EeufiOvRR5sVhg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2888 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2889 </ownedStyle>
2890 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2891 <ownedElements xmi:type="diagram:DNodeListElement" uid="_brB6kNd_EeufiOvRR5sVhg" name="intValue : EInt = 0" tooltipText="">
2892 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//IntConstant/intValue"/>
2893 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//IntConstant/intValue"/>
2894 <ownedStyle xmi:type="diagram:BundledImage" uid="_cig8Mdd_EeufiOvRR5sVhg" labelAlignment="LEFT">
2895 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
2896 </ownedStyle>
2897 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
2898 </ownedElements>
2899 </ownedDiagramElements>
2900 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_MARR4Nd_EeufiOvRR5sVhg" name="RealConstant" tooltipText="" outgoingEdges="_WEGuVtd_EeufiOvRR5sVhg" width="12" height="10">
2901 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//RealConstant"/>
2902 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//RealConstant"/>
2903 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2904 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2905 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2906 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_f7dN99d_EeufiOvRR5sVhg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2907 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2908 </ownedStyle>
2909 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2910 <ownedElements xmi:type="diagram:DNodeListElement" uid="_exQEoNd_EeufiOvRR5sVhg" name="realValue : EDouble = 0.0" tooltipText="">
2911 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//RealConstant/realValue"/>
2912 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//RealConstant/realValue"/>
2913 <ownedStyle xmi:type="diagram:BundledImage" uid="_f7fqMtd_EeufiOvRR5sVhg" labelAlignment="LEFT">
2914 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
2915 </ownedStyle>
2916 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
2917 </ownedElements>
2918 </ownedDiagramElements>
2919 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_Rv9_oNd_EeufiOvRR5sVhg" name="StringConstant" tooltipText="" outgoingEdges="_Waktldd_EeufiOvRR5sVhg" width="12" height="10">
2920 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//StringConstant"/>
2921 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//StringConstant"/>
2922 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2923 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2924 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2925 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_h2BdN9d_EeufiOvRR5sVhg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2926 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2927 </ownedStyle>
2928 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2929 <ownedElements xmi:type="diagram:DNodeListElement" uid="_g4rrMNd_EeufiOvRR5sVhg" name="stringValue : EString" tooltipText="">
2930 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//StringConstant/stringValue"/>
2931 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//StringConstant/stringValue"/>
2932 <ownedStyle xmi:type="diagram:BundledImage" uid="_h2Egg9d_EeufiOvRR5sVhg" labelAlignment="LEFT">
2933 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
2934 </ownedStyle>
2935 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
2936 </ownedElements>
2937 </ownedDiagramElements>
2938 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_Vsm0XNd_EeufiOvRR5sVhg" sourceNode="_KdTqUNd_EeufiOvRR5sVhg" targetNode="_IrcrINd_EeufiOvRR5sVhg">
2939 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//IntConstant"/>
2940 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//IntConstant"/>
2941 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_cioRGtd_EeufiOvRR5sVhg" targetArrow="InputClosedArrow" routingStyle="tree">
2942 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2943 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_cioRG9d_EeufiOvRR5sVhg" showIcon="false">
2944 <labelFormat>italic</labelFormat>
2945 </beginLabelStyle>
2946 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_cioRHNd_EeufiOvRR5sVhg" showIcon="false"/>
2947 </ownedStyle>
2948 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2949 </ownedDiagramElements>
2950 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_WEGuVtd_EeufiOvRR5sVhg" sourceNode="_MARR4Nd_EeufiOvRR5sVhg" targetNode="_IrcrINd_EeufiOvRR5sVhg">
2951 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//RealConstant"/>
2952 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//RealConstant"/>
2953 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_f7lJ9td_EeufiOvRR5sVhg" targetArrow="InputClosedArrow" routingStyle="tree">
2954 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2955 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_f7lJ99d_EeufiOvRR5sVhg" showIcon="false">
2956 <labelFormat>italic</labelFormat>
2957 </beginLabelStyle>
2958 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_f7lJ-Nd_EeufiOvRR5sVhg" showIcon="false"/>
2959 </ownedStyle>
2960 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2961 </ownedDiagramElements>
2962 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_Waktldd_EeufiOvRR5sVhg" sourceNode="_Rv9_oNd_EeufiOvRR5sVhg" targetNode="_IrcrINd_EeufiOvRR5sVhg">
2963 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//StringConstant"/>
2964 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//StringConstant"/>
2965 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_h2LONtd_EeufiOvRR5sVhg" targetArrow="InputClosedArrow" routingStyle="tree">
2966 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
2967 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_h2LON9d_EeufiOvRR5sVhg" showIcon="false">
2968 <labelFormat>italic</labelFormat>
2969 </beginLabelStyle>
2970 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_h2LOONd_EeufiOvRR5sVhg" showIcon="false"/>
2971 </ownedStyle>
2972 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2973 </ownedDiagramElements>
2974 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_mCSCQNd_EeufiOvRR5sVhg" name="ConstantArgument" tooltipText="" outgoingEdges="_zhINQtd_EeufiOvRR5sVhg _nuG1dteAEeufiOvRR5sVhg" width="12" height="10">
2975 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ConstantArgument"/>
2976 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ConstantArgument"/>
2977 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2978 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2979 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2980 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_mCSCQdd_EeufiOvRR5sVhg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2981 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2982 </ownedStyle>
2983 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2984 </ownedDiagramElements>
2985 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_rwC2UNd_EeufiOvRR5sVhg" name="Argument" tooltipText="" incomingEdges="_KmxbkNYPEeuF_d0WEhR3Xw _zhINQtd_EeufiOvRR5sVhg _1HYZXNd_EeufiOvRR5sVhg" width="12" height="10">
2986 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Argument"/>
2987 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Argument"/>
2988 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2989 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2990 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2991 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_uH9wANd_EeufiOvRR5sVhg" iconPath="/org.eclipse.emf.ecoretools.design/icons/full/obj16/EClass_abstract.gif" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="228,228,228">
2992 <labelFormat>italic</labelFormat>
2993 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@conditionnalStyles.1/@style"/>
2994 </ownedStyle>
2995 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2996 </ownedDiagramElements>
2997 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_zhINQtd_EeufiOvRR5sVhg" sourceNode="_mCSCQNd_EeufiOvRR5sVhg" targetNode="_rwC2UNd_EeufiOvRR5sVhg">
2998 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ConstantArgument"/>
2999 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ConstantArgument"/>
3000 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_zhI0ENd_EeufiOvRR5sVhg" targetArrow="InputClosedArrow" routingStyle="tree">
3001 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
3002 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_zhI0Edd_EeufiOvRR5sVhg" showIcon="false">
3003 <labelFormat>italic</labelFormat>
3004 </beginLabelStyle>
3005 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_zhI0Etd_EeufiOvRR5sVhg" showIcon="false"/>
3006 </ownedStyle>
3007 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3008 </ownedDiagramElements>
3009 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_1HYZXNd_EeufiOvRR5sVhg" sourceNode="_IwJ4MNYPEeuF_d0WEhR3Xw" targetNode="_rwC2UNd_EeufiOvRR5sVhg">
3010 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//VariableOrNodeArgument"/>
3011 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//VariableOrNodeArgument"/>
3012 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_1HYZXdd_EeufiOvRR5sVhg" targetArrow="InputClosedArrow" routingStyle="tree">
3013 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
3014 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_1HYZXtd_EeufiOvRR5sVhg" showIcon="false">
3015 <labelFormat>italic</labelFormat>
3016 </beginLabelStyle>
3017 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_1HYZX9d_EeufiOvRR5sVhg" showIcon="false"/>
3018 </ownedStyle>
3019 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3020 </ownedDiagramElements>
3021 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_OWYYMNeAEeufiOvRR5sVhg" name="NodeAssertionArgument" tooltipText="" outgoingEdges="_dDd3wteAEeufiOvRR5sVhg _vRo4VteAEeufiOvRR5sVhg" width="12" height="10">
3022 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NodeAssertionArgument"/>
3023 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NodeAssertionArgument"/>
3024 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3025 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3026 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3027 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_OWY_QNeAEeufiOvRR5sVhg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
3028 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
3029 </ownedStyle>
3030 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3031 </ownedDiagramElements>
3032 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_ViJbUNeAEeufiOvRR5sVhg" name="AssertionArgument" tooltipText="" incomingEdges="_YAoQHdeAEeufiOvRR5sVhg _dDd3wteAEeufiOvRR5sVhg _g_aI1teAEeufiOvRR5sVhg _WAUqNgGzEey7cfH5K6RyCw" width="12" height="10">
3033 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//AssertionArgument"/>
3034 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//AssertionArgument"/>
3035 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3036 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3037 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3038 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_XdUurteAEeufiOvRR5sVhg" iconPath="/org.eclipse.emf.ecoretools.design/icons/full/obj16/EClass_abstract.gif" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="228,228,228">
3039 <labelFormat>italic</labelFormat>
3040 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@conditionnalStyles.1/@style"/>
3041 </ownedStyle>
3042 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3043 </ownedDiagramElements>
3044 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_YAoQHdeAEeufiOvRR5sVhg" name="[0..*] arguments" sourceNode="_p9d30KA8EeuqkpDnuik1sg" targetNode="_ViJbUNeAEeufiOvRR5sVhg">
3045 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Assertion/arguments"/>
3046 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Assertion/arguments"/>
3047 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_YAo3ANeAEeufiOvRR5sVhg" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
3048 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_YAo3AteAEeufiOvRR5sVhg" showIcon="false">
3049 <customFeatures>labelSize</customFeatures>
3050 </centerLabelStyle>
3051 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_YAo3AdeAEeufiOvRR5sVhg" showIcon="false" labelColor="39,76,114">
3052 <customFeatures>labelSize</customFeatures>
3053 </endLabelStyle>
3054 </ownedStyle>
3055 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
3056 </ownedDiagramElements>
3057 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_dDd3wteAEeufiOvRR5sVhg" sourceNode="_OWYYMNeAEeufiOvRR5sVhg" targetNode="_ViJbUNeAEeufiOvRR5sVhg">
3058 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NodeAssertionArgument"/>
3059 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NodeAssertionArgument"/>
3060 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_dDeewNeAEeufiOvRR5sVhg" targetArrow="InputClosedArrow" routingStyle="tree">
3061 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
3062 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_dDeewdeAEeufiOvRR5sVhg" showIcon="false">
3063 <labelFormat>italic</labelFormat>
3064 </beginLabelStyle>
3065 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_dDeewteAEeufiOvRR5sVhg" showIcon="false"/>
3066 </ownedStyle>
3067 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3068 </ownedDiagramElements>
3069 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_dZVaANeAEeufiOvRR5sVhg" name="ConstantAssertionArgument" tooltipText="" outgoingEdges="_g_aI1teAEeufiOvRR5sVhg _pMdCzNeAEeufiOvRR5sVhg" width="12" height="10">
3070 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ConstantAssertionArgument"/>
3071 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ConstantAssertionArgument"/>
3072 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3073 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3074 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3075 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_dZVaAdeAEeufiOvRR5sVhg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
3076 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
3077 </ownedStyle>
3078 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3079 </ownedDiagramElements>
3080 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_g_aI1teAEeufiOvRR5sVhg" sourceNode="_dZVaANeAEeufiOvRR5sVhg" targetNode="_ViJbUNeAEeufiOvRR5sVhg">
3081 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ConstantAssertionArgument"/>
3082 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ConstantAssertionArgument"/>
3083 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_g_aI19eAEeufiOvRR5sVhg" targetArrow="InputClosedArrow" routingStyle="tree">
3084 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
3085 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_g_aI2NeAEeufiOvRR5sVhg" showIcon="false">
3086 <labelFormat>italic</labelFormat>
3087 </beginLabelStyle>
3088 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_g_aI2deAEeufiOvRR5sVhg" showIcon="false"/>
3089 </ownedStyle>
3090 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3091 </ownedDiagramElements>
3092 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_nuG1dteAEeufiOvRR5sVhg" name="[0..1] constant" sourceNode="_mCSCQNd_EeufiOvRR5sVhg" targetNode="_IrcrINd_EeufiOvRR5sVhg">
3093 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ConstantArgument/constant"/>
3094 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ConstantArgument/constant"/>
3095 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_nuG1d9eAEeufiOvRR5sVhg" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
3096 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_nuG1edeAEeufiOvRR5sVhg" showIcon="false">
3097 <customFeatures>labelSize</customFeatures>
3098 </centerLabelStyle>
3099 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_nuG1eNeAEeufiOvRR5sVhg" showIcon="false" labelColor="39,76,114">
3100 <customFeatures>labelSize</customFeatures>
3101 </endLabelStyle>
3102 </ownedStyle>
3103 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
3104 </ownedDiagramElements>
3105 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_pMdCzNeAEeufiOvRR5sVhg" name="[0..1] constant" sourceNode="_dZVaANeAEeufiOvRR5sVhg" targetNode="_IrcrINd_EeufiOvRR5sVhg">
3106 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ConstantAssertionArgument/constant"/>
3107 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ConstantAssertionArgument/constant"/>
3108 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_pMdp0NeAEeufiOvRR5sVhg" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
3109 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_pMdp0teAEeufiOvRR5sVhg" showIcon="false">
3110 <customFeatures>labelSize</customFeatures>
3111 </centerLabelStyle>
3112 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_pMdp0deAEeufiOvRR5sVhg" showIcon="false" labelColor="39,76,114">
3113 <customFeatures>labelSize</customFeatures>
3114 </endLabelStyle>
3115 </ownedStyle>
3116 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
3117 </ownedDiagramElements>
3118 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_vRo4VteAEeufiOvRR5sVhg" name="[0..1] node" sourceNode="_OWYYMNeAEeufiOvRR5sVhg" targetNode="_xsYrUKA8EeuqkpDnuik1sg">
3119 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//NodeAssertionArgument/node"/>
3120 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//NodeAssertionArgument/node"/>
3121 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_vRo4V9eAEeufiOvRR5sVhg" routingStyle="manhattan" strokeColor="0,0,0">
3122 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']/@style"/>
3123 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_vRo4WdeAEeufiOvRR5sVhg" showIcon="false">
3124 <customFeatures>labelSize</customFeatures>
3125 </centerLabelStyle>
3126 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_vRo4WNeAEeufiOvRR5sVhg" showIcon="false" labelColor="39,76,114">
3127 <customFeatures>labelSize</customFeatures>
3128 </endLabelStyle>
3129 </ownedStyle>
3130 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
3131 </ownedDiagramElements>
3132 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_9TjT4NeAEeufiOvRR5sVhg" name="NodeValueAssertion" tooltipText="" outgoingEdges="_AmBxQNeBEeufiOvRR5sVhg _DkzPhteBEeufiOvRR5sVhg _WiKRoteBEeufiOvRR5sVhg" width="12" height="10">
3133 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NodeValueAssertion"/>
3134 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NodeValueAssertion"/>
3135 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3136 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3137 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3138 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_9Tj68NeAEeufiOvRR5sVhg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
3139 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
3140 </ownedStyle>
3141 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3142 </ownedDiagramElements>
3143 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_AmBxQNeBEeufiOvRR5sVhg" name="[0..1] node" sourceNode="_9TjT4NeAEeufiOvRR5sVhg" targetNode="_xsYrUKA8EeuqkpDnuik1sg">
3144 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//NodeValueAssertion/node"/>
3145 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//NodeValueAssertion/node"/>
3146 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_AmBxQdeBEeufiOvRR5sVhg" routingStyle="manhattan" strokeColor="0,0,0">
3147 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']/@style"/>
3148 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_AmBxQ9eBEeufiOvRR5sVhg" showIcon="false">
3149 <customFeatures>labelSize</customFeatures>
3150 </centerLabelStyle>
3151 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_AmBxQteBEeufiOvRR5sVhg" showIcon="false" labelColor="39,76,114">
3152 <customFeatures>labelSize</customFeatures>
3153 </endLabelStyle>
3154 </ownedStyle>
3155 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
3156 </ownedDiagramElements>
3157 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_DkzPhteBEeufiOvRR5sVhg" sourceNode="_9TjT4NeAEeufiOvRR5sVhg" targetNode="_e7ydoKA9EeuqkpDnuik1sg">
3158 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NodeValueAssertion"/>
3159 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NodeValueAssertion"/>
3160 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_DkzPh9eBEeufiOvRR5sVhg" lineStyle="dash" targetArrow="InputClosedArrow" routingStyle="tree">
3161 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@conditionnalStyles.0/@style"/>
3162 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_DkzPiNeBEeufiOvRR5sVhg" showIcon="false">
3163 <labelFormat>italic</labelFormat>
3164 </beginLabelStyle>
3165 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_DkzPideBEeufiOvRR5sVhg" showIcon="false"/>
3166 </ownedStyle>
3167 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3168 </ownedDiagramElements>
3169 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_WiKRoteBEeufiOvRR5sVhg" name="[0..1] value" sourceNode="_9TjT4NeAEeufiOvRR5sVhg" targetNode="_IrcrINd_EeufiOvRR5sVhg">
3170 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//NodeValueAssertion/value"/>
3171 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//NodeValueAssertion/value"/>
3172 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_WiK4cNeBEeufiOvRR5sVhg" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
3173 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_WiK4cteBEeufiOvRR5sVhg" showIcon="false">
3174 <customFeatures>labelSize</customFeatures>
3175 </centerLabelStyle>
3176 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_WiK4cdeBEeufiOvRR5sVhg" showIcon="false" labelColor="39,76,114">
3177 <customFeatures>labelSize</customFeatures>
3178 </endLabelStyle>
3179 </ownedStyle>
3180 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
3181 </ownedDiagramElements>
3182 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_pcXBkAGyEey7cfH5K6RyCw" name="IndividualDeclaration" tooltipText="" outgoingEdges="_s68oXAGyEey7cfH5K6RyCw _zJpmRgGyEey7cfH5K6RyCw" width="12" height="10">
3183 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//IndividualDeclaration"/>
3184 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//IndividualDeclaration"/>
3185 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3186 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3187 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3188 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_pcYPsAGyEey7cfH5K6RyCw" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
3189 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
3190 </ownedStyle>
3191 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3192 </ownedDiagramElements>
3193 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_s68oXAGyEey7cfH5K6RyCw" sourceNode="_pcXBkAGyEey7cfH5K6RyCw" targetNode="_e7ydoKA9EeuqkpDnuik1sg">
3194 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//IndividualDeclaration"/>
3195 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//IndividualDeclaration"/>
3196 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_s69PYAGyEey7cfH5K6RyCw" lineStyle="dash" targetArrow="InputClosedArrow" routingStyle="tree">
3197 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@conditionnalStyles.0/@style"/>
3198 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_s69PYQGyEey7cfH5K6RyCw" showIcon="false">
3199 <labelFormat>italic</labelFormat>
3200 </beginLabelStyle>
3201 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_s69PYgGyEey7cfH5K6RyCw" showIcon="false"/>
3202 </ownedStyle>
3203 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3204 </ownedDiagramElements>
3205 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_zJpmRgGyEey7cfH5K6RyCw" name="[0..*] nodes" sourceNode="_pcXBkAGyEey7cfH5K6RyCw" targetNode="_xsYrUKA8EeuqkpDnuik1sg">
3206 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//IndividualDeclaration/nodes"/>
3207 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//IndividualDeclaration/nodes"/>
3208 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_zJqNUAGyEey7cfH5K6RyCw" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
3209 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_zJqNUgGyEey7cfH5K6RyCw" showIcon="false">
3210 <customFeatures>labelSize</customFeatures>
3211 </centerLabelStyle>
3212 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_zJqNUQGyEey7cfH5K6RyCw" showIcon="false" labelColor="39,76,114">
3213 <customFeatures>labelSize</customFeatures>
3214 </endLabelStyle>
3215 </ownedStyle>
3216 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
3217 </ownedDiagramElements>
3218 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_SNSdcAGzEey7cfH5K6RyCw" name="WildcardAssertionArgument" tooltipText="" outgoingEdges="_WAUqNgGzEey7cfH5K6RyCw" width="12" height="10">
3219 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//WildcardAssertionArgument"/>
3220 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//WildcardAssertionArgument"/>
3221 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3222 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3223 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3224 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_SNSdcQGzEey7cfH5K6RyCw" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
3225 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
3226 </ownedStyle>
3227 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3228 </ownedDiagramElements>
3229 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_WAUqNgGzEey7cfH5K6RyCw" sourceNode="_SNSdcAGzEey7cfH5K6RyCw" targetNode="_ViJbUNeAEeufiOvRR5sVhg">
3230 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//WildcardAssertionArgument"/>
3231 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//WildcardAssertionArgument"/>
3232 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_WAUqNwGzEey7cfH5K6RyCw" targetArrow="InputClosedArrow" routingStyle="tree">
3233 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
3234 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_WAUqOAGzEey7cfH5K6RyCw" showIcon="false">
3235 <labelFormat>italic</labelFormat>
3236 </beginLabelStyle>
3237 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_WAUqOQGzEey7cfH5K6RyCw" showIcon="false"/>
3238 </ownedStyle>
3239 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3240 </ownedDiagramElements>
3241 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_A8hIkCrZEeyyC-O0_LlY9w" name="ParametricDefinition" tooltipText="" outgoingEdges="_vDscvKA6EeuqkpDnuik1sg _Uy4bWaA6EeuqkpDnuik1sg _mzziwKA9EeuqkpDnuik1sg" incomingEdges="_ddmjcCrZEeyyC-O0_LlY9w _eGZo7irZEeyyC-O0_LlY9w" width="12" height="10">
3242 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ParametricDefinition"/>
3243 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ParametricDefinition"/>
3244 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3245 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3246 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3247 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_DKdm0irZEeyyC-O0_LlY9w" iconPath="/org.eclipse.emf.ecoretools.design/icons/full/obj16/EClass_interface.gif" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="228,228,228">
3248 <labelFormat>italic</labelFormat>
3249 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@conditionnalStyles.0/@style"/>
3250 </ownedStyle>
3251 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3252 </ownedDiagramElements>
3253 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_NzpMACrZEeyyC-O0_LlY9w" name="RuleDefinition" tooltipText="" outgoingEdges="_eGZo7irZEeyyC-O0_LlY9w _WUsgHCrcEeyyC-O0_LlY9w _-hzfnCtaEeySS4mYSornnA" width="12" height="10">
3254 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//RuleDefinition"/>
3255 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//RuleDefinition"/>
3256 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3257 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3258 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3259 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_J0F0EyreEeyyC-O0_LlY9w" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
3260 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
3261 </ownedStyle>
3262 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3263 <ownedElements xmi:type="diagram:DNodeListElement" uid="_H_EMACreEeyyC-O0_LlY9w" name="kind : RuleKind = DIRECT" tooltipText="">
3264 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//RuleDefinition/kind"/>
3265 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//RuleDefinition/kind"/>
3266 <ownedStyle xmi:type="diagram:BundledImage" uid="_J0HpISreEeyyC-O0_LlY9w" labelAlignment="LEFT">
3267 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
3268 </ownedStyle>
3269 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
3270 </ownedElements>
3271 </ownedDiagramElements>
3272 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_ddmjcCrZEeyyC-O0_LlY9w" sourceNode="_fihqUKA5EeuqkpDnuik1sg" targetNode="_A8hIkCrZEeyyC-O0_LlY9w">
3273 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//PredicateDefinition"/>
3274 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//PredicateDefinition"/>
3275 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_-2bB8yrcEeyyC-O0_LlY9w" lineStyle="dash" targetArrow="InputClosedArrow" routingStyle="tree">
3276 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@conditionnalStyles.0/@style"/>
3277 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_-2bB9CrcEeyyC-O0_LlY9w" showIcon="false">
3278 <labelFormat>italic</labelFormat>
3279 </beginLabelStyle>
3280 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_-2bB9SrcEeyyC-O0_LlY9w" showIcon="false"/>
3281 </ownedStyle>
3282 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3283 </ownedDiagramElements>
3284 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_eGZo7irZEeyyC-O0_LlY9w" sourceNode="_NzpMACrZEeyyC-O0_LlY9w" targetNode="_A8hIkCrZEeyyC-O0_LlY9w">
3285 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//RuleDefinition"/>
3286 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//RuleDefinition"/>
3287 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_J0NI5ireEeyyC-O0_LlY9w" lineStyle="dash" targetArrow="InputClosedArrow" routingStyle="tree">
3288 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@conditionnalStyles.0/@style"/>
3289 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_J0NI5yreEeyyC-O0_LlY9w" showIcon="false">
3290 <labelFormat>italic</labelFormat>
3291 </beginLabelStyle>
3292 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_J0NI6CreEeyyC-O0_LlY9w" showIcon="false"/>
3293 </ownedStyle>
3294 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3295 </ownedDiagramElements>
3296 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_jzRFACrZEeyyC-O0_LlY9w" name="Action" tooltipText="" outgoingEdges="_vfYG6CrZEeyyC-O0_LlY9w" incomingEdges="_-hzfnCtaEeySS4mYSornnA" width="12" height="10">
3297 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Action"/>
3298 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Action"/>
3299 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3300 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3301 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3302 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_jzRsECrZEeyyC-O0_LlY9w" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
3303 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
3304 </ownedStyle>
3305 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3306 </ownedDiagramElements>
3307 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_remZACrZEeyyC-O0_LlY9w" name="ActionLiteral" tooltipText="" incomingEdges="_vfYG6CrZEeyyC-O0_LlY9w _KrcPniraEeyyC-O0_LlY9w _LMS50CraEeyyC-O0_LlY9w _Lfp6vCraEeyyC-O0_LlY9w" width="12" height="10">
3308 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ActionLiteral"/>
3309 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ActionLiteral"/>
3310 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3311 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3312 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3313 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_swnaiirZEeyyC-O0_LlY9w" iconPath="/org.eclipse.emf.ecoretools.design/icons/full/obj16/EClass_abstract.gif" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="228,228,228">
3314 <labelFormat>italic</labelFormat>
3315 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@conditionnalStyles.1/@style"/>
3316 </ownedStyle>
3317 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3318 </ownedDiagramElements>
3319 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_vfYG6CrZEeyyC-O0_LlY9w" name="[0..*] actionLiterals" sourceNode="_jzRFACrZEeyyC-O0_LlY9w" targetNode="_remZACrZEeyyC-O0_LlY9w">
3320 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Action/actionLiterals"/>
3321 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//Action/actionLiterals"/>
3322 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_vfYt4CrZEeyyC-O0_LlY9w" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
3323 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_vfYt4irZEeyyC-O0_LlY9w" showIcon="false">
3324 <customFeatures>labelSize</customFeatures>
3325 </centerLabelStyle>
3326 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_vfYt4SrZEeyyC-O0_LlY9w" showIcon="false" labelColor="39,76,114">
3327 <customFeatures>labelSize</customFeatures>
3328 </endLabelStyle>
3329 </ownedStyle>
3330 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
3331 </ownedDiagramElements>
3332 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_3Z67gCrZEeyyC-O0_LlY9w" name="ValueActionLiteral" tooltipText="" outgoingEdges="_KrcPniraEeyyC-O0_LlY9w _MiDWeCrbEeyyC-O0_LlY9w" width="12" height="10">
3333 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ValueActionLiteral"/>
3334 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ValueActionLiteral"/>
3335 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3336 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3337 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3338 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_hymTwStbEeySS4mYSornnA" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
3339 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
3340 </ownedStyle>
3341 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3342 <ownedElements xmi:type="diagram:DNodeListElement" uid="_cSL_ICtbEeySS4mYSornnA" name="value : LogicValue = TRUE" tooltipText="">
3343 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//ValueActionLiteral/value"/>
3344 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//ValueActionLiteral/value"/>
3345 <ownedStyle xmi:type="diagram:BundledImage" uid="_d8_UEStbEeySS4mYSornnA" labelAlignment="LEFT">
3346 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
3347 </ownedStyle>
3348 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
3349 </ownedElements>
3350 <ownedElements xmi:type="diagram:DNodeListElement" uid="_e16gICtbEeySS4mYSornnA" name="refinement : EBoolean = FALSE" tooltipText="">
3351 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//ValueActionLiteral/refinement"/>
3352 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//ValueActionLiteral/refinement"/>
3353 <ownedStyle xmi:type="diagram:BundledImage" uid="_hypXAitbEeySS4mYSornnA" labelAlignment="LEFT">
3354 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
3355 </ownedStyle>
3356 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
3357 </ownedElements>
3358 </ownedDiagramElements>
3359 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_9mHYACrZEeyyC-O0_LlY9w" name="DeleteActionLiteral" tooltipText="" outgoingEdges="_LMS50CraEeyyC-O0_LlY9w _btj3UirbEeyyC-O0_LlY9w" width="12" height="10">
3360 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//DeleteActionLiteral"/>
3361 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//DeleteActionLiteral"/>
3362 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3363 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3364 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3365 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_9mHYASrZEeyyC-O0_LlY9w" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
3366 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
3367 </ownedStyle>
3368 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3369 </ownedDiagramElements>
3370 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_AvrxACraEeyyC-O0_LlY9w" name="NewActionLiteral" tooltipText="" outgoingEdges="_Lfp6vCraEeyyC-O0_LlY9w _NXzvMiraEeyyC-O0_LlY9w" width="12" height="10">
3371 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NewActionLiteral"/>
3372 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NewActionLiteral"/>
3373 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3374 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3375 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3376 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_AvsYECraEeyyC-O0_LlY9w" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
3377 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
3378 </ownedStyle>
3379 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3380 </ownedDiagramElements>
3381 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_Fl5SACraEeyyC-O0_LlY9w" name="NewVariable" tooltipText="" outgoingEdges="_0KiRnCrdEeyyC-O0_LlY9w" incomingEdges="_NXzvMiraEeyyC-O0_LlY9w" width="12" height="10">
3382 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NewVariable"/>
3383 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NewVariable"/>
3384 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3385 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3386 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3387 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_Fl55ECraEeyyC-O0_LlY9w" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
3388 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
3389 </ownedStyle>
3390 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3391 </ownedDiagramElements>
3392 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_KrcPniraEeyyC-O0_LlY9w" sourceNode="_3Z67gCrZEeyyC-O0_LlY9w" targetNode="_remZACrZEeyyC-O0_LlY9w">
3393 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ValueActionLiteral"/>
3394 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ValueActionLiteral"/>
3395 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_hyx6FitbEeySS4mYSornnA" targetArrow="InputClosedArrow" routingStyle="tree">
3396 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
3397 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_hyx6FytbEeySS4mYSornnA" showIcon="false">
3398 <labelFormat>italic</labelFormat>
3399 </beginLabelStyle>
3400 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_hyx6GCtbEeySS4mYSornnA" showIcon="false"/>
3401 </ownedStyle>
3402 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3403 </ownedDiagramElements>
3404 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_LMS50CraEeyyC-O0_LlY9w" sourceNode="_9mHYACrZEeyyC-O0_LlY9w" targetNode="_remZACrZEeyyC-O0_LlY9w">
3405 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//DeleteActionLiteral"/>
3406 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//DeleteActionLiteral"/>
3407 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_LMS50SraEeyyC-O0_LlY9w" targetArrow="InputClosedArrow" routingStyle="tree">
3408 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
3409 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_LMS50iraEeyyC-O0_LlY9w" showIcon="false">
3410 <labelFormat>italic</labelFormat>
3411 </beginLabelStyle>
3412 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_LMS50yraEeyyC-O0_LlY9w" showIcon="false"/>
3413 </ownedStyle>
3414 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3415 </ownedDiagramElements>
3416 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_Lfp6vCraEeyyC-O0_LlY9w" sourceNode="_AvrxACraEeyyC-O0_LlY9w" targetNode="_remZACrZEeyyC-O0_LlY9w">
3417 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NewActionLiteral"/>
3418 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NewActionLiteral"/>
3419 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_Lfp6vSraEeyyC-O0_LlY9w" targetArrow="InputClosedArrow" routingStyle="tree">
3420 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
3421 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_Lfp6viraEeyyC-O0_LlY9w" showIcon="false">
3422 <labelFormat>italic</labelFormat>
3423 </beginLabelStyle>
3424 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_Lfp6vyraEeyyC-O0_LlY9w" showIcon="false"/>
3425 </ownedStyle>
3426 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3427 </ownedDiagramElements>
3428 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_NXzvMiraEeyyC-O0_LlY9w" name="[0..1] variable" sourceNode="_AvrxACraEeyyC-O0_LlY9w" targetNode="_Fl5SACraEeyyC-O0_LlY9w">
3429 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//NewActionLiteral/variable"/>
3430 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//NewActionLiteral/variable"/>
3431 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_NXzvMyraEeyyC-O0_LlY9w" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
3432 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_NXzvNSraEeyyC-O0_LlY9w" showIcon="false">
3433 <customFeatures>labelSize</customFeatures>
3434 </centerLabelStyle>
3435 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_NXzvNCraEeyyC-O0_LlY9w" showIcon="false" labelColor="39,76,114">
3436 <customFeatures>labelSize</customFeatures>
3437 </endLabelStyle>
3438 </ownedStyle>
3439 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
3440 </ownedDiagramElements>
3441 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_XLJ4gCraEeyyC-O0_LlY9w" name="CompoundLiteral" tooltipText="" outgoingEdges="_UJJy0qBDEeuqkpDnuik1sg _9OddBqA7EeuqkpDnuik1sg" incomingEdges="_5CBsdCraEeyyC-O0_LlY9w _5jFyVCraEeyyC-O0_LlY9w" width="12" height="10">
3442 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//CompoundLiteral"/>
3443 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//CompoundLiteral"/>
3444 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3445 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3446 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3447 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_aK_KECreEeyyC-O0_LlY9w" iconPath="/org.eclipse.emf.ecoretools.design/icons/full/obj16/EClass_abstract.gif" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="228,228,228">
3448 <labelFormat>italic</labelFormat>
3449 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@conditionnalStyles.1/@style"/>
3450 </ownedStyle>
3451 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3452 </ownedDiagramElements>
3453 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_jjUIACraEeyyC-O0_LlY9w" name="ValueLiteral" tooltipText="" outgoingEdges="_5CBsdCraEeyyC-O0_LlY9w _td_zEirbEeyyC-O0_LlY9w" width="12" height="10">
3454 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ValueLiteral"/>
3455 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ValueLiteral"/>
3456 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3457 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3458 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3459 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_1LZjoirbEeyyC-O0_LlY9w" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
3460 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
3461 </ownedStyle>
3462 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3463 <ownedElements xmi:type="diagram:DNodeListElement" uid="_z_B3ECrbEeyyC-O0_LlY9w" name="refinement : EBoolean = false" tooltipText="">
3464 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//ValueLiteral/refinement"/>
3465 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//ValueLiteral/refinement"/>
3466 <ownedStyle xmi:type="diagram:BundledImage" uid="_1LaxsirbEeyyC-O0_LlY9w" labelAlignment="LEFT">
3467 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
3468 </ownedStyle>
3469 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
3470 </ownedElements>
3471 </ownedDiagramElements>
3472 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_5CBsdCraEeyyC-O0_LlY9w" sourceNode="_jjUIACraEeyyC-O0_LlY9w" targetNode="_XLJ4gCraEeyyC-O0_LlY9w">
3473 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ValueLiteral"/>
3474 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ValueLiteral"/>
3475 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_1LgRUirbEeyyC-O0_LlY9w" targetArrow="InputClosedArrow" routingStyle="tree">
3476 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
3477 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_1LgRUyrbEeyyC-O0_LlY9w" showIcon="false">
3478 <labelFormat>italic</labelFormat>
3479 </beginLabelStyle>
3480 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_1LgRVCrbEeyyC-O0_LlY9w" showIcon="false"/>
3481 </ownedStyle>
3482 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3483 </ownedDiagramElements>
3484 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_5jFyVCraEeyyC-O0_LlY9w" sourceNode="_V6YZcKA7EeuqkpDnuik1sg" targetNode="_XLJ4gCraEeyyC-O0_LlY9w">
3485 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NegativeLiteral"/>
3486 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NegativeLiteral"/>
3487 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_5jFyVSraEeyyC-O0_LlY9w" targetArrow="InputClosedArrow" routingStyle="tree">
3488 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
3489 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_5jFyViraEeyyC-O0_LlY9w" showIcon="false">
3490 <labelFormat>italic</labelFormat>
3491 </beginLabelStyle>
3492 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_5jFyVyraEeyyC-O0_LlY9w" showIcon="false"/>
3493 </ownedStyle>
3494 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3495 </ownedDiagramElements>
3496 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_MiDWeCrbEeyyC-O0_LlY9w" name="[0..1] atom" sourceNode="_3Z67gCrZEeyyC-O0_LlY9w" targetNode="_6J_bsKA6EeuqkpDnuik1sg">
3497 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ValueActionLiteral/atom"/>
3498 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ValueActionLiteral/atom"/>
3499 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_MiD9cCrbEeyyC-O0_LlY9w" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
3500 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_MiD9cirbEeyyC-O0_LlY9w" showIcon="false">
3501 <customFeatures>labelSize</customFeatures>
3502 </centerLabelStyle>
3503 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_MiD9cSrbEeyyC-O0_LlY9w" showIcon="false" labelColor="39,76,114">
3504 <customFeatures>labelSize</customFeatures>
3505 </endLabelStyle>
3506 </ownedStyle>
3507 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
3508 </ownedDiagramElements>
3509 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_btj3UirbEeyyC-O0_LlY9w" name="[0..1] variableOrNode" sourceNode="_9mHYACrZEeyyC-O0_LlY9w" targetNode="_KwtCUNbNEeuymriYTNxK2g">
3510 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//DeleteActionLiteral/variableOrNode"/>
3511 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//DeleteActionLiteral/variableOrNode"/>
3512 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_btkeUCrbEeyyC-O0_LlY9w" routingStyle="manhattan" strokeColor="0,0,0">
3513 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']/@style"/>
3514 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_btkeUirbEeyyC-O0_LlY9w" showIcon="false">
3515 <customFeatures>labelSize</customFeatures>
3516 </centerLabelStyle>
3517 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_btkeUSrbEeyyC-O0_LlY9w" showIcon="false" labelColor="39,76,114">
3518 <customFeatures>labelSize</customFeatures>
3519 </endLabelStyle>
3520 </ownedStyle>
3521 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
3522 </ownedDiagramElements>
3523 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_q-nmgCrbEeyyC-O0_LlY9w" name="LogicConstant" tooltipText="" incomingEdges="_td_zEirbEeyyC-O0_LlY9w" width="12" height="10">
3524 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//LogicConstant"/>
3525 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//LogicConstant"/>
3526 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3527 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3528 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3529 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_IvqlQStcEeySS4mYSornnA" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
3530 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
3531 </ownedStyle>
3532 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
3533 <ownedElements xmi:type="diagram:DNodeListElement" uid="_GLYqoCtcEeySS4mYSornnA" name="value : LogicValue = TRUE" tooltipText="">
3534 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//LogicConstant/value"/>
3535 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//LogicConstant/value"/>
3536 <ownedStyle xmi:type="diagram:BundledImage" uid="_IvtBgStcEeySS4mYSornnA" labelAlignment="LEFT">
3537 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
3538 </ownedStyle>
3539 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
3540 </ownedElements>
3541 </ownedDiagramElements>
3542 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_td_zEirbEeyyC-O0_LlY9w" name="[0..*] values" sourceNode="_jjUIACraEeyyC-O0_LlY9w" targetNode="_q-nmgCrbEeyyC-O0_LlY9w">
3543 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ValueLiteral/values"/>
3544 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ValueLiteral/values"/>
3545 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_td_zEyrbEeyyC-O0_LlY9w" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
3546 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_td_zFSrbEeyyC-O0_LlY9w" showIcon="false">
3547 <customFeatures>labelSize</customFeatures>
3548 </centerLabelStyle>
3549 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_td_zFCrbEeyyC-O0_LlY9w" showIcon="false" labelColor="39,76,114">
3550 <customFeatures>labelSize</customFeatures>
3551 </endLabelStyle>
3552 </ownedStyle>
3553 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
3554 </ownedDiagramElements>
3555 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_LO_WkCrcEeyyC-O0_LlY9w" name="PredicateKind" tooltipText="" width="12" height="10">
3556 <target xmi:type="ecore:EEnum" href="src/main/resources/model/problem.ecore#//PredicateKind"/>
3557 <semanticElements xmi:type="ecore:EEnum" href="src/main/resources/model/problem.ecore#//PredicateKind"/>
3558 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3559 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3560 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3561 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_LO_9oCrcEeyyC-O0_LlY9w" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="221,236,202">
3562 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@style"/>
3563 </ownedStyle>
3564 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']"/>
3565 <ownedElements xmi:type="diagram:DNodeListElement" uid="_M8yVICrcEeyyC-O0_LlY9w" name="PARTIAL" tooltipText="">
3566 <target xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//PredicateKind/PARTIAL"/>
3567 <semanticElements xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//PredicateKind/PARTIAL"/>
3568 <ownedStyle xmi:type="diagram:BundledImage" uid="_M8yVISrcEeyyC-O0_LlY9w" labelAlignment="LEFT">
3569 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']/@style"/>
3570 </ownedStyle>
3571 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/>
3572 </ownedElements>
3573 <ownedElements xmi:type="diagram:DNodeListElement" uid="_OHUOkCrcEeyyC-O0_LlY9w" name="DIRECT" tooltipText="">
3574 <target xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//PredicateKind/DIRECT"/>
3575 <semanticElements xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//PredicateKind/DIRECT"/>
3576 <ownedStyle xmi:type="diagram:BundledImage" uid="_OHUOkSrcEeyyC-O0_LlY9w" labelAlignment="LEFT">
3577 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']/@style"/>
3578 </ownedStyle>
3579 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/>
3580 </ownedElements>
3581 </ownedDiagramElements>
3582 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_PSV3QCrcEeyyC-O0_LlY9w" name="RuleKind" tooltipText="" width="12" height="10">
3583 <target xmi:type="ecore:EEnum" href="src/main/resources/model/problem.ecore#//RuleKind"/>
3584 <semanticElements xmi:type="ecore:EEnum" href="src/main/resources/model/problem.ecore#//RuleKind"/>
3585 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
3586 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
3587 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
3588 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_PSV3QSrcEeyyC-O0_LlY9w" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="221,236,202">
3589 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@style"/>
3590 </ownedStyle>
3591 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']"/>
3592 <ownedElements xmi:type="diagram:DNodeListElement" uid="_QZVUgCrcEeyyC-O0_LlY9w" name="DIRECT" tooltipText="">
3593 <target xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//RuleKind/DIRECT"/>
3594 <semanticElements xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//RuleKind/DIRECT"/>
3595 <ownedStyle xmi:type="diagram:BundledImage" uid="_QZV7kCrcEeyyC-O0_LlY9w" labelAlignment="LEFT">
3596 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']/@style"/>
3597 </ownedStyle>
3598 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/>
3599 </ownedElements>
3600 </ownedDiagramElements>
3601 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_WUsgHCrcEeyyC-O0_LlY9w" sourceNode="_NzpMACrZEeyyC-O0_LlY9w" targetNode="_RzK-YKA5EeuqkpDnuik1sg">
3602 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//RuleDefinition"/>
3603 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//RuleDefinition"/>
3604 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_J0NI7CreEeyyC-O0_LlY9w" targetArrow="InputClosedArrow" routingStyle="tree">
3605 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
3606 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_J0NI7SreEeyyC-O0_LlY9w" showIcon="false">
3607 <labelFormat>italic</labelFormat>
3608 </beginLabelStyle>
3609 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_J0NI7ireEeyyC-O0_LlY9w" showIcon="false"/>
3610 </ownedStyle>
3611 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3612 </ownedDiagramElements>
3613 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_0KiRnCrdEeyyC-O0_LlY9w" sourceNode="_Fl5SACraEeyyC-O0_LlY9w" targetNode="_jPpm4KA6EeuqkpDnuik1sg">
3614 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NewVariable"/>
3615 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//NewVariable"/>
3616 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_0KiRnSrdEeyyC-O0_LlY9w" targetArrow="InputClosedArrow" routingStyle="manhattan">
3617 <customFeatures>routingStyle</customFeatures>
3618 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@style"/>
3619 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_0KiRnirdEeyyC-O0_LlY9w" showIcon="false">
3620 <labelFormat>italic</labelFormat>
3621 </beginLabelStyle>
3622 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_0KiRnyrdEeyyC-O0_LlY9w" showIcon="false"/>
3623 </ownedStyle>
3624 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
3625 </ownedDiagramElements>
3626 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_-hzfnCtaEeySS4mYSornnA" name="[0..1] action" sourceNode="_NzpMACrZEeyyC-O0_LlY9w" targetNode="_jzRFACrZEeyyC-O0_LlY9w">
3627 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//RuleDefinition/action"/>
3628 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//RuleDefinition/action"/>
3629 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_-h0GoCtaEeySS4mYSornnA" description="_L-JhMKA4EeuqkpDnuik1sg" sourceArrow="FillDiamond" routingStyle="manhattan" strokeColor="0,0,0">
3630 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_-h0GoitaEeySS4mYSornnA" showIcon="false">
3631 <customFeatures>labelSize</customFeatures>
3632 </centerLabelStyle>
3633 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_-h0GoStaEeySS4mYSornnA" showIcon="false" labelColor="39,76,114">
3634 <customFeatures>labelSize</customFeatures>
3635 </endLabelStyle>
3636 </ownedStyle>
3637 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
3638 </ownedDiagramElements>
3639 <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']"/>
3640 <filterVariableHistory xmi:type="diagram:FilterVariableHistory" uid="_CsWlsKA4EeuqkpDnuik1sg"/>
3641 <activatedLayers xmi:type="description_1:Layer" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer"/>
3642 <activatedLayers xmi:type="description_1:AdditionalLayer" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@additionalLayers[name='Package']"/>
3643 <activatedLayers xmi:type="description_1:AdditionalLayer" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@additionalLayers[name='Validation']"/>
3644 <target xmi:type="ecore:EPackage" href="src/main/resources/model/problem.ecore#/"/>
3645 </diagram:DSemanticDiagram>
3646</xmi:XMI>
diff --git a/subprojects/language-model/src/main/java/tools/refinery/language/model/GenerateProblemModel.mwe2 b/subprojects/language-model/src/main/java/tools/refinery/language/model/GenerateProblemModel.mwe2
new file mode 100644
index 00000000..15198d69
--- /dev/null
+++ b/subprojects/language-model/src/main/java/tools/refinery/language/model/GenerateProblemModel.mwe2
@@ -0,0 +1,20 @@
1module tools.refinery.language.model.GenerateProblemModel
2
3Workflow {
4 bean = org.eclipse.emf.mwe.utils.StandaloneSetup {
5 projectMapping = {
6 projectName = "tools.refinery.refinery-language-model"
7 path = "."
8 }
9 }
10
11 component = org.eclipse.emf.mwe.utils.DirectoryCleaner {
12 directory = "src/main/emf-gen"
13 }
14
15 component = org.eclipse.emf.mwe2.ecore.EcoreGenerator {
16 generateCustomClasses = false
17 genModel = "platform:/resource/tools.refinery.refinery-language-model/src/main/resources/model/problem.genmodel"
18 srcPath = "platform:/resource/tools.refinery.refinery-language-model/src/main/emf-gen"
19 }
20}
diff --git a/subprojects/language-model/src/main/java/tools/refinery/language/model/ProblemEMFSetup.java b/subprojects/language-model/src/main/java/tools/refinery/language/model/ProblemEMFSetup.java
new file mode 100644
index 00000000..9383098b
--- /dev/null
+++ b/subprojects/language-model/src/main/java/tools/refinery/language/model/ProblemEMFSetup.java
@@ -0,0 +1,34 @@
1package tools.refinery.language.model;
2
3import org.eclipse.emf.ecore.EPackage;
4import org.eclipse.emf.ecore.resource.Resource;
5
6import tools.refinery.language.model.problem.ProblemPackage;
7import tools.refinery.language.model.problem.impl.ProblemFactoryImpl;
8
9public class ProblemEMFSetup {
10 public static final String XMI_RESOURCE_EXTENSION = "problem_xmi";
11
12 private ProblemEMFSetup() {
13 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
14 }
15
16 // Here we can't rely on java.util.HashMap#putIfAbsent, because
17 // org.eclipse.emf.ecore.impl.EPackageRegistryImpl#containsKey is overridden
18 // without also overriding putIfAbsent. We must make sure to call the
19 // overridden containsKey implementation.
20 @SuppressWarnings("squid:S3824")
21 public static void doEMFRegistration() {
22 if (!EPackage.Registry.INSTANCE.containsKey(ProblemPackage.eNS_URI)) {
23 EPackage.Registry.INSTANCE.put(ProblemPackage.eNS_URI, ProblemPackage.eINSTANCE);
24 }
25
26 // This Resource.Factory is not actually used once
27 // tools.refinery.language.ProblemStandaloneSetup.createInjectorAndDoEMFRegistration()
28 // is called, because if will be replaced by
29 // tools.refinery.language.resource.ProblemXmiResourceFactory, which implements
30 // org.eclipse.xtext.resource.IResourceFactory as required by Xtext.
31 Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().putIfAbsent(XMI_RESOURCE_EXTENSION,
32 new ProblemFactoryImpl());
33 }
34}
diff --git a/subprojects/language-model/src/main/java/tools/refinery/language/model/ProblemUtil.java b/subprojects/language-model/src/main/java/tools/refinery/language/model/ProblemUtil.java
new file mode 100644
index 00000000..d8958381
--- /dev/null
+++ b/subprojects/language-model/src/main/java/tools/refinery/language/model/ProblemUtil.java
@@ -0,0 +1,121 @@
1package tools.refinery.language.model;
2
3import java.util.ArrayDeque;
4import java.util.Collection;
5import java.util.Deque;
6import java.util.HashSet;
7import java.util.Optional;
8import java.util.Set;
9
10import org.eclipse.emf.common.util.URI;
11import org.eclipse.emf.ecore.EObject;
12import org.eclipse.emf.ecore.resource.Resource;
13
14import tools.refinery.language.model.problem.ClassDeclaration;
15import tools.refinery.language.model.problem.ImplicitVariable;
16import tools.refinery.language.model.problem.Node;
17import tools.refinery.language.model.problem.Problem;
18import tools.refinery.language.model.problem.ProblemPackage;
19import tools.refinery.language.model.problem.ReferenceDeclaration;
20import tools.refinery.language.model.problem.Relation;
21import tools.refinery.language.model.problem.Variable;
22
23public final class ProblemUtil {
24 public static final String BUILTIN_LIBRARY_NAME = "builtin";
25
26 public static final URI BUILTIN_LIBRARY_URI = getLibraryUri(BUILTIN_LIBRARY_NAME);
27
28 public static final String NODE_CLASS_NAME = "node";
29
30 private ProblemUtil() {
31 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
32 }
33
34 public static boolean isSingletonVariable(Variable variable) {
35 return variable.eContainingFeature() == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__SINGLETON_VARIABLE;
36 }
37
38 public static boolean isImplicitVariable(Variable variable) {
39 return variable instanceof ImplicitVariable;
40 }
41
42 public static boolean isImplicitNode(Node node) {
43 return node.eContainingFeature() == ProblemPackage.Literals.PROBLEM__NODES;
44 }
45
46 public static boolean isImplicit(EObject eObject) {
47 if (eObject instanceof Node node) {
48 return isImplicitNode(node);
49 } else if (eObject instanceof Variable variable) {
50 return isImplicitVariable(variable);
51 } else {
52 return false;
53 }
54 }
55
56 public static boolean isIndividualNode(Node node) {
57 var containingFeature = node.eContainingFeature();
58 return containingFeature == ProblemPackage.Literals.INDIVIDUAL_DECLARATION__NODES
59 || containingFeature == ProblemPackage.Literals.ENUM_DECLARATION__LITERALS;
60 }
61
62 public static boolean isNewNode(Node node) {
63 return node.eContainingFeature() == ProblemPackage.Literals.CLASS_DECLARATION__NEW_NODE;
64 }
65
66 public static Optional<Problem> getBuiltInLibrary(EObject context) {
67 return Optional.ofNullable(context.eResource()).map(Resource::getResourceSet)
68 .map(resourceSet -> resourceSet.getResource(BUILTIN_LIBRARY_URI, true)).map(Resource::getContents)
69 .filter(contents -> !contents.isEmpty()).map(contents -> contents.get(0))
70 .filter(Problem.class::isInstance).map(Problem.class::cast);
71 }
72
73 public static boolean isBuiltIn(EObject eObject) {
74 if (eObject != null) {
75 var eResource = eObject.eResource();
76 if (eResource != null) {
77 return BUILTIN_LIBRARY_URI.equals(eResource.getURI());
78 }
79 }
80 return false;
81 }
82
83 public static Optional<ClassDeclaration> getNodeClassDeclaration(EObject context) {
84 return getBuiltInLibrary(context).flatMap(problem -> problem.getStatements().stream()
85 .filter(ClassDeclaration.class::isInstance).map(ClassDeclaration.class::cast)
86 .filter(declaration -> NODE_CLASS_NAME.equals(declaration.getName())).findFirst());
87 }
88
89 public static Collection<ClassDeclaration> getSuperclassesAndSelf(ClassDeclaration classDeclaration) {
90 Set<ClassDeclaration> found = new HashSet<>();
91 getNodeClassDeclaration(classDeclaration).ifPresent(found::add);
92 Deque<ClassDeclaration> queue = new ArrayDeque<>();
93 queue.addLast(classDeclaration);
94 while (!queue.isEmpty()) {
95 ClassDeclaration current = queue.removeFirst();
96 if (!found.contains(current)) {
97 found.add(current);
98 for (Relation superType : current.getSuperTypes()) {
99 if (superType instanceof ClassDeclaration superDeclaration) {
100 queue.addLast(superDeclaration);
101 }
102 }
103 }
104 }
105 return found;
106 }
107
108 public static Collection<ReferenceDeclaration> getAllReferenceDeclarations(ClassDeclaration classDeclaration) {
109 Set<ReferenceDeclaration> referenceDeclarations = new HashSet<>();
110 for (ClassDeclaration superclass : getSuperclassesAndSelf(classDeclaration)) {
111 referenceDeclarations.addAll(superclass.getReferenceDeclarations());
112 }
113 return referenceDeclarations;
114 }
115
116 private static URI getLibraryUri(String libraryName) {
117 return URI.createURI(ProblemUtil.class.getClassLoader()
118 .getResource("model/" + libraryName + "." + ProblemEMFSetup.XMI_RESOURCE_EXTENSION)
119 .toString());
120 }
121}
diff --git a/subprojects/language-model/src/main/resources/model/builtin.problem_xmi b/subprojects/language-model/src/main/resources/model/builtin.problem_xmi
new file mode 100644
index 00000000..9255ab66
--- /dev/null
+++ b/subprojects/language-model/src/main/resources/model/builtin.problem_xmi
@@ -0,0 +1,67 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<problem:Problem
3 xmi:version="2.0"
4 xmlns:xmi="http://www.omg.org/XMI"
5 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6 xmlns:problem="https://refinery.tools/emf/2021/Problem"
7 xsi:schemaLocation="https://refinery.tools/emf/2021/Problem problem.ecore"
8 name="builtin">
9 <statements
10 xsi:type="problem:ClassDeclaration"
11 name="node"
12 abstract="true">
13 <referenceDeclarations
14 name="equals"
15 referenceType="//@statements.0"
16 opposite="//@statements.0/@referenceDeclarations.0">
17 <multiplicity
18 xsi:type="problem:UnboundedMultiplicity"/>
19 </referenceDeclarations>
20 </statements>
21 <statements
22 xsi:type="problem:PredicateDefinition"
23 name="exists">
24 <parameters
25 name="node"
26 parameterType="//@statements.0"/>
27 </statements>
28 <statements
29 xsi:type="problem:ClassDeclaration"
30 name="domain"
31 abstract="true"
32 superTypes="//@statements.0"/>
33 <statements
34 xsi:type="problem:ClassDeclaration"
35 name="data"
36 abstract="true"
37 superTypes="//@statements.0"/>
38 <statements
39 xsi:type="problem:EnumDeclaration"
40 name="bool">
41 <literals
42 name="false"/>
43 <literals
44 name="true"/>
45 </statements>
46 <statements
47 xsi:type="problem:ClassDeclaration"
48 name="real"
49 superTypes="//@statements.3">
50 <newNode
51 name="new"/>
52 </statements>
53 <statements
54 xsi:type="problem:ClassDeclaration"
55 name="int"
56 superTypes="//@statements.3">
57 <newNode
58 name="new"/>
59 </statements>
60 <statements
61 xsi:type="problem:ClassDeclaration"
62 name="string"
63 superTypes="//@statements.3">
64 <newNode
65 name="new"/>
66 </statements>
67</problem:Problem>
diff --git a/subprojects/language-model/src/main/resources/model/problem.ecore b/subprojects/language-model/src/main/resources/model/problem.ecore
new file mode 100644
index 00000000..582f67c8
--- /dev/null
+++ b/subprojects/language-model/src/main/resources/model/problem.ecore
@@ -0,0 +1,191 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="problem" nsURI="https://refinery.tools/emf/2021/Problem" nsPrefix="problem">
4 <eClassifiers xsi:type="ecore:EClass" name="Problem" eSuperTypes="#//NamedElement">
5 <eStructuralFeatures xsi:type="ecore:EReference" name="nodes" upperBound="-1"
6 eType="#//Node" transient="true" containment="true"/>
7 <eStructuralFeatures xsi:type="ecore:EReference" name="statements" upperBound="-1"
8 eType="#//Statement" containment="true"/>
9 </eClassifiers>
10 <eClassifiers xsi:type="ecore:EClass" name="Relation" abstract="true" eSuperTypes="#//NamedElement"/>
11 <eClassifiers xsi:type="ecore:EClass" name="ClassDeclaration" eSuperTypes="#//Relation #//Statement">
12 <eStructuralFeatures xsi:type="ecore:EAttribute" name="abstract" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
13 <eStructuralFeatures xsi:type="ecore:EReference" name="superTypes" upperBound="-1"
14 eType="#//Relation"/>
15 <eStructuralFeatures xsi:type="ecore:EReference" name="referenceDeclarations"
16 upperBound="-1" eType="#//ReferenceDeclaration" containment="true"/>
17 <eStructuralFeatures xsi:type="ecore:EReference" name="newNode" eType="#//Node"
18 transient="true" containment="true"/>
19 </eClassifiers>
20 <eClassifiers xsi:type="ecore:EClass" name="ReferenceDeclaration" eSuperTypes="#//Relation">
21 <eStructuralFeatures xsi:type="ecore:EReference" name="referenceType" eType="#//Relation"/>
22 <eStructuralFeatures xsi:type="ecore:EReference" name="opposite" eType="#//ReferenceDeclaration"/>
23 <eStructuralFeatures xsi:type="ecore:EAttribute" name="containment" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
24 <eStructuralFeatures xsi:type="ecore:EReference" name="multiplicity" eType="#//Multiplicity"
25 containment="true"/>
26 </eClassifiers>
27 <eClassifiers xsi:type="ecore:EClass" name="NamedElement" abstract="true">
28 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
29 </eClassifiers>
30 <eClassifiers xsi:type="ecore:EClass" name="PredicateDefinition" eSuperTypes="#//Relation #//ParametricDefinition">
31 <eStructuralFeatures xsi:type="ecore:EAttribute" name="error" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
32 <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//PredicateKind"
33 defaultValueLiteral="PARTIAL"/>
34 </eClassifiers>
35 <eClassifiers xsi:type="ecore:EClass" name="Parameter" eSuperTypes="#//Variable">
36 <eStructuralFeatures xsi:type="ecore:EReference" name="parameterType" eType="#//Relation"/>
37 </eClassifiers>
38 <eClassifiers xsi:type="ecore:EClass" name="Variable" abstract="true" eSuperTypes="#//VariableOrNode"/>
39 <eClassifiers xsi:type="ecore:EClass" name="Conjunction" eSuperTypes="#//ExistentialQuantifier">
40 <eStructuralFeatures xsi:type="ecore:EReference" name="literals" upperBound="-1"
41 eType="#//Literal" containment="true"/>
42 </eClassifiers>
43 <eClassifiers xsi:type="ecore:EClass" name="Literal" abstract="true"/>
44 <eClassifiers xsi:type="ecore:EClass" name="Atom" eSuperTypes="#//Literal">
45 <eStructuralFeatures xsi:type="ecore:EReference" name="relation" eType="#//Relation"/>
46 <eStructuralFeatures xsi:type="ecore:EAttribute" name="transitiveClosure" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
47 <eStructuralFeatures xsi:type="ecore:EReference" name="arguments" upperBound="-1"
48 eType="#//Argument" containment="true"/>
49 </eClassifiers>
50 <eClassifiers xsi:type="ecore:EClass" name="ImplicitVariable" eSuperTypes="#//Variable"/>
51 <eClassifiers xsi:type="ecore:EClass" name="NegativeLiteral" eSuperTypes="#//ExistentialQuantifier #//CompoundLiteral"/>
52 <eClassifiers xsi:type="ecore:EClass" name="ExistentialQuantifier" abstract="true"
53 interface="true">
54 <eStructuralFeatures xsi:type="ecore:EReference" name="implicitVariables" upperBound="-1"
55 eType="#//ImplicitVariable" transient="true" containment="true"/>
56 </eClassifiers>
57 <eClassifiers xsi:type="ecore:EClass" name="Assertion" eSuperTypes="#//Statement">
58 <eStructuralFeatures xsi:type="ecore:EReference" name="relation" eType="#//Relation"/>
59 <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="#//LogicValue"/>
60 <eStructuralFeatures xsi:type="ecore:EReference" name="arguments" upperBound="-1"
61 eType="#//AssertionArgument" containment="true"/>
62 <eStructuralFeatures xsi:type="ecore:EAttribute" name="default" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"
63 defaultValueLiteral="false"/>
64 </eClassifiers>
65 <eClassifiers xsi:type="ecore:EClass" name="Node" eSuperTypes="#//VariableOrNode"/>
66 <eClassifiers xsi:type="ecore:EEnum" name="LogicValue">
67 <eLiterals name="TRUE" literal="TRUE"/>
68 <eLiterals name="FALSE" value="1"/>
69 <eLiterals name="UNKNOWN" value="2"/>
70 <eLiterals name="ERROR" value="3"/>
71 </eClassifiers>
72 <eClassifiers xsi:type="ecore:EClass" name="ScopeDeclaration" eSuperTypes="#//Statement">
73 <eStructuralFeatures xsi:type="ecore:EReference" name="typeScopes" upperBound="-1"
74 eType="#//TypeScope" containment="true"/>
75 </eClassifiers>
76 <eClassifiers xsi:type="ecore:EClass" name="Statement" abstract="true" interface="true"/>
77 <eClassifiers xsi:type="ecore:EClass" name="TypeScope">
78 <eStructuralFeatures xsi:type="ecore:EReference" name="targetType" eType="#//ClassDeclaration"/>
79 <eStructuralFeatures xsi:type="ecore:EAttribute" name="increment" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
80 <eStructuralFeatures xsi:type="ecore:EReference" name="multiplicity" eType="#//Multiplicity"
81 containment="true"/>
82 </eClassifiers>
83 <eClassifiers xsi:type="ecore:EClass" name="Multiplicity" abstract="true"/>
84 <eClassifiers xsi:type="ecore:EClass" name="RangeMultiplicity" eSuperTypes="#//Multiplicity">
85 <eStructuralFeatures xsi:type="ecore:EAttribute" name="lowerBound" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"
86 defaultValueLiteral="0"/>
87 <eStructuralFeatures xsi:type="ecore:EAttribute" name="upperBound" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"
88 defaultValueLiteral="-1"/>
89 </eClassifiers>
90 <eClassifiers xsi:type="ecore:EClass" name="ExactMultiplicity" eSuperTypes="#//Multiplicity">
91 <eStructuralFeatures xsi:type="ecore:EAttribute" name="exactValue" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"
92 defaultValueLiteral="1"/>
93 </eClassifiers>
94 <eClassifiers xsi:type="ecore:EClass" name="UnboundedMultiplicity" eSuperTypes="#//Multiplicity"/>
95 <eClassifiers xsi:type="ecore:EClass" name="VariableOrNodeArgument" eSuperTypes="#//Argument">
96 <eStructuralFeatures xsi:type="ecore:EReference" name="variableOrNode" eType="#//VariableOrNode"/>
97 <eStructuralFeatures xsi:type="ecore:EReference" name="singletonVariable" eType="#//ImplicitVariable"
98 transient="true" containment="true"/>
99 </eClassifiers>
100 <eClassifiers xsi:type="ecore:EClass" name="EnumDeclaration" eSuperTypes="#//Relation #//Statement">
101 <eStructuralFeatures xsi:type="ecore:EReference" name="literals" upperBound="-1"
102 eType="#//Node" containment="true"/>
103 </eClassifiers>
104 <eClassifiers xsi:type="ecore:EClass" name="VariableOrNode" abstract="true" eSuperTypes="#//NamedElement"/>
105 <eClassifiers xsi:type="ecore:EClass" name="Constant" abstract="true"/>
106 <eClassifiers xsi:type="ecore:EClass" name="IntConstant" eSuperTypes="#//Constant">
107 <eStructuralFeatures xsi:type="ecore:EAttribute" name="intValue" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"
108 defaultValueLiteral="0"/>
109 </eClassifiers>
110 <eClassifiers xsi:type="ecore:EClass" name="RealConstant" eSuperTypes="#//Constant">
111 <eStructuralFeatures xsi:type="ecore:EAttribute" name="realValue" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
112 defaultValueLiteral="0.0"/>
113 </eClassifiers>
114 <eClassifiers xsi:type="ecore:EClass" name="StringConstant" eSuperTypes="#//Constant">
115 <eStructuralFeatures xsi:type="ecore:EAttribute" name="stringValue" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
116 </eClassifiers>
117 <eClassifiers xsi:type="ecore:EClass" name="ConstantArgument" eSuperTypes="#//Argument">
118 <eStructuralFeatures xsi:type="ecore:EReference" name="constant" eType="#//Constant"
119 containment="true"/>
120 </eClassifiers>
121 <eClassifiers xsi:type="ecore:EClass" name="Argument" abstract="true"/>
122 <eClassifiers xsi:type="ecore:EClass" name="NodeAssertionArgument" eSuperTypes="#//AssertionArgument">
123 <eStructuralFeatures xsi:type="ecore:EReference" name="node" eType="#//Node"/>
124 </eClassifiers>
125 <eClassifiers xsi:type="ecore:EClass" name="AssertionArgument" abstract="true"/>
126 <eClassifiers xsi:type="ecore:EClass" name="ConstantAssertionArgument" eSuperTypes="#//AssertionArgument">
127 <eStructuralFeatures xsi:type="ecore:EReference" name="constant" eType="#//Constant"
128 containment="true"/>
129 </eClassifiers>
130 <eClassifiers xsi:type="ecore:EClass" name="NodeValueAssertion" eSuperTypes="#//Statement">
131 <eStructuralFeatures xsi:type="ecore:EReference" name="node" eType="#//Node"/>
132 <eStructuralFeatures xsi:type="ecore:EReference" name="value" eType="#//Constant"
133 containment="true"/>
134 </eClassifiers>
135 <eClassifiers xsi:type="ecore:EClass" name="IndividualDeclaration" eSuperTypes="#//Statement">
136 <eStructuralFeatures xsi:type="ecore:EReference" name="nodes" upperBound="-1"
137 eType="#//Node" containment="true"/>
138 </eClassifiers>
139 <eClassifiers xsi:type="ecore:EClass" name="WildcardAssertionArgument" eSuperTypes="#//AssertionArgument"/>
140 <eClassifiers xsi:type="ecore:EClass" name="ParametricDefinition" abstract="true"
141 interface="true" eSuperTypes="#//Statement">
142 <eStructuralFeatures xsi:type="ecore:EReference" name="bodies" upperBound="-1"
143 eType="#//Conjunction" containment="true"/>
144 <eStructuralFeatures xsi:type="ecore:EReference" name="parameters" upperBound="-1"
145 eType="#//Parameter" containment="true"/>
146 </eClassifiers>
147 <eClassifiers xsi:type="ecore:EClass" name="RuleDefinition" eSuperTypes="#//ParametricDefinition #//NamedElement">
148 <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//RuleKind"
149 defaultValueLiteral="DIRECT"/>
150 <eStructuralFeatures xsi:type="ecore:EReference" name="action" eType="#//Action"
151 containment="true"/>
152 </eClassifiers>
153 <eClassifiers xsi:type="ecore:EClass" name="Action">
154 <eStructuralFeatures xsi:type="ecore:EReference" name="actionLiterals" upperBound="-1"
155 eType="#//ActionLiteral" containment="true"/>
156 </eClassifiers>
157 <eClassifiers xsi:type="ecore:EClass" name="ActionLiteral" abstract="true"/>
158 <eClassifiers xsi:type="ecore:EClass" name="ValueActionLiteral" eSuperTypes="#//ActionLiteral">
159 <eStructuralFeatures xsi:type="ecore:EReference" name="atom" eType="#//Atom" containment="true"/>
160 <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="#//LogicValue"
161 defaultValueLiteral="TRUE"/>
162 <eStructuralFeatures xsi:type="ecore:EAttribute" name="refinement" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"
163 defaultValueLiteral="FALSE"/>
164 </eClassifiers>
165 <eClassifiers xsi:type="ecore:EClass" name="DeleteActionLiteral" eSuperTypes="#//ActionLiteral">
166 <eStructuralFeatures xsi:type="ecore:EReference" name="variableOrNode" eType="#//VariableOrNode"/>
167 </eClassifiers>
168 <eClassifiers xsi:type="ecore:EClass" name="NewActionLiteral" eSuperTypes="#//ActionLiteral">
169 <eStructuralFeatures xsi:type="ecore:EReference" name="variable" eType="#//NewVariable"
170 containment="true"/>
171 </eClassifiers>
172 <eClassifiers xsi:type="ecore:EClass" name="NewVariable" eSuperTypes="#//Variable"/>
173 <eClassifiers xsi:type="ecore:EClass" name="CompoundLiteral" abstract="true" eSuperTypes="#//Literal">
174 <eStructuralFeatures xsi:type="ecore:EReference" name="atom" eType="#//Atom" containment="true"/>
175 </eClassifiers>
176 <eClassifiers xsi:type="ecore:EClass" name="ValueLiteral" eSuperTypes="#//CompoundLiteral">
177 <eStructuralFeatures xsi:type="ecore:EReference" name="values" upperBound="-1"
178 eType="#//LogicConstant" containment="true"/>
179 <eStructuralFeatures xsi:type="ecore:EAttribute" name="refinement" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
180 </eClassifiers>
181 <eClassifiers xsi:type="ecore:EClass" name="LogicConstant">
182 <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="#//LogicValue"/>
183 </eClassifiers>
184 <eClassifiers xsi:type="ecore:EEnum" name="PredicateKind">
185 <eLiterals name="PARTIAL"/>
186 <eLiterals name="DIRECT" value="1"/>
187 </eClassifiers>
188 <eClassifiers xsi:type="ecore:EEnum" name="RuleKind">
189 <eLiterals name="DIRECT"/>
190 </eClassifiers>
191</ecore:EPackage>
diff --git a/subprojects/language-model/src/main/resources/model/problem.genmodel b/subprojects/language-model/src/main/resources/model/problem.genmodel
new file mode 100644
index 00000000..9ba2274b
--- /dev/null
+++ b/subprojects/language-model/src/main/resources/model/problem.genmodel
@@ -0,0 +1,165 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<genmodel:GenModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:genmodel="http://www.eclipse.org/emf/2002/GenModel"
4 modelDirectory="/tools.refinery.refinery-language-model/src/main/emf-gen"
5 creationIcons="false" editDirectory="/language-edit/src/main/emf-gen" editorDirectory="/org.eclipse.viatra.solver.language.model.editor/src-gen"
6 modelPluginID="language-model" modelName="Problem" rootExtendsClass="org.eclipse.emf.ecore.impl.MinimalEObjectImpl$Container"
7 codeFormatting="true" importerID="org.eclipse.emf.importer.ecore" complianceLevel="5.0"
8 copyrightFields="false" operationReflection="true" importOrganizing="true">
9 <foreignModel>problem.ecore</foreignModel>
10 <testsDirectory xsi:nil="true"/>
11 <genPackages prefix="Problem" basePackage="tools.refinery.language.model" resource="XMI"
12 disposableProviderFactory="true" fileExtensions="problem_xmi" ecorePackage="problem.ecore#/">
13 <genEnums typeSafeEnumCompatible="false" ecoreEnum="problem.ecore#//LogicValue">
14 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//LogicValue/TRUE"/>
15 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//LogicValue/FALSE"/>
16 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//LogicValue/UNKNOWN"/>
17 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//LogicValue/ERROR"/>
18 </genEnums>
19 <genEnums typeSafeEnumCompatible="false" ecoreEnum="problem.ecore#//PredicateKind">
20 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//PredicateKind/PARTIAL"/>
21 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//PredicateKind/DIRECT"/>
22 </genEnums>
23 <genEnums typeSafeEnumCompatible="false" ecoreEnum="problem.ecore#//RuleKind">
24 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//RuleKind/DIRECT"/>
25 </genEnums>
26 <genClasses ecoreClass="problem.ecore#//Problem">
27 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//Problem/nodes"/>
28 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//Problem/statements"/>
29 </genClasses>
30 <genClasses ecoreClass="problem.ecore#//Relation"/>
31 <genClasses ecoreClass="problem.ecore#//ClassDeclaration">
32 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//ClassDeclaration/abstract"/>
33 <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//ClassDeclaration/superTypes"/>
34 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//ClassDeclaration/referenceDeclarations"/>
35 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//ClassDeclaration/newNode"/>
36 </genClasses>
37 <genClasses ecoreClass="problem.ecore#//ReferenceDeclaration">
38 <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//ReferenceDeclaration/referenceType"/>
39 <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//ReferenceDeclaration/opposite"/>
40 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//ReferenceDeclaration/containment"/>
41 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//ReferenceDeclaration/multiplicity"/>
42 </genClasses>
43 <genClasses ecoreClass="problem.ecore#//NamedElement">
44 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//NamedElement/name"/>
45 </genClasses>
46 <genClasses ecoreClass="problem.ecore#//PredicateDefinition">
47 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//PredicateDefinition/error"/>
48 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//PredicateDefinition/kind"/>
49 </genClasses>
50 <genClasses ecoreClass="problem.ecore#//Parameter">
51 <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//Parameter/parameterType"/>
52 </genClasses>
53 <genClasses ecoreClass="problem.ecore#//Variable"/>
54 <genClasses ecoreClass="problem.ecore#//Conjunction">
55 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//Conjunction/literals"/>
56 </genClasses>
57 <genClasses ecoreClass="problem.ecore#//Literal"/>
58 <genClasses ecoreClass="problem.ecore#//Atom">
59 <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//Atom/relation"/>
60 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//Atom/transitiveClosure"/>
61 <genFeatures children="true" createChild="true" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//Atom/arguments"/>
62 </genClasses>
63 <genClasses ecoreClass="problem.ecore#//ImplicitVariable"/>
64 <genClasses ecoreClass="problem.ecore#//NegativeLiteral"/>
65 <genClasses ecoreClass="problem.ecore#//ExistentialQuantifier">
66 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//ExistentialQuantifier/implicitVariables"/>
67 </genClasses>
68 <genClasses ecoreClass="problem.ecore#//Assertion">
69 <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//Assertion/relation"/>
70 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//Assertion/value"/>
71 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//Assertion/arguments"/>
72 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//Assertion/default"/>
73 </genClasses>
74 <genClasses ecoreClass="problem.ecore#//Node"/>
75 <genClasses ecoreClass="problem.ecore#//ScopeDeclaration">
76 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//ScopeDeclaration/typeScopes"/>
77 </genClasses>
78 <genClasses ecoreClass="problem.ecore#//Statement"/>
79 <genClasses ecoreClass="problem.ecore#//TypeScope">
80 <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//TypeScope/targetType"/>
81 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//TypeScope/increment"/>
82 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//TypeScope/multiplicity"/>
83 </genClasses>
84 <genClasses ecoreClass="problem.ecore#//Multiplicity"/>
85 <genClasses ecoreClass="problem.ecore#//RangeMultiplicity">
86 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//RangeMultiplicity/lowerBound"/>
87 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//RangeMultiplicity/upperBound"/>
88 </genClasses>
89 <genClasses ecoreClass="problem.ecore#//ExactMultiplicity">
90 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//ExactMultiplicity/exactValue"/>
91 </genClasses>
92 <genClasses ecoreClass="problem.ecore#//UnboundedMultiplicity"/>
93 <genClasses ecoreClass="problem.ecore#//VariableOrNodeArgument">
94 <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//VariableOrNodeArgument/variableOrNode"/>
95 <genFeatures children="true" createChild="true" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//VariableOrNodeArgument/singletonVariable"/>
96 </genClasses>
97 <genClasses ecoreClass="problem.ecore#//EnumDeclaration">
98 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//EnumDeclaration/literals"/>
99 </genClasses>
100 <genClasses ecoreClass="problem.ecore#//VariableOrNode"/>
101 <genClasses ecoreClass="problem.ecore#//Constant"/>
102 <genClasses ecoreClass="problem.ecore#//IntConstant">
103 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//IntConstant/intValue"/>
104 </genClasses>
105 <genClasses ecoreClass="problem.ecore#//RealConstant">
106 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//RealConstant/realValue"/>
107 </genClasses>
108 <genClasses ecoreClass="problem.ecore#//StringConstant">
109 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//StringConstant/stringValue"/>
110 </genClasses>
111 <genClasses ecoreClass="problem.ecore#//ConstantArgument">
112 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//ConstantArgument/constant"/>
113 </genClasses>
114 <genClasses ecoreClass="problem.ecore#//Argument"/>
115 <genClasses ecoreClass="problem.ecore#//NodeAssertionArgument">
116 <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//NodeAssertionArgument/node"/>
117 </genClasses>
118 <genClasses ecoreClass="problem.ecore#//AssertionArgument"/>
119 <genClasses ecoreClass="problem.ecore#//ConstantAssertionArgument">
120 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//ConstantAssertionArgument/constant"/>
121 </genClasses>
122 <genClasses ecoreClass="problem.ecore#//NodeValueAssertion">
123 <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//NodeValueAssertion/node"/>
124 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//NodeValueAssertion/value"/>
125 </genClasses>
126 <genClasses ecoreClass="problem.ecore#//IndividualDeclaration">
127 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//IndividualDeclaration/nodes"/>
128 </genClasses>
129 <genClasses ecoreClass="problem.ecore#//WildcardAssertionArgument"/>
130 <genClasses ecoreClass="problem.ecore#//ParametricDefinition">
131 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//ParametricDefinition/bodies"/>
132 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//ParametricDefinition/parameters"/>
133 </genClasses>
134 <genClasses ecoreClass="problem.ecore#//RuleDefinition">
135 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//RuleDefinition/kind"/>
136 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//RuleDefinition/action"/>
137 </genClasses>
138 <genClasses ecoreClass="problem.ecore#//Action">
139 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//Action/actionLiterals"/>
140 </genClasses>
141 <genClasses ecoreClass="problem.ecore#//ActionLiteral"/>
142 <genClasses ecoreClass="problem.ecore#//ValueActionLiteral">
143 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//ValueActionLiteral/atom"/>
144 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//ValueActionLiteral/value"/>
145 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//ValueActionLiteral/refinement"/>
146 </genClasses>
147 <genClasses ecoreClass="problem.ecore#//DeleteActionLiteral">
148 <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//DeleteActionLiteral/variableOrNode"/>
149 </genClasses>
150 <genClasses ecoreClass="problem.ecore#//NewActionLiteral">
151 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//NewActionLiteral/variable"/>
152 </genClasses>
153 <genClasses ecoreClass="problem.ecore#//NewVariable"/>
154 <genClasses ecoreClass="problem.ecore#//CompoundLiteral">
155 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//CompoundLiteral/atom"/>
156 </genClasses>
157 <genClasses ecoreClass="problem.ecore#//ValueLiteral">
158 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//ValueLiteral/values"/>
159 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//ValueLiteral/refinement"/>
160 </genClasses>
161 <genClasses ecoreClass="problem.ecore#//LogicConstant">
162 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//LogicConstant/value"/>
163 </genClasses>
164 </genPackages>
165</genmodel:GenModel>
diff --git a/subprojects/language-model/src/testFixtures/java/tools/refinery/language/model/tests/ProblemTestUtil.java b/subprojects/language-model/src/testFixtures/java/tools/refinery/language/model/tests/ProblemTestUtil.java
new file mode 100644
index 00000000..d0990dc0
--- /dev/null
+++ b/subprojects/language-model/src/testFixtures/java/tools/refinery/language/model/tests/ProblemTestUtil.java
@@ -0,0 +1,197 @@
1package tools.refinery.language.model.tests;
2
3import java.util.List;
4import java.util.stream.Stream;
5
6import org.eclipse.emf.ecore.resource.Resource.Diagnostic;
7import org.eclipse.emf.ecore.util.EcoreUtil;
8
9import tools.refinery.language.model.ProblemUtil;
10import tools.refinery.language.model.problem.ActionLiteral;
11import tools.refinery.language.model.problem.Argument;
12import tools.refinery.language.model.problem.Assertion;
13import tools.refinery.language.model.problem.AssertionArgument;
14import tools.refinery.language.model.problem.Atom;
15import tools.refinery.language.model.problem.ClassDeclaration;
16import tools.refinery.language.model.problem.Conjunction;
17import tools.refinery.language.model.problem.DeleteActionLiteral;
18import tools.refinery.language.model.problem.EnumDeclaration;
19import tools.refinery.language.model.problem.IndividualDeclaration;
20import tools.refinery.language.model.problem.Literal;
21import tools.refinery.language.model.problem.NamedElement;
22import tools.refinery.language.model.problem.NegativeLiteral;
23import tools.refinery.language.model.problem.NewActionLiteral;
24import tools.refinery.language.model.problem.Node;
25import tools.refinery.language.model.problem.NodeAssertionArgument;
26import tools.refinery.language.model.problem.NodeValueAssertion;
27import tools.refinery.language.model.problem.Parameter;
28import tools.refinery.language.model.problem.ParametricDefinition;
29import tools.refinery.language.model.problem.PredicateDefinition;
30import tools.refinery.language.model.problem.Problem;
31import tools.refinery.language.model.problem.ReferenceDeclaration;
32import tools.refinery.language.model.problem.Relation;
33import tools.refinery.language.model.problem.RuleDefinition;
34import tools.refinery.language.model.problem.Statement;
35import tools.refinery.language.model.problem.ValueActionLiteral;
36import tools.refinery.language.model.problem.ValueLiteral;
37import tools.refinery.language.model.problem.Variable;
38import tools.refinery.language.model.problem.VariableOrNode;
39import tools.refinery.language.model.problem.VariableOrNodeArgument;
40
41public class ProblemTestUtil {
42 public Problem builtin(Problem problem) {
43 return ProblemUtil.getBuiltInLibrary(problem).get();
44 }
45
46 public List<Diagnostic> errors(Problem problem) {
47 EcoreUtil.resolveAll(problem);
48 return problem.eResource().getErrors();
49 }
50
51 public List<String> nodeNames(Problem problem) {
52 return problem.getNodes().stream().map(node -> node.getName()).toList();
53 }
54
55 public PredicateDefinition pred(Problem problem, String name) {
56 return namedStatementOfType(problem, PredicateDefinition.class, name);
57 }
58
59 public RuleDefinition rule(Problem problem, String name) {
60 return namedStatementOfType(problem, RuleDefinition.class, name);
61 }
62
63 public Parameter param(ParametricDefinition definition, int i) {
64 return definition.getParameters().get(i);
65 }
66
67 public Conjunction conj(ParametricDefinition definition, int i) {
68 return definition.getBodies().get(i);
69 }
70
71 public Literal lit(Conjunction conjunction, int i) {
72 return conjunction.getLiterals().get(i);
73 }
74
75 public ActionLiteral actionLit(RuleDefinition rule, int i) {
76 return rule.getAction().getActionLiterals().get(i);
77 }
78
79 public Atom valueAtom(Literal literal) {
80 return ((ValueLiteral) literal).getAtom();
81 }
82
83 public Atom negated(Literal literal) {
84 return ((NegativeLiteral) literal).getAtom();
85 }
86
87 public Relation relation(Literal literal) {
88 return ((Atom) literal).getRelation();
89 }
90
91 public Argument arg(Atom atom, int i) {
92 return atom.getArguments().get(i);
93 }
94
95 public Argument arg(Literal literal, int i) {
96 return arg((Atom) literal, i);
97 }
98
99 public VariableOrNode variableOrNode(Argument argument) {
100 return ((VariableOrNodeArgument) argument).getVariableOrNode();
101 }
102
103 public Variable variable(Argument argument) {
104 return (Variable) variableOrNode(argument);
105 }
106
107 public Variable variable(ValueActionLiteral valueActionLiteral, int i) {
108 return variable(arg(valueActionLiteral.getAtom(), i));
109 }
110
111 public Variable variable(NewActionLiteral newActionLiteral) {
112 return newActionLiteral.getVariable();
113 }
114
115 public VariableOrNode deleteVar(ActionLiteral actionLiteral) {
116 return ((DeleteActionLiteral) actionLiteral).getVariableOrNode();
117 }
118
119 public VariableOrNode newVar(ActionLiteral actionLiteral) {
120 return ((NewActionLiteral) actionLiteral).getVariable();
121 }
122
123 public Atom valueAtom(ActionLiteral actionLiteral) {
124 return ((ValueActionLiteral) actionLiteral).getAtom();
125 }
126
127 public Variable variable(DeleteActionLiteral deleteActionLiteral) {
128 return (Variable) deleteActionLiteral.getVariableOrNode();
129 }
130
131 public Node node(Argument argument) {
132 return (Node) variableOrNode(argument);
133 }
134
135 public Assertion assertion(Problem problem, int i) {
136 return nthStatementOfType(problem, Assertion.class, i);
137 }
138
139 public AssertionArgument arg(Assertion assertion, int i) {
140 return assertion.getArguments().get(i);
141 }
142
143 public Node node(AssertionArgument argument) {
144 return ((NodeAssertionArgument) argument).getNode();
145 }
146
147 public Node node(Problem problem, String name) {
148 return named(problem.getNodes(), name);
149 }
150
151 public Node individualNode(Problem problem, String name) {
152 var uniqueNodes = statementsOfType(problem, IndividualDeclaration.class)
153 .flatMap(declaration -> declaration.getNodes().stream());
154 return named(uniqueNodes, name);
155 }
156
157 public NodeValueAssertion nodeValueAssertion(Problem problem, int i) {
158 return nthStatementOfType(problem, NodeValueAssertion.class, i);
159 }
160
161 public ClassDeclaration findClass(Problem problem, String name) {
162 return namedStatementOfType(problem, ClassDeclaration.class, name);
163 }
164
165 public ReferenceDeclaration reference(ClassDeclaration declaration, String name) {
166 return named(declaration.getReferenceDeclarations(), name);
167 }
168
169 public EnumDeclaration findEnum(Problem problem, String name) {
170 return namedStatementOfType(problem, EnumDeclaration.class, name);
171 }
172
173 public Node literal(EnumDeclaration declaration, String name) {
174 return named(declaration.getLiterals(), name);
175 }
176
177 private <T extends NamedElement> T named(Stream<? extends T> stream, String name) {
178 return stream.filter(statement -> name.equals(statement.getName())).findAny().get();
179 }
180
181 private <T extends NamedElement> T named(List<? extends T> list, String name) {
182 return named(list.stream(), name);
183 }
184
185 private <T extends Statement> Stream<T> statementsOfType(Problem problem, Class<? extends T> type) {
186 return problem.getStatements().stream().filter(type::isInstance).map(type::cast);
187 }
188
189 private <T extends Statement & NamedElement> T namedStatementOfType(Problem problem, Class<? extends T> type,
190 String name) {
191 return named(statementsOfType(problem, type), name);
192 }
193
194 private <T extends Statement> T nthStatementOfType(Problem problem, Class<? extends T> type, int n) {
195 return statementsOfType(problem, type).skip(n).findFirst().get();
196 }
197}
diff --git a/subprojects/language-to-store/build.gradle b/subprojects/language-to-store/build.gradle
new file mode 100644
index 00000000..f1c1564d
--- /dev/null
+++ b/subprojects/language-to-store/build.gradle
@@ -0,0 +1,10 @@
1plugins {
2 id 'refinery-java-library'
3 id 'refinery-xtext-conventions'
4}
5
6dependencies {
7 api project(':refinery-language-model')
8 api project(':refinery-store')
9 testImplementation testFixtures(project(':refinery-language'))
10}
diff --git a/subprojects/language-to-store/src/main/java/tools/refinery/language/mapping/PartialModelMapper.java b/subprojects/language-to-store/src/main/java/tools/refinery/language/mapping/PartialModelMapper.java
new file mode 100644
index 00000000..8f90a743
--- /dev/null
+++ b/subprojects/language-to-store/src/main/java/tools/refinery/language/mapping/PartialModelMapper.java
@@ -0,0 +1,239 @@
1package tools.refinery.language.mapping;
2
3import java.util.HashMap;
4import java.util.HashSet;
5import java.util.Map;
6import java.util.Optional;
7
8import org.eclipse.emf.common.util.EList;
9
10import tools.refinery.language.model.ProblemUtil;
11import tools.refinery.language.model.problem.Assertion;
12import tools.refinery.language.model.problem.AssertionArgument;
13import tools.refinery.language.model.problem.ClassDeclaration;
14import tools.refinery.language.model.problem.EnumDeclaration;
15import tools.refinery.language.model.problem.IndividualDeclaration;
16import tools.refinery.language.model.problem.LogicValue;
17import tools.refinery.language.model.problem.Node;
18import tools.refinery.language.model.problem.NodeAssertionArgument;
19import tools.refinery.language.model.problem.PredicateDefinition;
20import tools.refinery.language.model.problem.Problem;
21import tools.refinery.language.model.problem.ReferenceDeclaration;
22import tools.refinery.language.model.problem.Statement;
23import tools.refinery.store.model.Model;
24import tools.refinery.store.model.ModelStore;
25import tools.refinery.store.model.ModelStoreImpl;
26import tools.refinery.store.model.Tuple;
27import tools.refinery.store.model.representation.Relation;
28import tools.refinery.store.model.representation.TruthValue;
29
30public class PartialModelMapper {
31 public PartialModelMapperDTO transformProblem(Problem problem) throws PartialModelMapperException {
32 // Defining an integer in order to assign different values to all the nodes
33 int[] nodeIter = new int[] { 0 };
34
35 // Getting the relations and the nodes from the given problem
36 PartialModelMapperDTO pmmDTO = initTransform(problem, nodeIter);
37
38 // Getting the relations and the nodes from the built in problem
39 Optional<Problem> builtinProblem = ProblemUtil.getBuiltInLibrary(problem);
40 if (builtinProblem.isEmpty())
41 throw new PartialModelMapperException("builtin.problem not found");
42 PartialModelMapperDTO builtinProblemDTO = initTransform(builtinProblem.get(), nodeIter);
43
44 // Merging the relation and the nodes from the given problem and from the built
45 // in problem
46 pmmDTO.getRelationMap().putAll(builtinProblemDTO.getRelationMap());
47 pmmDTO.getNodeMap().putAll(builtinProblemDTO.getNodeMap());
48 pmmDTO.getEnumNodeMap().putAll(builtinProblemDTO.getEnumNodeMap());
49 pmmDTO.getNewNodeMap().putAll(builtinProblemDTO.getNewNodeMap());
50 pmmDTO.getUniqueNodeMap().putAll(builtinProblemDTO.getUniqueNodeMap());
51
52 // Definition of store and model
53 ModelStore store = new ModelStoreImpl(new HashSet<>(pmmDTO.getRelationMap().values()));
54 Model model = store.createModel();
55 pmmDTO.setModel(model);
56
57 // Collecting all the nodes in one map
58 Map<Node, Integer> allNodesMap = mergeNodeMaps(pmmDTO.getEnumNodeMap(), pmmDTO.getUniqueNodeMap(),
59 pmmDTO.getNewNodeMap(), pmmDTO.getNodeMap());
60
61 // Filling up the relations with unknown truth values
62 for (tools.refinery.language.model.problem.Relation relation : pmmDTO.getRelationMap().keySet()) {
63 if (!(relation instanceof PredicateDefinition pd && pd.isError())) {
64 Relation<TruthValue> r = pmmDTO.getRelationMap().get(relation);
65 if (r.getArity() == 1)
66 for (Integer i : allNodesMap.values()) {
67 pmmDTO.getModel().put(r, Tuple.of(i), TruthValue.UNKNOWN);
68 }
69 else if (r.getArity() == 2)
70 for (Integer i : allNodesMap.values()) {
71 for (Integer j : allNodesMap.values()) {
72 pmmDTO.getModel().put(r, Tuple.of(i, j), TruthValue.UNKNOWN);
73 }
74 }
75 else
76 throw new PartialModelMapperException("Relation with arity above 2 is not supported");
77 }
78 }
79
80 // Filling up the exists
81 tools.refinery.language.model.problem.Relation existsRelation = findingRelationInDTO(builtinProblemDTO,
82 "exists", "The exists not found in built in problem");
83 for (Node n : allNodesMap.keySet()) {
84 if (pmmDTO.getNewNodeMap().containsKey(n)) {
85 pmmDTO.getModel().put(builtinProblemDTO.getRelationMap().get(existsRelation),
86 Tuple.of(allNodesMap.get(n)), TruthValue.UNKNOWN);
87 } else {
88 pmmDTO.getModel().put(builtinProblemDTO.getRelationMap().get(existsRelation),
89 Tuple.of(allNodesMap.get(n)), TruthValue.TRUE);
90 }
91 }
92
93 // Filling up the equals
94 tools.refinery.language.model.problem.Relation equalsRelation = findingRelationInDTO(builtinProblemDTO,
95 "equals", "The equals not found in built in problem");
96 for (Node n1 : allNodesMap.keySet()) {
97 for (Node n2 : allNodesMap.keySet()) {
98 if (n1.equals(n2)) {
99 if (pmmDTO.getNewNodeMap().containsKey(n1)) {
100 pmmDTO.getModel().put(builtinProblemDTO.getRelationMap().get(equalsRelation),
101 Tuple.of(allNodesMap.get(n1), allNodesMap.get(n2)), TruthValue.UNKNOWN);
102 } else {
103 pmmDTO.getModel().put(builtinProblemDTO.getRelationMap().get(equalsRelation),
104 Tuple.of(allNodesMap.get(n1), allNodesMap.get(n2)), TruthValue.TRUE);
105 }
106 } else {
107 pmmDTO.getModel().put(builtinProblemDTO.getRelationMap().get(equalsRelation),
108 Tuple.of(allNodesMap.get(n1), allNodesMap.get(n2)), TruthValue.FALSE);
109 }
110 }
111 }
112
113 // Transforming the assertions
114 processAssertions(problem, pmmDTO, allNodesMap);
115 processAssertions(builtinProblem.get(), pmmDTO, allNodesMap);
116
117 return pmmDTO;
118 }
119
120 // Searches for and gives back a relation in a PartialModelMapperDTO
121 private tools.refinery.language.model.problem.Relation findingRelationInDTO(
122 PartialModelMapperDTO partialModelMapperDTO, String searchedRelation, String errorText)
123 throws PartialModelMapperException {
124 for (tools.refinery.language.model.problem.Relation r : partialModelMapperDTO.getRelationMap().keySet()) {
125 if (searchedRelation.equals(r.getName()))
126 return r;
127 }
128 throw new PartialModelMapperException(errorText);
129 }
130
131 // Processing assertions and placing them in the model
132 private void processAssertions(Problem problem, PartialModelMapperDTO pmmDTO, Map<Node, Integer> allNodesMap) {
133 for (Statement s : problem.getStatements()) {
134 if (s instanceof Assertion assertion) {
135 Relation<TruthValue> r1 = pmmDTO.getRelationMap().get(assertion.getRelation());
136 int i = 0;
137 int[] integers = new int[assertion.getArguments().size()];
138 for (AssertionArgument aa : assertion.getArguments()) {
139 if (aa instanceof NodeAssertionArgument nas) {
140 integers[i] = allNodesMap.get(nas.getNode());
141 i++;
142 }
143 }
144 pmmDTO.getModel().put(r1, Tuple.of(integers), logicValueToTruthValue(assertion.getValue()));
145 } else if (s instanceof ClassDeclaration cd) {
146 if (!cd.isAbstract())
147 pmmDTO.getModel().put(pmmDTO.getRelationMap().get(cd),
148 Tuple.of(pmmDTO.getNewNodeMap().get(cd.getNewNode())), TruthValue.TRUE);
149 } else if (s instanceof EnumDeclaration ed) {
150 for (Node n : ed.getLiterals()) {
151 pmmDTO.getModel().put(pmmDTO.getRelationMap().get(ed), Tuple.of(pmmDTO.getEnumNodeMap().get(n)),
152 TruthValue.TRUE);
153 }
154 }
155 }
156 }
157
158 // Getting the relations and nodes from the problem
159 private PartialModelMapperDTO initTransform(Problem problem, int[] nodeIter) {
160 // Defining needed Maps
161 Map<tools.refinery.language.model.problem.Relation, Relation<TruthValue>> relationMap = new HashMap<>();
162 Map<Node, Integer> enumNodeMap = new HashMap<>();
163 Map<Node, Integer> uniqueNodeMap = new HashMap<>();
164 Map<Node, Integer> newNodeMap = new HashMap<>();
165
166 // Definition of Relations, filling up the enumNodeMap, uniqueNodeMap,
167 // newNodeMap
168 EList<Statement> statements = problem.getStatements();
169 for (Statement s : statements) {
170 if (s instanceof ClassDeclaration cd) {
171 Relation<TruthValue> r1 = new Relation<>(cd.getName(), 1, TruthValue.FALSE);
172 relationMap.put(cd, r1);
173 if (!cd.isAbstract())
174 newNodeMap.put(cd.getNewNode(), nodeIter[0]++);
175 EList<ReferenceDeclaration> refDeclList = cd.getReferenceDeclarations();
176 for (ReferenceDeclaration refDec : refDeclList) {
177 Relation<TruthValue> r2 = new Relation<>(refDec.getName(), 2, TruthValue.FALSE);
178 relationMap.put(refDec, r2);
179 }
180 } else if (s instanceof EnumDeclaration ed) {
181 Relation<TruthValue> r = new Relation<>(ed.getName(), 1, TruthValue.FALSE);
182 relationMap.put(ed, r);
183 for (Node n : ed.getLiterals()) {
184 enumNodeMap.put(n, nodeIter[0]++);
185 }
186 } else if (s instanceof IndividualDeclaration ud) {
187 for (Node n : ud.getNodes()) {
188 uniqueNodeMap.put(n, nodeIter[0]++);
189 }
190 } else if (s instanceof PredicateDefinition pd) {
191 Relation<TruthValue> r = new Relation<>(pd.getName(), 1, TruthValue.FALSE);
192 relationMap.put(pd, r);
193 }
194 }
195
196 // Filling the nodeMap up
197 Map<Node, Integer> nodeMap = new HashMap<>();
198 for (Node n : problem.getNodes()) {
199 nodeMap.put(n, nodeIter[0]++);
200 }
201
202 return new PartialModelMapperDTO(null, relationMap, nodeMap, enumNodeMap, uniqueNodeMap, newNodeMap);
203 }
204
205 // Merging the maps of nodes into one map
206 private Map<Node, Integer> mergeNodeMaps(Map<Node, Integer> enumNodeMap, Map<Node, Integer> uniqueNodeMap,
207 Map<Node, Integer> newNodeMap, Map<Node, Integer> nodeMap) {
208 Map<Node, Integer> out = new HashMap<>();
209 out.putAll(enumNodeMap);
210 out.putAll(uniqueNodeMap);
211 out.putAll(newNodeMap);
212 out.putAll(nodeMap);
213 return out;
214 }
215
216 // Exchange method from LogicValue to TruthValue
217 private TruthValue logicValueToTruthValue(LogicValue value) {
218 if (value.equals(LogicValue.TRUE))
219 return TruthValue.TRUE;
220 else if (value.equals(LogicValue.FALSE))
221 return TruthValue.FALSE;
222 else if (value.equals(LogicValue.UNKNOWN))
223 return TruthValue.UNKNOWN;
224 else
225 return TruthValue.ERROR;
226 }
227
228 public class PartialModelMapperException extends Exception {
229 private static final long serialVersionUID = 1L;
230
231 public PartialModelMapperException(String errorText) {
232 super(errorText);
233 }
234
235 public PartialModelMapperException() {
236 super();
237 }
238 }
239}
diff --git a/subprojects/language-to-store/src/main/java/tools/refinery/language/mapping/PartialModelMapperDTO.java b/subprojects/language-to-store/src/main/java/tools/refinery/language/mapping/PartialModelMapperDTO.java
new file mode 100644
index 00000000..3397b4bd
--- /dev/null
+++ b/subprojects/language-to-store/src/main/java/tools/refinery/language/mapping/PartialModelMapperDTO.java
@@ -0,0 +1,54 @@
1package tools.refinery.language.mapping;
2
3import java.util.Map;
4
5import tools.refinery.language.model.problem.Node;
6import tools.refinery.store.model.Model;
7import tools.refinery.store.model.representation.Relation;
8import tools.refinery.store.model.representation.TruthValue;
9
10public class PartialModelMapperDTO {
11 private Model model;
12 private Map<tools.refinery.language.model.problem.Relation, Relation<TruthValue>> relationMap;
13 private Map<Node, Integer> nodeMap;
14 private Map<Node, Integer> enumNodeMap;
15 private Map<Node, Integer> uniqueNodeMap;
16 private Map<Node, Integer> newNodeMap;
17
18 public PartialModelMapperDTO(Model model,
19 Map<tools.refinery.language.model.problem.Relation, Relation<TruthValue>> relationMap,
20 Map<Node, Integer> nodeMap,
21 Map<Node, Integer> enumNodeMap,
22 Map<Node, Integer> uniqueNodeMap,
23 Map<Node, Integer> newNodeMap) {
24 this.model = model;
25 this.relationMap = relationMap;
26 this.nodeMap = nodeMap;
27 this.enumNodeMap = enumNodeMap;
28 this.uniqueNodeMap = uniqueNodeMap;
29 this.newNodeMap = newNodeMap;
30 }
31
32 public Model getModel() {
33 return this.model;
34 }
35 public Map<tools.refinery.language.model.problem.Relation, Relation<TruthValue>> getRelationMap(){
36 return this.relationMap;
37 }
38 public Map<Node, Integer> getNodeMap() {
39 return this.nodeMap;
40 }
41 public Map<Node, Integer> getEnumNodeMap() {
42 return this.enumNodeMap;
43 }
44 public Map<Node, Integer> getUniqueNodeMap() {
45 return this.uniqueNodeMap;
46 }
47 public Map<Node, Integer> getNewNodeMap() {
48 return this.newNodeMap;
49 }
50
51 public void setModel(Model model) {
52 this.model = model;
53 }
54}
diff --git a/subprojects/language-to-store/src/test/java/tools/refinery/language/mapping/tests/PartialModelMapperTest.xtend b/subprojects/language-to-store/src/test/java/tools/refinery/language/mapping/tests/PartialModelMapperTest.xtend
new file mode 100644
index 00000000..b2fcbaa9
--- /dev/null
+++ b/subprojects/language-to-store/src/test/java/tools/refinery/language/mapping/tests/PartialModelMapperTest.xtend
@@ -0,0 +1,438 @@
1package tools.refinery.language.mapping.tests
2
3import com.google.inject.Inject
4import org.eclipse.emf.ecore.util.EcoreUtil
5import org.eclipse.xtext.testing.InjectWith
6import org.eclipse.xtext.testing.extensions.InjectionExtension
7import org.eclipse.xtext.testing.util.ParseHelper
8import org.junit.jupiter.api.BeforeEach
9import org.junit.jupiter.api.Test
10import org.junit.jupiter.api.^extension.ExtendWith
11import tools.refinery.language.mapping.PartialModelMapper
12import tools.refinery.language.model.problem.Problem
13import tools.refinery.language.model.tests.ProblemTestUtil
14import tools.refinery.language.tests.ProblemInjectorProvider
15import tools.refinery.store.model.Tuple
16import tools.refinery.store.model.representation.TruthValue
17
18import static org.hamcrest.MatcherAssert.assertThat
19import static org.hamcrest.Matchers.*
20import static org.junit.jupiter.api.Assertions.assertTrue
21
22@ExtendWith(InjectionExtension)
23@InjectWith(ProblemInjectorProvider)
24class PartialModelMapperTest {
25 @Inject
26 ParseHelper<Problem> parseHelper
27
28 @Inject
29 extension ProblemTestUtil
30
31 PartialModelMapper mapper
32
33 @BeforeEach
34 def void beforeEach() {
35 mapper = new PartialModelMapper
36 }
37
38 //Testing the relation
39 @Test
40 def void relationTest() {
41 val problem = parseHelper.parse('''
42 class Person {
43 Person[0..*] friend
44 }
45
46 friend(a, b).
47 ''')
48 EcoreUtil.resolveAll(problem)
49
50 val modelAndMaps = mapper.transformProblem(problem)
51 assertThat(modelAndMaps, notNullValue())
52
53 val model = modelAndMaps.model
54 val relationMap = modelAndMaps.relationMap
55 val nodeMap = modelAndMaps.nodeMap
56
57 val person = problem.findClass("Person")
58 val friend = problem.findClass("Person").reference("friend")
59 val a = problem.node("a")
60 val b = problem.node("b")
61
62 assertTrue(model.getDataRepresentations().contains(relationMap.get(person)))
63 assertTrue(model.getDataRepresentations().contains(relationMap.get(friend)))
64 assertTrue(model.get(relationMap.get(friend), Tuple.of(nodeMap.get(a),nodeMap.get(b))).equals(TruthValue.TRUE))
65 }
66
67 //Testing the class
68 @Test
69 def void classTest() {
70 val problem = parseHelper.parse('''
71 class Person {
72 Person[0..*] friend
73 }
74
75 Person(a).
76 ''')
77 EcoreUtil.resolveAll(problem)
78
79 val modelAndMaps = mapper.transformProblem(problem)
80 assertThat(modelAndMaps, notNullValue())
81
82 val model = modelAndMaps.model
83 val relationMap = modelAndMaps.relationMap
84 val nodeMap = modelAndMaps.nodeMap
85
86 val person = problem.findClass("Person")
87 val friend = problem.findClass("Person").reference("friend")
88 val a = problem.node("a")
89
90 assertTrue(model.getDataRepresentations().contains(relationMap.get(person)))
91 assertTrue(model.getDataRepresentations().contains(relationMap.get(friend)))
92
93 assertTrue(model.get(relationMap.get(person), Tuple.of(nodeMap.get(a))).equals(TruthValue.TRUE))
94 }
95
96 //Testing the equals and exists from the built in problem
97 @Test
98 def void equalsAndExistTest() {
99 val problem = parseHelper.parse('''
100 node(a).
101 node(b).
102
103 class Person.
104 ''')
105 EcoreUtil.resolveAll(problem)
106 val builtin = problem.builtin
107
108 val modelAndMaps = mapper.transformProblem(problem)
109 assertThat(modelAndMaps, notNullValue())
110
111 val model = modelAndMaps.model
112 val relationMap = modelAndMaps.relationMap
113 val nodeMap = modelAndMaps.nodeMap
114 val newNodeMap = modelAndMaps.newNodeMap
115
116 val a = problem.node("a")
117 val b = problem.node("b")
118 val Person = problem.findClass("Person")
119 val PersonNew = problem.findClass("Person").newNode
120 val exists = builtin.pred("exists")
121 val equals = builtin.findClass("node").reference("equals")
122
123 assertTrue(model.getDataRepresentations().contains(relationMap.get(Person)))
124 assertTrue(model.getDataRepresentations().contains(relationMap.get(exists)))
125 assertTrue(model.getDataRepresentations().contains(relationMap.get(equals)))
126
127 assertTrue(model.get(relationMap.get(exists), Tuple.of(nodeMap.get(a))).equals(TruthValue.TRUE))
128 assertTrue(model.get(relationMap.get(exists), Tuple.of(nodeMap.get(b))).equals(TruthValue.TRUE))
129 assertTrue(model.get(relationMap.get(equals), Tuple.of(nodeMap.get(a), nodeMap.get(a))).equals(TruthValue.TRUE))
130 assertTrue(model.get(relationMap.get(equals), Tuple.of(nodeMap.get(b), nodeMap.get(b))).equals(TruthValue.TRUE))
131 assertTrue(model.get(relationMap.get(equals), Tuple.of(nodeMap.get(a), nodeMap.get(b))).equals(TruthValue.FALSE))
132 assertTrue(model.get(relationMap.get(equals), Tuple.of(nodeMap.get(b), nodeMap.get(a))).equals(TruthValue.FALSE))
133
134 assertTrue(model.get(relationMap.get(exists), Tuple.of(newNodeMap.get(PersonNew))).equals(TruthValue.UNKNOWN))
135 assertTrue(model.get(relationMap.get(equals), Tuple.of(newNodeMap.get(PersonNew), newNodeMap.get(PersonNew))).equals(TruthValue.UNKNOWN))
136 assertTrue(model.get(relationMap.get(equals), Tuple.of(newNodeMap.get(PersonNew), nodeMap.get(a))).equals(TruthValue.FALSE))
137 assertTrue(model.get(relationMap.get(equals), Tuple.of(newNodeMap.get(PersonNew), nodeMap.get(b))).equals(TruthValue.FALSE))
138 assertTrue(model.get(relationMap.get(equals), Tuple.of(nodeMap.get(a), newNodeMap.get(PersonNew))).equals(TruthValue.FALSE))
139 assertTrue(model.get(relationMap.get(equals), Tuple.of(nodeMap.get(b), newNodeMap.get(PersonNew))).equals(TruthValue.FALSE))
140 }
141
142 //Testing the equals and exists from the built in problem with a different example
143 @Test
144 def void equalsAndExistTest2() {
145 val problem = parseHelper.parse('''
146 class Person.
147
148 Person(a).
149 Person(b).
150 ''')
151 val builtin = problem.builtin
152 EcoreUtil.resolveAll(problem)
153
154 val modelAndMaps = mapper.transformProblem(problem)
155 assertThat(modelAndMaps, notNullValue())
156
157 val model = modelAndMaps.model
158 val relationMap = modelAndMaps.relationMap
159 val nodeMap = modelAndMaps.nodeMap
160 val newNodeMap = modelAndMaps.newNodeMap
161
162 val a = problem.node("a")
163 val b = problem.node("b")
164 val Person = problem.findClass("Person")
165 val PersonNew = problem.findClass("Person").newNode
166 val exists = builtin.pred("exists")
167 val equals = builtin.findClass("node").reference("equals")
168
169 assertTrue(model.getDataRepresentations().contains(relationMap.get(Person)))
170 assertTrue(model.getDataRepresentations().contains(relationMap.get(exists)))
171 assertTrue(model.getDataRepresentations().contains(relationMap.get(equals)))
172
173 assertTrue(model.get(relationMap.get(exists), Tuple.of(nodeMap.get(a))).equals(TruthValue.TRUE))
174 assertTrue(model.get(relationMap.get(exists), Tuple.of(nodeMap.get(b))).equals(TruthValue.TRUE))
175 assertTrue(model.get(relationMap.get(equals), Tuple.of(nodeMap.get(a), nodeMap.get(a))).equals(TruthValue.TRUE))
176 assertTrue(model.get(relationMap.get(equals), Tuple.of(nodeMap.get(b), nodeMap.get(b))).equals(TruthValue.TRUE))
177 assertTrue(model.get(relationMap.get(equals), Tuple.of(nodeMap.get(a), nodeMap.get(b))).equals(TruthValue.FALSE))
178 assertTrue(model.get(relationMap.get(equals), Tuple.of(nodeMap.get(b), nodeMap.get(a))).equals(TruthValue.FALSE))
179
180 assertTrue(model.get(relationMap.get(exists), Tuple.of(newNodeMap.get(PersonNew))).equals(TruthValue.UNKNOWN))
181 assertTrue(model.get(relationMap.get(equals), Tuple.of(newNodeMap.get(PersonNew), newNodeMap.get(PersonNew))).equals(TruthValue.UNKNOWN))
182 assertTrue(model.get(relationMap.get(equals), Tuple.of(newNodeMap.get(PersonNew), nodeMap.get(a))).equals(TruthValue.FALSE))
183 assertTrue(model.get(relationMap.get(equals), Tuple.of(newNodeMap.get(PersonNew), nodeMap.get(b))).equals(TruthValue.FALSE))
184 assertTrue(model.get(relationMap.get(equals), Tuple.of(nodeMap.get(a), newNodeMap.get(PersonNew))).equals(TruthValue.FALSE))
185 assertTrue(model.get(relationMap.get(equals), Tuple.of(nodeMap.get(b), newNodeMap.get(PersonNew))).equals(TruthValue.FALSE))
186 }
187
188 //Testing the behavior of the newNodes
189 @Test
190 def void newNodeTest(){
191 val problem = parseHelper.parse('''
192 class Person.
193 abstract class Family.
194 ''')
195 EcoreUtil.resolveAll(problem)
196
197 val modelAndMaps = mapper.transformProblem(problem)
198 assertThat(modelAndMaps, notNullValue())
199
200 val model = modelAndMaps.model
201 val relationMap = modelAndMaps.relationMap
202 val newNodeMap = modelAndMaps.newNodeMap
203
204 val Person = problem.findClass("Person")
205 val Family = problem.findClass("Family")
206 val PersonNew = problem.findClass("Person").newNode
207
208
209 assertTrue(model.getDataRepresentations().contains(relationMap.get(Person)))
210 assertTrue(model.getDataRepresentations().contains(relationMap.get(Family)))
211
212 assertTrue(newNodeMap.size.equals(4)) //3 from builtin.problem, 1 from Person
213 assertTrue(model.get(relationMap.get(Person), Tuple.of(newNodeMap.get(PersonNew))).equals(TruthValue.TRUE))
214 }
215
216 //Testing the behavior of enumerations
217 @Test
218 def void enumTest(){
219 val problem = parseHelper.parse('''
220 enum TaxStatus {
221 child, student, adult, retired
222 }
223 ''')
224 EcoreUtil.resolveAll(problem)
225
226 val modelAndMaps = mapper.transformProblem(problem)
227 assertThat(modelAndMaps, notNullValue())
228
229 val model = modelAndMaps.model
230 val relationMap = modelAndMaps.relationMap
231 val enumNodeMap = modelAndMaps.enumNodeMap
232
233 val TaxStatus = problem.findEnum("TaxStatus")
234 val child = problem.findEnum("TaxStatus").literal("child")
235 val student = problem.findEnum("TaxStatus").literal("student")
236 val adult = problem.findEnum("TaxStatus").literal("adult")
237 val retired = problem.findEnum("TaxStatus").literal("retired")
238
239 assertTrue(model.getDataRepresentations().contains(relationMap.get(TaxStatus)))
240 assertTrue(model.get(relationMap.get(TaxStatus), Tuple.of(enumNodeMap.get(child))).equals(TruthValue.TRUE))
241 assertTrue(model.get(relationMap.get(TaxStatus), Tuple.of(enumNodeMap.get(student))).equals(TruthValue.TRUE))
242 assertTrue(model.get(relationMap.get(TaxStatus), Tuple.of(enumNodeMap.get(adult))).equals(TruthValue.TRUE))
243 assertTrue(model.get(relationMap.get(TaxStatus), Tuple.of(enumNodeMap.get(retired))).equals(TruthValue.TRUE))
244 }
245
246 //Testing the bool from the built in problem
247 @Test
248 def void builtinBoolTest(){
249 val problem = parseHelper.parse('''
250 class Person.
251 ''')
252 EcoreUtil.resolveAll(problem)
253 val builtin = problem.builtin
254
255 val modelAndMaps = mapper.transformProblem(problem)
256 assertThat(modelAndMaps, notNullValue())
257
258 val model = modelAndMaps.model
259 val relationMap = modelAndMaps.relationMap
260 val enumNodeMap = modelAndMaps.enumNodeMap
261
262 val bool = builtin.findEnum("bool")
263 val trueEnum = builtin.findEnum("bool").literal("true") //Emiatt nem sikerül a teszt
264 val falseEnum = builtin.findEnum("bool").literal("false")
265
266 assertTrue(model.getDataRepresentations().contains(relationMap.get(bool)))
267 assertTrue(model.get(relationMap.get(bool), Tuple.of(enumNodeMap.get(trueEnum))).equals(TruthValue.TRUE))
268 assertTrue(model.get(relationMap.get(bool), Tuple.of(enumNodeMap.get(falseEnum))).equals(TruthValue.TRUE))
269 }
270
271 //Testing different aspects of the behavior
272 @Test
273 def void compositeTest() {
274 val problem = parseHelper.parse('''
275 class Family {
276 contains Person[] members
277 }
278
279 class Person {
280 Person[0..*] children
281 Person[0..1] parent
282 TaxStatus taxStatus
283 }
284
285 enum TaxStatus {
286 child, student, adult, retired
287 }
288
289 % A child cannot have any dependents.
290 error invalidTaxStatus(Person p) <->
291 taxStatus(p, child), children(p, _q).
292
293 indiv family.
294 Family(family).
295 members(family, anne): true.
296 members(family, bob).
297 members(family, ciri).
298 children(anne, ciri).
299 ?children(bob, ciri).
300 taxStatus(anne, adult).
301 ''')
302 EcoreUtil.resolveAll(problem)
303
304 val modelAndMaps = mapper.transformProblem(problem)
305 assertThat(modelAndMaps, notNullValue())
306
307 val model = modelAndMaps.model
308 val relationMap = modelAndMaps.relationMap
309 val nodeMap = modelAndMaps.nodeMap
310 val uniqueNodeMap = modelAndMaps.uniqueNodeMap
311 val enumNodeMap = modelAndMaps.enumNodeMap
312
313 val Family = problem.findClass("Family")
314 val members = problem.findClass("Family").reference("members")
315 val Person = problem.findClass("Person")
316 val children = problem.findClass("Person").reference("children")
317 val parent = problem.findClass("Person").reference("parent")
318 val taxStatus = problem.findClass("Person").reference("taxStatus")
319 val TaxStatus = problem.findEnum("TaxStatus")
320 val invalidTaxStatus = problem.pred("invalidTaxStatus")
321
322 val anne = problem.node("anne")
323 val bob = problem.node("bob")
324 val ciri = problem.node("ciri")
325 val family = problem.individualNode("family")
326 val adult = problem.findEnum("TaxStatus").literal("adult")
327
328 assertTrue(model.getDataRepresentations().contains(relationMap.get(Family)))
329 assertTrue(model.getDataRepresentations().contains(relationMap.get(members)))
330 assertTrue(model.getDataRepresentations().contains(relationMap.get(Person)))
331 assertTrue(model.getDataRepresentations().contains(relationMap.get(children)))
332 assertTrue(model.getDataRepresentations().contains(relationMap.get(parent)))
333 assertTrue(model.getDataRepresentations().contains(relationMap.get(taxStatus)))
334 assertTrue(model.getDataRepresentations().contains(relationMap.get(TaxStatus)))
335 assertTrue(model.getDataRepresentations().contains(relationMap.get(invalidTaxStatus)))
336
337 assertTrue(model.get(relationMap.get(Family), Tuple.of(uniqueNodeMap.get(family))).equals(TruthValue.TRUE))
338 assertTrue(model.get(relationMap.get(members), Tuple.of(uniqueNodeMap.get(family),nodeMap.get(anne))).equals(TruthValue.TRUE))
339 assertTrue(model.get(relationMap.get(members), Tuple.of(uniqueNodeMap.get(family),nodeMap.get(bob))).equals(TruthValue.TRUE))
340 assertTrue(model.get(relationMap.get(members), Tuple.of(uniqueNodeMap.get(family),nodeMap.get(ciri))).equals(TruthValue.TRUE))
341 assertTrue(model.get(relationMap.get(children), Tuple.of(nodeMap.get(anne),nodeMap.get(ciri))).equals(TruthValue.TRUE))
342 assertTrue(model.get(relationMap.get(children), Tuple.of(nodeMap.get(bob),nodeMap.get(ciri))).equals(TruthValue.UNKNOWN))
343 assertTrue(model.get(relationMap.get(taxStatus), Tuple.of(nodeMap.get(anne),enumNodeMap.get(adult))).equals(TruthValue.TRUE))
344 }
345
346 @Test
347 def void carCaseStudyTest(){
348 val problem = parseHelper.parse('''
349 abstract class DynamicComponent {
350 contains StaticComponent[1..1] placedOn
351 }
352 abstract class StaticComponent.
353 class Car extends DynamicComponent.
354 class Pedestrian extends DynamicComponent.
355 class Road extends StaticComponent {
356 contains LaneSegment[0..*] lanes
357 }
358 class LaneSegment extends StaticComponent {
359 Lane[0..*] adjacentLanes
360 Lane[0..*] sameDirLanes
361 }
362
363 Car(c1).
364 Car(c2).
365 Pedestrian(p1).
366 Road(r1).
367 LaneSegment(l1).
368 LaneSegment(l2).
369 LaneSegment(l3).
370 placedOn(c1,l1).
371 placedOn(c2,l2).
372 placedOn(p1,l3).
373 lanes(r1,l1).
374 lanes(r1,l2).
375 lanes(r1,l3).
376 adjacentLanes(l1,l2).
377 adjacentLanes(l2,l1).
378 sameDirLanes(l1,l3).
379 sameDirLanes(l3,l1).
380 ''')
381 EcoreUtil.resolveAll(problem)
382
383 val modelAndMaps = mapper.transformProblem(problem)
384 assertThat(modelAndMaps, notNullValue())
385
386 val model = modelAndMaps.model
387 val relationMap = modelAndMaps.relationMap
388 val nodeMap = modelAndMaps.nodeMap
389
390 val DynamicComponent = problem.findClass("DynamicComponent")
391 val placedOn = problem.findClass("DynamicComponent").reference("placedOn")
392 val StaticComponent = problem.findClass("StaticComponent")
393 val Car = problem.findClass("Car")
394 val Pedestrian = problem.findClass("Pedestrian")
395 val Road = problem.findClass("Road")
396 val lanes = problem.findClass("Road").reference("lanes")
397 val LaneSegment = problem.findClass("LaneSegment")
398 val adjacentLanes = problem.findClass("LaneSegment").reference("adjacentLanes")
399 val sameDirLanes = problem.findClass("LaneSegment").reference("sameDirLanes")
400
401 val c1 = problem.node("c1")
402 val c2 = problem.node("c2")
403 val p1 = problem.node("p1")
404 val r1 = problem.node("r1")
405 val l1 = problem.node("l1")
406 val l2 = problem.node("l2")
407 val l3 = problem.node("l3")
408
409 assertTrue(model.getDataRepresentations().contains(relationMap.get(DynamicComponent)))
410 assertTrue(model.getDataRepresentations().contains(relationMap.get(placedOn)))
411 assertTrue(model.getDataRepresentations().contains(relationMap.get(StaticComponent)))
412 assertTrue(model.getDataRepresentations().contains(relationMap.get(Car)))
413 assertTrue(model.getDataRepresentations().contains(relationMap.get(Pedestrian)))
414 assertTrue(model.getDataRepresentations().contains(relationMap.get(Road)))
415 assertTrue(model.getDataRepresentations().contains(relationMap.get(lanes)))
416 assertTrue(model.getDataRepresentations().contains(relationMap.get(LaneSegment)))
417 assertTrue(model.getDataRepresentations().contains(relationMap.get(adjacentLanes)))
418 assertTrue(model.getDataRepresentations().contains(relationMap.get(sameDirLanes)))
419
420 assertTrue(model.get(relationMap.get(Car), Tuple.of(nodeMap.get(c1))).equals(TruthValue.TRUE))
421 assertTrue(model.get(relationMap.get(Car), Tuple.of(nodeMap.get(c2))).equals(TruthValue.TRUE))
422 assertTrue(model.get(relationMap.get(Pedestrian), Tuple.of(nodeMap.get(p1))).equals(TruthValue.TRUE))
423 assertTrue(model.get(relationMap.get(Road), Tuple.of(nodeMap.get(r1))).equals(TruthValue.TRUE))
424 assertTrue(model.get(relationMap.get(LaneSegment), Tuple.of(nodeMap.get(l1))).equals(TruthValue.TRUE))
425 assertTrue(model.get(relationMap.get(LaneSegment), Tuple.of(nodeMap.get(l2))).equals(TruthValue.TRUE))
426 assertTrue(model.get(relationMap.get(LaneSegment), Tuple.of(nodeMap.get(l3))).equals(TruthValue.TRUE))
427 assertTrue(model.get(relationMap.get(placedOn), Tuple.of(nodeMap.get(c1),nodeMap.get(l1))).equals(TruthValue.TRUE))
428 assertTrue(model.get(relationMap.get(placedOn), Tuple.of(nodeMap.get(c2),nodeMap.get(l2))).equals(TruthValue.TRUE))
429 assertTrue(model.get(relationMap.get(placedOn), Tuple.of(nodeMap.get(p1),nodeMap.get(l3))).equals(TruthValue.TRUE))
430 assertTrue(model.get(relationMap.get(lanes), Tuple.of(nodeMap.get(r1),nodeMap.get(l1))).equals(TruthValue.TRUE))
431 assertTrue(model.get(relationMap.get(lanes), Tuple.of(nodeMap.get(r1),nodeMap.get(l2))).equals(TruthValue.TRUE))
432 assertTrue(model.get(relationMap.get(lanes), Tuple.of(nodeMap.get(r1),nodeMap.get(l3))).equals(TruthValue.TRUE))
433 assertTrue(model.get(relationMap.get(adjacentLanes), Tuple.of(nodeMap.get(l1),nodeMap.get(l2))).equals(TruthValue.TRUE))
434 assertTrue(model.get(relationMap.get(adjacentLanes), Tuple.of(nodeMap.get(l2),nodeMap.get(l1))).equals(TruthValue.TRUE))
435 assertTrue(model.get(relationMap.get(sameDirLanes), Tuple.of(nodeMap.get(l1),nodeMap.get(l3))).equals(TruthValue.TRUE))
436 assertTrue(model.get(relationMap.get(sameDirLanes), Tuple.of(nodeMap.get(l3),nodeMap.get(l1))).equals(TruthValue.TRUE))
437 }
438}
diff --git a/subprojects/language-web/.editorconfig b/subprojects/language-web/.editorconfig
new file mode 100644
index 00000000..1b78e967
--- /dev/null
+++ b/subprojects/language-web/.editorconfig
@@ -0,0 +1,5 @@
1[src/main/css/xtext/**.css]
2indent_style = tab
3
4[src/main/js/xtext/**.js]
5indent_style = tab
diff --git a/subprojects/language-web/.eslintrc.js b/subprojects/language-web/.eslintrc.js
new file mode 100644
index 00000000..b27feb0e
--- /dev/null
+++ b/subprojects/language-web/.eslintrc.js
@@ -0,0 +1,40 @@
1// Loosely based on
2// https://github.com/iamturns/create-exposed-app/blob/f14e435b8ce179c89cce3eea89e56202153a53da/.eslintrc.js
3module.exports = {
4 plugins: [
5 '@typescript-eslint',
6 ],
7 extends: [
8 'airbnb',
9 'airbnb-typescript',
10 'airbnb/hooks',
11 'plugin:@typescript-eslint/recommended',
12 'plugin:@typescript-eslint/recommended-requiring-type-checking',
13 ],
14 parserOptions: {
15 project: './tsconfig.json',
16 },
17 rules: {
18 // https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html
19 'import/prefer-default-export': 'off',
20 'import/no-default-export': 'error',
21 // propTypes are for runtime validation, but we rely on TypeScript for build-time validation:
22 // https://github.com/yannickcr/eslint-plugin-react/issues/2275#issuecomment-492003857
23 'react/prop-types': 'off',
24 // Make sure switches are exhaustive: https://stackoverflow.com/a/60166264
25 'default-case': 'off',
26 '@typescript-eslint/switch-exhaustiveness-check': 'error',
27 // https://github.com/airbnb/javascript/pull/2501
28 'react/function-component-definition': ['error', {
29 namedComponents: 'function-expression',
30 namedComponents: 'function-declaration',
31 }],
32 },
33 env: {
34 browser: true,
35 },
36 ignorePatterns: [
37 '*.js',
38 'build/**/*',
39 ],
40};
diff --git a/subprojects/language-web/.stylelintrc.js b/subprojects/language-web/.stylelintrc.js
new file mode 100644
index 00000000..7adf8f26
--- /dev/null
+++ b/subprojects/language-web/.stylelintrc.js
@@ -0,0 +1,15 @@
1module.exports = {
2 extends: 'stylelint-config-recommended-scss',
3 // Simplified for only :export to TypeScript based on
4 // https://github.com/pascalduez/stylelint-config-css-modules/blob/d792a6ac7d2bce8239edccbc5a72e0616f22d696/index.js
5 rules: {
6 'selector-pseudo-class-no-unknown': [
7 true,
8 {
9 ignorePseudoClasses: [
10 'export',
11 ],
12 },
13 ],
14 },
15};
diff --git a/subprojects/language-web/build.gradle b/subprojects/language-web/build.gradle
new file mode 100644
index 00000000..a549288a
--- /dev/null
+++ b/subprojects/language-web/build.gradle
@@ -0,0 +1,147 @@
1plugins {
2 id 'refinery-frontend-workspace'
3 id 'refinery-java-application'
4 id 'refinery-xtext-conventions'
5}
6
7import org.siouan.frontendgradleplugin.infrastructure.gradle.RunYarn
8
9dependencies {
10 implementation project(':refinery-language')
11 implementation project(':refinery-language-ide')
12 implementation libs.xtend.lib
13 implementation libs.xtext.web
14 implementation libs.jetty.server
15 implementation libs.jetty.servlet
16 implementation libs.jetty.websocket.server
17 implementation libs.slf4j.simple
18 implementation libs.slf4j.log4j
19 testImplementation testFixtures(project(':refinery-language'))
20 testImplementation libs.jetty.websocket.client
21}
22
23def generateXtextLanguage = project(':refinery-language').tasks.named('generateXtextLanguage')
24
25for (taskName in ['compileJava', 'processResources']) {
26 tasks.named(taskName) {
27 dependsOn generateXtextLanguage
28 }
29}
30
31def webpackOutputDir = "${buildDir}/webpack"
32def productionResources = "${webpackOutputDir}/production"
33def serverMainClass = 'tools.refinery.language.web.ServerLauncher'
34
35frontend {
36 assembleScript = 'assemble:webpack'
37}
38
39def installFrontend = tasks.named('installFrontend')
40
41def generateLezerGrammar = tasks.register('generateLezerGrammar', RunYarn) {
42 dependsOn installFrontend
43 inputs.file('src/main/js/language/problem.grammar')
44 inputs.files('package.json', 'yarn.lock')
45 outputs.file "${buildDir}/generated/sources/lezer/problem.ts"
46 outputs.file "${buildDir}/generated/sources/lezer/problem.terms.ts"
47 script = 'run assemble:lezer'
48}
49
50def assembleFrontend = tasks.named('assembleFrontend')
51assembleFrontend.configure {
52 dependsOn generateLezerGrammar
53 inputs.dir 'src/main/css'
54 inputs.dir 'src/main/html'
55 inputs.dir 'src/main/js'
56 inputs.file "${buildDir}/generated/sources/lezer/problem.ts"
57 inputs.file "${buildDir}/generated/sources/lezer/problem.terms.ts"
58 inputs.files('package.json', 'yarn.lock', 'webpack.config.js')
59 outputs.dir productionResources
60}
61
62def eslint = tasks.register('eslint', RunYarn) {
63 dependsOn installFrontend
64 inputs.dir 'src/main/js'
65 inputs.files('.eslintrc.js', 'tsconfig.json')
66 if (project.hasProperty('ci')) {
67 outputs.file "${buildDir}/eslint.json"
68 script = 'run check:eslint:ci'
69 } else {
70 script = 'run check:eslint'
71 }
72 group = 'verification'
73 description = 'Check for TypeScript errors.'
74}
75
76def stylelint = tasks.register('stylelint', RunYarn) {
77 dependsOn installFrontend
78 inputs.dir 'src/main/css'
79 inputs.file '.stylelintrc.js'
80 if (project.hasProperty('ci')) {
81 outputs.file "${buildDir}/stylelint.json"
82 script = 'run check:stylelint:ci'
83 } else {
84 script = 'run check:stylelint'
85 }
86 group = 'verification'
87 description = 'Check for Sass errors.'
88}
89
90tasks.named('check') {
91 dependsOn(eslint, stylelint)
92}
93
94mainClassName = serverMainClass
95
96tasks.named('jar') {
97 dependsOn assembleFrontend
98 from(productionResources) {
99 into 'webapp'
100 }
101}
102
103tasks.named('shadowJar') {
104 dependsOn assembleFrontend
105 from(project.convention.getPlugin(JavaPluginConvention).sourceSets.main.output)
106 configurations = [project.configurations.runtimeClasspath]
107 exclude('META-INF/INDEX.LIST', 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA','schema/*',
108 '.options', '.api_description', '*.profile', 'about.*', 'about_*.html', 'about_files/*',
109 'plugin.xml', 'systembundle.properties', 'profile.list', 'META-INF/resources/xtext/**')
110 append('plugin.properties')
111 from(productionResources) {
112 into 'webapp'
113 }
114}
115
116def jettyRun = tasks.register('jettyRun', JavaExec) {
117 dependsOn assembleFrontend
118 dependsOn sourceSets.main.runtimeClasspath
119 classpath = sourceSets.main.runtimeClasspath.filter{it.exists()}
120 mainClass = serverMainClass
121 standardInput = System.in
122 environment BASE_RESOURCE: productionResources
123 group = 'run'
124 description = 'Start a Jetty web server serving the Xtex API and assets.'
125}
126
127tasks.register('webpackServe', RunYarn) {
128 dependsOn installFrontend
129 dependsOn generateLezerGrammar
130 outputs.dir "${webpackOutputDir}/development"
131 script = 'run serve'
132 group = 'run'
133 description = 'Start a Webpack dev server with hot module replacement.'
134}
135
136sonarqube.properties {
137 properties['sonar.sources'] += [
138 'src/main/css',
139 'src/main/html',
140 'src/main/js',
141 ]
142 property 'sonar.nodejs.executable', "${frontend.nodeInstallDirectory.get()}/bin/node"
143 property 'sonar.eslint.reportPaths', "${buildDir}/eslint.json"
144 property 'sonar.css.stylelint.reportPaths', "${buildDir}/stylelint.json"
145 // SonarJS does not pick up typescript files with `exactOptionalPropertyTypes`
146 property 'sonar.typescript.tsconfigPath', 'tsconfig.sonar.json'
147}
diff --git a/subprojects/language-web/package.json b/subprojects/language-web/package.json
new file mode 100644
index 00000000..5fa977d9
--- /dev/null
+++ b/subprojects/language-web/package.json
@@ -0,0 +1,103 @@
1{
2 "name": "@refinery/language-web",
3 "version": "0.0.0",
4 "description": "Web frontend for VIATRA-Generator",
5 "main": "index.js",
6 "scripts": {
7 "assemble:lezer": "lezer-generator src/main/js/language/problem.grammar -o build/generated/sources/lezer/problem.ts",
8 "assemble:webpack": "webpack --node-env production",
9 "serve": "webpack serve --node-env development --hot",
10 "check": "yarn run check:eslint && yarn run check:stylelint",
11 "check:eslint": "eslint .",
12 "check:eslint:ci": "eslint -f json -o build/eslint.json .",
13 "check:stylelint": "stylelint src/main/css/**/*.scss",
14 "check:stylelint:ci": "stylelint -f json src/main/css/**/*.scss > build/stylelint.json"
15 },
16 "repository": {
17 "type": "git",
18 "url": "git+https://github.com/graphs4value/refinery.git"
19 },
20 "author": "VIATRA-Generator authors",
21 "license": "EPL-2.0",
22 "bugs": {
23 "url": "https://github.com/graphs4value/issues"
24 },
25 "homepage": "https://refinery.tools",
26 "devDependencies": {
27 "@babel/core": "^7.16.0",
28 "@babel/plugin-transform-runtime": "^7.16.4",
29 "@babel/preset-env": "^7.16.4",
30 "@babel/preset-react": "^7.16.0",
31 "@babel/preset-typescript": "^7.16.0",
32 "@lezer/generator": "^0.15.2",
33 "@principalstudio/html-webpack-inject-preload": "^1.2.7",
34 "@types/react": "^17.0.37",
35 "@types/react-dom": "^17.0.11",
36 "@typescript-eslint/eslint-plugin": "^5.6.0",
37 "@typescript-eslint/parser": "^5.6.0",
38 "babel-loader": "^8.2.3",
39 "css-loader": "^6.5.1",
40 "eslint": "^8.4.1",
41 "eslint-config-airbnb": "^19.0.2",
42 "eslint-config-airbnb-typescript": "^16.1.0",
43 "eslint-import-resolver-node": "^0.3.6",
44 "eslint-plugin-import": "^2.25.3",
45 "eslint-plugin-jsx-a11y": "^6.5.1",
46 "eslint-plugin-react": "^7.27.1",
47 "eslint-plugin-react-hooks": "^4.3.0",
48 "html-webpack-plugin": "^5.5.0",
49 "image-webpack-loader": "^8.0.1",
50 "magic-comments-loader": "^1.4.1",
51 "mini-css-extract-plugin": "^2.4.5",
52 "postcss": "^8.4.4",
53 "postcss-scss": "^4.0.2",
54 "sass": "^1.45.0",
55 "sass-loader": "^12.4.0",
56 "style-loader": "^3.3.1",
57 "stylelint": "^14.1.0",
58 "stylelint-config-recommended-scss": "^5.0.2",
59 "stylelint-scss": "^4.0.1",
60 "typescript": "~4.5.3",
61 "webpack": "^5.65.0",
62 "webpack-cli": "^4.9.1",
63 "webpack-dev-server": "^4.6.0",
64 "webpack-subresource-integrity": "^5.0.0"
65 },
66 "dependencies": {
67 "@babel/runtime": "^7.16.3",
68 "@codemirror/autocomplete": "^0.19.9",
69 "@codemirror/closebrackets": "^0.19.0",
70 "@codemirror/commands": "^0.19.6",
71 "@codemirror/comment": "^0.19.0",
72 "@codemirror/fold": "^0.19.2",
73 "@codemirror/gutter": "^0.19.9",
74 "@codemirror/highlight": "^0.19.6",
75 "@codemirror/history": "^0.19.0",
76 "@codemirror/language": "^0.19.7",
77 "@codemirror/lint": "^0.19.3",
78 "@codemirror/matchbrackets": "^0.19.3",
79 "@codemirror/rangeset": "^0.19.2",
80 "@codemirror/rectangular-selection": "^0.19.1",
81 "@codemirror/search": "^0.19.4",
82 "@codemirror/state": "^0.19.6",
83 "@codemirror/view": "^0.19.29",
84 "@emotion/react": "^11.7.0",
85 "@emotion/styled": "^11.6.0",
86 "@fontsource/jetbrains-mono": "^4.5.0",
87 "@fontsource/roboto": "^4.5.1",
88 "@lezer/common": "^0.15.10",
89 "@lezer/lr": "^0.15.5",
90 "@mui/icons-material": "5.2.1",
91 "@mui/material": "5.2.3",
92 "ansi-styles": "^6.1.0",
93 "escape-string-regexp": "^5.0.0",
94 "loglevel": "^1.8.0",
95 "loglevel-plugin-prefix": "^0.8.4",
96 "mobx": "^6.3.8",
97 "mobx-react-lite": "^3.2.2",
98 "nanoid": "^3.1.30",
99 "react": "^17.0.2",
100 "react-dom": "^17.0.2",
101 "zod": "^3.11.6"
102 }
103}
diff --git a/subprojects/language-web/src/main/css/index.scss b/subprojects/language-web/src/main/css/index.scss
new file mode 100644
index 00000000..ad876aaf
--- /dev/null
+++ b/subprojects/language-web/src/main/css/index.scss
@@ -0,0 +1,16 @@
1@use '@fontsource/roboto/scss/mixins' as Roboto;
2@use '@fontsource/jetbrains-mono/scss/mixins' as JetbrainsMono;
3
4$fontWeights: 300, 400, 500, 700;
5@each $weight in $fontWeights {
6 @include Roboto.fontFace($fontName: 'Roboto', $weight: $weight);
7 @include Roboto.fontFace($fontName: 'Roboto', $weight: $weight, $style: italic);
8}
9
10$monoFontWeights: 400, 700;
11@each $weight in $monoFontWeights {
12 @include JetbrainsMono.fontFace($fontName: 'JetBrains Mono', $weight: $weight);
13 @include JetbrainsMono.fontFace($fontName: 'JetBrains Mono', $weight: $weight, $style: italic);
14}
15@include JetbrainsMono.fontFaceVariable($fontName: 'JetBrains MonoVariable');
16@include JetbrainsMono.fontFaceVariable($fontName: 'JetBrains MonoVariable', $style: italic);
diff --git a/subprojects/language-web/src/main/css/themeVariables.module.scss b/subprojects/language-web/src/main/css/themeVariables.module.scss
new file mode 100644
index 00000000..85af4219
--- /dev/null
+++ b/subprojects/language-web/src/main/css/themeVariables.module.scss
@@ -0,0 +1,9 @@
1@import './themes';
2
3:export {
4 @each $themeName, $theme in $themes {
5 @each $variable, $value in $theme {
6 #{$themeName}--#{$variable}: $value,
7 }
8 }
9}
diff --git a/subprojects/language-web/src/main/css/themes.scss b/subprojects/language-web/src/main/css/themes.scss
new file mode 100644
index 00000000..a30f1de3
--- /dev/null
+++ b/subprojects/language-web/src/main/css/themes.scss
@@ -0,0 +1,38 @@
1$themes: (
2 'dark': (
3 'foreground': #abb2bf,
4 'foregroundHighlight': #eeffff,
5 'background': #212121,
6 'primary': #56b6c2,
7 'secondary': #ff5370,
8 'keyword': #56b6c2,
9 'predicate': #d6e9ff,
10 'variable': #c8ae9d,
11 'uniqueNode': #d6e9ff,
12 'number': #6e88a6,
13 'delimiter': #707787,
14 'comment': #5c6370,
15 'cursor': #56b6c2,
16 'selection': #3e4452,
17 'currentLine': rgba(0, 0, 0, 0.2),
18 'lineNumber': #5c6370,
19 ),
20 'light': (
21 'foreground': #abb2bf,
22 'background': #282c34,
23 'paper': #21252b,
24 'primary': #56b6c2,
25 'secondary': #ff5370,
26 'keyword': #56b6c2,
27 'predicate': #d6e9ff,
28 'variable': #c8ae9d,
29 'uniqueNode': #d6e9ff,
30 'number': #6e88a6,
31 'delimiter': #56606d,
32 'comment': #55606d,
33 'cursor': #f3efe7,
34 'selection': #3e4452,
35 'currentLine': #2c323c,
36 'lineNumber': #5c6370,
37 ),
38);
diff --git a/subprojects/language-web/src/main/html/index.html b/subprojects/language-web/src/main/html/index.html
new file mode 100644
index 00000000..f404aa8a
--- /dev/null
+++ b/subprojects/language-web/src/main/html/index.html
@@ -0,0 +1,16 @@
1<!DOCTYPE html>
2<html lang="en-US">
3 <head>
4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1">
6 <title>Refinery</title>
7 </head>
8 <body>
9 <noscript>
10 <p>
11 This application requires JavaScript to run.
12 </p>
13 </noscript>
14 <div id="app"></div>
15 </body>
16</html>
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java
new file mode 100644
index 00000000..b13ae95d
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java
@@ -0,0 +1,52 @@
1package tools.refinery.language.web;
2
3import java.io.IOException;
4import java.time.Duration;
5import java.util.regex.Pattern;
6
7import org.eclipse.jetty.http.HttpHeader;
8
9import jakarta.servlet.Filter;
10import jakarta.servlet.FilterChain;
11import jakarta.servlet.FilterConfig;
12import jakarta.servlet.ServletException;
13import jakarta.servlet.ServletRequest;
14import jakarta.servlet.ServletResponse;
15import jakarta.servlet.http.HttpServletRequest;
16import jakarta.servlet.http.HttpServletResponse;
17
18public class CacheControlFilter implements Filter {
19 private static final Pattern CACHE_URI_PATTERN = Pattern.compile(".*\\.(css|gif|js|map|png|svg|woff2)");
20
21 private static final Duration EXPIRY = Duration.ofDays(365);
22
23 private static final String CACHE_CONTROL_CACHE_VALUE = "public, max-age: " + EXPIRY.toSeconds() + ", immutable";
24
25 private static final String CACHE_CONTROL_NO_CACHE_VALUE = "no-cache, no-store, max-age: 0, must-revalidate";
26
27 @Override
28 public void init(FilterConfig filterConfig) throws ServletException {
29 // Nothing to initialize.
30 }
31
32 @Override
33 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
34 throws IOException, ServletException {
35 if (request instanceof HttpServletRequest httpRequest && response instanceof HttpServletResponse httpResponse) {
36 if (CACHE_URI_PATTERN.matcher(httpRequest.getRequestURI()).matches()) {
37 httpResponse.setHeader(HttpHeader.CACHE_CONTROL.asString(), CACHE_CONTROL_CACHE_VALUE);
38 httpResponse.setDateHeader(HttpHeader.EXPIRES.asString(),
39 System.currentTimeMillis() + EXPIRY.toMillis());
40 } else {
41 httpResponse.setHeader(HttpHeader.CACHE_CONTROL.asString(), CACHE_CONTROL_NO_CACHE_VALUE);
42 httpResponse.setDateHeader(HttpHeader.EXPIRES.asString(), 0);
43 }
44 }
45 chain.doFilter(request, response);
46 }
47
48 @Override
49 public void destroy() {
50 // Nothing to dispose.
51 }
52}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebModule.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebModule.java
new file mode 100644
index 00000000..ec55036f
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebModule.java
@@ -0,0 +1,35 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language.web;
5
6import org.eclipse.xtext.web.server.XtextServiceDispatcher;
7import org.eclipse.xtext.web.server.model.IWebDocumentProvider;
8import org.eclipse.xtext.web.server.model.XtextWebDocumentAccess;
9import org.eclipse.xtext.web.server.occurrences.OccurrencesService;
10
11import tools.refinery.language.web.occurrences.ProblemOccurrencesService;
12import tools.refinery.language.web.xtext.server.push.PushServiceDispatcher;
13import tools.refinery.language.web.xtext.server.push.PushWebDocumentAccess;
14import tools.refinery.language.web.xtext.server.push.PushWebDocumentProvider;
15
16/**
17 * Use this class to register additional components to be used within the web application.
18 */
19public class ProblemWebModule extends AbstractProblemWebModule {
20 public Class<? extends IWebDocumentProvider> bindIWebDocumentProvider() {
21 return PushWebDocumentProvider.class;
22 }
23
24 public Class<? extends XtextWebDocumentAccess> bindXtextWebDocumentAccess() {
25 return PushWebDocumentAccess.class;
26 }
27
28 public Class<? extends XtextServiceDispatcher> bindXtextServiceDispatcher() {
29 return PushServiceDispatcher.class;
30 }
31
32 public Class<? extends OccurrencesService> bindOccurrencesService() {
33 return ProblemOccurrencesService.class;
34 }
35}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebSetup.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebSetup.java
new file mode 100644
index 00000000..4738bc80
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebSetup.java
@@ -0,0 +1,25 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language.web;
5
6import org.eclipse.xtext.util.Modules2;
7
8import com.google.inject.Guice;
9import com.google.inject.Injector;
10
11import tools.refinery.language.ProblemRuntimeModule;
12import tools.refinery.language.ProblemStandaloneSetup;
13import tools.refinery.language.ide.ProblemIdeModule;
14
15/**
16 * Initialization support for running Xtext languages in web applications.
17 */
18public class ProblemWebSetup extends ProblemStandaloneSetup {
19
20 @Override
21 public Injector createInjector() {
22 return Guice.createInjector(Modules2.mixin(new ProblemRuntimeModule(), new ProblemIdeModule(), new ProblemWebModule()));
23 }
24
25}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebSocketServlet.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebSocketServlet.java
new file mode 100644
index 00000000..df67b521
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebSocketServlet.java
@@ -0,0 +1,29 @@
1package tools.refinery.language.web;
2
3import org.eclipse.xtext.util.DisposableRegistry;
4
5import jakarta.servlet.ServletException;
6import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet;
7
8public class ProblemWebSocketServlet extends XtextWebSocketServlet {
9
10 private static final long serialVersionUID = -7040955470384797008L;
11
12 private transient DisposableRegistry disposableRegistry;
13
14 @Override
15 public void init() throws ServletException {
16 super.init();
17 var injector = new ProblemWebSetup().createInjectorAndDoEMFRegistration();
18 this.disposableRegistry = injector.getInstance(DisposableRegistry.class);
19 }
20
21 @Override
22 public void destroy() {
23 if (disposableRegistry != null) {
24 disposableRegistry.dispose();
25 disposableRegistry = null;
26 }
27 super.destroy();
28 }
29}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java
new file mode 100644
index 00000000..ffd903d0
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java
@@ -0,0 +1,192 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language.web;
5
6import java.io.File;
7import java.io.IOException;
8import java.net.InetSocketAddress;
9import java.net.URI;
10import java.net.URISyntaxException;
11import java.util.EnumSet;
12import java.util.Optional;
13import java.util.Set;
14
15import org.eclipse.jetty.server.Server;
16import org.eclipse.jetty.server.session.SessionHandler;
17import org.eclipse.jetty.servlet.DefaultServlet;
18import org.eclipse.jetty.servlet.ServletContextHandler;
19import org.eclipse.jetty.servlet.ServletHolder;
20import org.eclipse.jetty.util.resource.Resource;
21import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
22import org.slf4j.Logger;
23import org.slf4j.LoggerFactory;
24
25import jakarta.servlet.DispatcherType;
26import jakarta.servlet.SessionTrackingMode;
27import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet;
28
29public class ServerLauncher {
30 public static final String DEFAULT_LISTEN_ADDRESS = "localhost";
31
32 public static final int DEFAULT_LISTEN_PORT = 1312;
33
34 public static final int DEFAULT_PUBLIC_PORT = 443;
35
36 public static final int HTTP_DEFAULT_PORT = 80;
37
38 public static final int HTTPS_DEFAULT_PORT = 443;
39
40 public static final String ALLOWED_ORIGINS_SEPARATOR = ";";
41
42 private static final Logger LOG = LoggerFactory.getLogger(ServerLauncher.class);
43
44 private final Server server;
45
46 public ServerLauncher(InetSocketAddress bindAddress, Resource baseResource, Optional<String[]> allowedOrigins) {
47 server = new Server(bindAddress);
48 var handler = new ServletContextHandler();
49 addSessionHandler(handler);
50 addProblemServlet(handler, allowedOrigins);
51 if (baseResource != null) {
52 handler.setBaseResource(baseResource);
53 handler.setWelcomeFiles(new String[] { "index.html" });
54 addDefaultServlet(handler);
55 }
56 handler.addFilter(CacheControlFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
57 server.setHandler(handler);
58 }
59
60 private void addSessionHandler(ServletContextHandler handler) {
61 var sessionHandler = new SessionHandler();
62 sessionHandler.setSessionTrackingModes(Set.of(SessionTrackingMode.COOKIE));
63 handler.setSessionHandler(sessionHandler);
64 }
65
66 private void addProblemServlet(ServletContextHandler handler, Optional<String[]> allowedOrigins) {
67 var problemServletHolder = new ServletHolder(ProblemWebSocketServlet.class);
68 if (allowedOrigins.isEmpty()) {
69 LOG.warn("All WebSocket origins are allowed! This setting should not be used in production!");
70 } else {
71 var allowedOriginsString = String.join(XtextWebSocketServlet.ALLOWED_ORIGINS_SEPARATOR,
72 allowedOrigins.get());
73 problemServletHolder.setInitParameter(XtextWebSocketServlet.ALLOWED_ORIGINS_INIT_PARAM,
74 allowedOriginsString);
75 }
76 handler.addServlet(problemServletHolder, "/xtext-service");
77 JettyWebSocketServletContainerInitializer.configure(handler, null);
78 }
79
80 private void addDefaultServlet(ServletContextHandler handler) {
81 var defaultServletHolder = new ServletHolder(DefaultServlet.class);
82 var isWindows = System.getProperty("os.name").toLowerCase().contains("win");
83 // Avoid file locking on Windows: https://stackoverflow.com/a/4985717
84 // See also the related Jetty ticket:
85 // https://github.com/eclipse/jetty.project/issues/2925
86 defaultServletHolder.setInitParameter("useFileMappedBuffer", isWindows ? "false" : "true");
87 handler.addServlet(defaultServletHolder, "/");
88 }
89
90 public void start() throws Exception {
91 server.start();
92 LOG.info("Server started on {}", server.getURI());
93 server.join();
94 }
95
96 public static void main(String[] args) {
97 try {
98 var bindAddress = getBindAddress();
99 var baseResource = getBaseResource();
100 var allowedOrigins = getAllowedOrigins();
101 var serverLauncher = new ServerLauncher(bindAddress, baseResource, allowedOrigins);
102 serverLauncher.start();
103 } catch (Exception exception) {
104 LOG.error("Fatal server error", exception);
105 System.exit(1);
106 }
107 }
108
109 private static String getListenAddress() {
110 var listenAddress = System.getenv("LISTEN_ADDRESS");
111 if (listenAddress == null) {
112 return DEFAULT_LISTEN_ADDRESS;
113 }
114 return listenAddress;
115 }
116
117 private static int getListenPort() {
118 var portStr = System.getenv("LISTEN_PORT");
119 if (portStr != null) {
120 return Integer.parseInt(portStr);
121 }
122 return DEFAULT_LISTEN_PORT;
123 }
124
125 private static InetSocketAddress getBindAddress() {
126 var listenAddress = getListenAddress();
127 var listenPort = getListenPort();
128 return new InetSocketAddress(listenAddress, listenPort);
129 }
130
131 private static Resource getBaseResource() throws IOException, URISyntaxException {
132 var baseResourceOverride = System.getenv("BASE_RESOURCE");
133 if (baseResourceOverride != null) {
134 // If a user override is provided, use it.
135 return Resource.newResource(baseResourceOverride);
136 }
137 var indexUrlInJar = ServerLauncher.class.getResource("/webapp/index.html");
138 if (indexUrlInJar != null) {
139 // If the app is packaged in the jar, serve it.
140 var webRootUri = URI.create(indexUrlInJar.toURI().toASCIIString().replaceFirst("/index.html$", "/"));
141 return Resource.newResource(webRootUri);
142 }
143 // Look for unpacked production artifacts (convenience for running from IDE).
144 var unpackedResourcePathComponents = new String[] { System.getProperty("user.dir"), "build", "webpack",
145 "production" };
146 var unpackedResourceDir = new File(String.join(File.separator, unpackedResourcePathComponents));
147 if (unpackedResourceDir.isDirectory()) {
148 return Resource.newResource(unpackedResourceDir);
149 }
150 // Fall back to just serving a 404.
151 return null;
152 }
153
154 private static String getPublicHost() {
155 var publicHost = System.getenv("PUBLIC_HOST");
156 if (publicHost != null) {
157 return publicHost.toLowerCase();
158 }
159 return null;
160 }
161
162 private static int getPublicPort() {
163 var portStr = System.getenv("PUBLIC_PORT");
164 if (portStr != null) {
165 return Integer.parseInt(portStr);
166 }
167 return DEFAULT_LISTEN_PORT;
168 }
169
170 private static Optional<String[]> getAllowedOrigins() {
171 var allowedOrigins = System.getenv("ALLOWED_ORIGINS");
172 if (allowedOrigins != null) {
173 return Optional.of(allowedOrigins.split(ALLOWED_ORIGINS_SEPARATOR));
174 }
175 return getAllowedOriginsFromPublicHostAndPort();
176 }
177
178 private static Optional<String[]> getAllowedOriginsFromPublicHostAndPort() {
179 var publicHost = getPublicHost();
180 if (publicHost == null) {
181 return Optional.empty();
182 }
183 int publicPort = getPublicPort();
184 var scheme = publicPort == HTTPS_DEFAULT_PORT ? "https" : "http";
185 var urlWithPort = String.format("%s://%s:%d", scheme, publicHost, publicPort);
186 if (publicPort == HTTPS_DEFAULT_PORT || publicPort == HTTP_DEFAULT_PORT) {
187 var urlWithoutPort = String.format("%s://%s", scheme, publicHost);
188 return Optional.of(new String[] { urlWithPort, urlWithoutPort });
189 }
190 return Optional.of(new String[] { urlWithPort });
191 }
192}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/occurrences/ProblemOccurrencesService.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/occurrences/ProblemOccurrencesService.java
new file mode 100644
index 00000000..d32bbb54
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/occurrences/ProblemOccurrencesService.java
@@ -0,0 +1,16 @@
1package tools.refinery.language.web.occurrences;
2
3import org.eclipse.emf.ecore.EObject;
4import org.eclipse.xtext.web.server.occurrences.OccurrencesService;
5
6import com.google.inject.Singleton;
7
8import tools.refinery.language.model.problem.NamedElement;
9
10@Singleton
11public class ProblemOccurrencesService extends OccurrencesService {
12 @Override
13 protected boolean filter(EObject element) {
14 return super.filter(element) && element instanceof NamedElement;
15 }
16}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/PongResult.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/PongResult.java
new file mode 100644
index 00000000..fe510f51
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/PongResult.java
@@ -0,0 +1,44 @@
1package tools.refinery.language.web.xtext.server;
2
3import java.util.Objects;
4
5import org.eclipse.xtext.web.server.IServiceResult;
6
7public class PongResult implements IServiceResult {
8 private String pong;
9
10 public PongResult(String pong) {
11 super();
12 this.pong = pong;
13 }
14
15 public String getPong() {
16 return pong;
17 }
18
19 public void setPong(String pong) {
20 this.pong = pong;
21 }
22
23 @Override
24 public int hashCode() {
25 return Objects.hash(pong);
26 }
27
28 @Override
29 public boolean equals(Object obj) {
30 if (this == obj)
31 return true;
32 if (obj == null)
33 return false;
34 if (getClass() != obj.getClass())
35 return false;
36 PongResult other = (PongResult) obj;
37 return Objects.equals(pong, other.pong);
38 }
39
40 @Override
41 public String toString() {
42 return "PongResult [pong=" + pong + "]";
43 }
44}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ResponseHandler.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ResponseHandler.java
new file mode 100644
index 00000000..2a85afe3
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ResponseHandler.java
@@ -0,0 +1,8 @@
1package tools.refinery.language.web.xtext.server;
2
3import tools.refinery.language.web.xtext.server.message.XtextWebResponse;
4
5@FunctionalInterface
6public interface ResponseHandler {
7 void onResponse(XtextWebResponse response) throws ResponseHandlerException;
8}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ResponseHandlerException.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ResponseHandlerException.java
new file mode 100644
index 00000000..34fcb546
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ResponseHandlerException.java
@@ -0,0 +1,14 @@
1package tools.refinery.language.web.xtext.server;
2
3public class ResponseHandlerException extends Exception {
4
5 private static final long serialVersionUID = 3589866922420268164L;
6
7 public ResponseHandlerException(String message, Throwable cause) {
8 super(message, cause);
9 }
10
11 public ResponseHandlerException(String message) {
12 super(message);
13 }
14}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/SubscribingServiceContext.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/SubscribingServiceContext.java
new file mode 100644
index 00000000..78e00a9e
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/SubscribingServiceContext.java
@@ -0,0 +1,26 @@
1package tools.refinery.language.web.xtext.server;
2
3import java.util.Set;
4
5import org.eclipse.xtext.web.server.IServiceContext;
6import org.eclipse.xtext.web.server.ISession;
7
8import tools.refinery.language.web.xtext.server.push.PrecomputationListener;
9
10public record SubscribingServiceContext(IServiceContext delegate, PrecomputationListener subscriber)
11 implements IServiceContext {
12 @Override
13 public Set<String> getParameterKeys() {
14 return delegate.getParameterKeys();
15 }
16
17 @Override
18 public String getParameter(String key) {
19 return delegate.getParameter(key);
20 }
21
22 @Override
23 public ISession getSession() {
24 return delegate.getSession();
25 }
26}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java
new file mode 100644
index 00000000..0b417b06
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java
@@ -0,0 +1,180 @@
1package tools.refinery.language.web.xtext.server;
2
3import java.lang.ref.WeakReference;
4import java.util.ArrayList;
5import java.util.HashMap;
6import java.util.List;
7import java.util.Map;
8
9import org.eclipse.emf.common.util.URI;
10import org.eclipse.xtext.resource.IResourceServiceProvider;
11import org.eclipse.xtext.util.IDisposable;
12import org.eclipse.xtext.web.server.IServiceContext;
13import org.eclipse.xtext.web.server.IServiceResult;
14import org.eclipse.xtext.web.server.ISession;
15import org.eclipse.xtext.web.server.InvalidRequestException;
16import org.eclipse.xtext.web.server.InvalidRequestException.UnknownLanguageException;
17import org.eclipse.xtext.web.server.XtextServiceDispatcher;
18import org.slf4j.Logger;
19import org.slf4j.LoggerFactory;
20
21import com.google.common.base.Strings;
22import com.google.inject.Injector;
23
24import tools.refinery.language.web.xtext.server.message.XtextWebErrorKind;
25import tools.refinery.language.web.xtext.server.message.XtextWebErrorResponse;
26import tools.refinery.language.web.xtext.server.message.XtextWebOkResponse;
27import tools.refinery.language.web.xtext.server.message.XtextWebPushMessage;
28import tools.refinery.language.web.xtext.server.message.XtextWebRequest;
29import tools.refinery.language.web.xtext.server.push.PrecomputationListener;
30import tools.refinery.language.web.xtext.server.push.PushWebDocument;
31import tools.refinery.language.web.xtext.servlet.SimpleServiceContext;
32
33public class TransactionExecutor implements IDisposable, PrecomputationListener {
34 private static final Logger LOG = LoggerFactory.getLogger(TransactionExecutor.class);
35
36 private final ISession session;
37
38 private final IResourceServiceProvider.Registry resourceServiceProviderRegistry;
39
40 private final Map<String, WeakReference<PushWebDocument>> subscriptions = new HashMap<>();
41
42 private ResponseHandler responseHandler;
43
44 private Object callPendingLock = new Object();
45
46 private boolean callPending;
47
48 private List<XtextWebPushMessage> pendingPushMessages = new ArrayList<>();
49
50 public TransactionExecutor(ISession session, IResourceServiceProvider.Registry resourceServiceProviderRegistry) {
51 this.session = session;
52 this.resourceServiceProviderRegistry = resourceServiceProviderRegistry;
53 }
54
55 public void setResponseHandler(ResponseHandler responseHandler) {
56 this.responseHandler = responseHandler;
57 }
58
59 public void handleRequest(XtextWebRequest request) throws ResponseHandlerException {
60 var serviceContext = new SimpleServiceContext(session, request.getRequestData());
61 var ping = serviceContext.getParameter("ping");
62 if (ping != null) {
63 responseHandler.onResponse(new XtextWebOkResponse(request, new PongResult(ping)));
64 return;
65 }
66 synchronized (callPendingLock) {
67 if (callPending) {
68 LOG.error("Reentrant request detected");
69 }
70 if (!pendingPushMessages.isEmpty()) {
71 LOG.error("{} push messages got stuck without a pending request", pendingPushMessages.size());
72 }
73 callPending = true;
74 }
75 try {
76 var injector = getInjector(serviceContext);
77 var serviceDispatcher = injector.getInstance(XtextServiceDispatcher.class);
78 var service = serviceDispatcher.getService(new SubscribingServiceContext(serviceContext, this));
79 var serviceResult = service.getService().apply();
80 responseHandler.onResponse(new XtextWebOkResponse(request, serviceResult));
81 } catch (InvalidRequestException e) {
82 responseHandler.onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.REQUEST_ERROR, e));
83 } catch (RuntimeException e) {
84 responseHandler.onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.SERVER_ERROR, e));
85 } finally {
86 synchronized (callPendingLock) {
87 for (var message : pendingPushMessages) {
88 try {
89 responseHandler.onResponse(message);
90 } catch (ResponseHandlerException | RuntimeException e) {
91 LOG.error("Error while flushing push message", e);
92 }
93 }
94 pendingPushMessages.clear();
95 callPending = false;
96 }
97 }
98 }
99
100 @Override
101 public void onPrecomputedServiceResult(String resourceId, String stateId, String serviceName,
102 IServiceResult serviceResult) throws ResponseHandlerException {
103 var message = new XtextWebPushMessage(resourceId, stateId, serviceName, serviceResult);
104 synchronized (callPendingLock) {
105 // If we're currently responding to a call we must delay any push messages until
106 // the reply is sent, because push messages relating to the new state id must be
107 // sent after the response with the new state id so that the client knows about
108 // the new state when it receives the push message.
109 if (callPending) {
110 pendingPushMessages.add(message);
111 } else {
112 responseHandler.onResponse(message);
113 }
114 }
115 }
116
117 @Override
118 public void onSubscribeToPrecomputationEvents(String resourceId, PushWebDocument document) {
119 PushWebDocument previousDocument = null;
120 var previousSubscription = subscriptions.get(resourceId);
121 if (previousSubscription != null) {
122 previousDocument = previousSubscription.get();
123 }
124 if (previousDocument == document) {
125 return;
126 }
127 if (previousDocument != null) {
128 previousDocument.removePrecomputationListener(this);
129 }
130 subscriptions.put(resourceId, new WeakReference<>(document));
131 }
132
133 /**
134 * Get the injector to satisfy the request in the {@code serviceContext}.
135 *
136 * Based on {@link org.eclipse.xtext.web.servlet.XtextServlet#getInjector}.
137 *
138 * @param serviceContext the Xtext service context of the request
139 * @return the injector for the Xtext language in the request
140 * @throws UnknownLanguageException if the Xtext language cannot be determined
141 */
142 protected Injector getInjector(IServiceContext context) {
143 IResourceServiceProvider resourceServiceProvider = null;
144 var resourceName = context.getParameter("resource");
145 if (resourceName == null) {
146 resourceName = "";
147 }
148 var emfURI = URI.createURI(resourceName);
149 var contentType = context.getParameter("contentType");
150 if (Strings.isNullOrEmpty(contentType)) {
151 resourceServiceProvider = resourceServiceProviderRegistry.getResourceServiceProvider(emfURI);
152 if (resourceServiceProvider == null) {
153 if (emfURI.toString().isEmpty()) {
154 throw new UnknownLanguageException(
155 "Unable to identify the Xtext language: missing parameter 'resource' or 'contentType'.");
156 } else {
157 throw new UnknownLanguageException(
158 "Unable to identify the Xtext language for resource " + emfURI + ".");
159 }
160 }
161 } else {
162 resourceServiceProvider = resourceServiceProviderRegistry.getResourceServiceProvider(emfURI, contentType);
163 if (resourceServiceProvider == null) {
164 throw new UnknownLanguageException(
165 "Unable to identify the Xtext language for contentType " + contentType + ".");
166 }
167 }
168 return resourceServiceProvider.get(Injector.class);
169 }
170
171 @Override
172 public void dispose() {
173 for (var subscription : subscriptions.values()) {
174 var document = subscription.get();
175 if (document != null) {
176 document.removePrecomputationListener(this);
177 }
178 }
179 }
180}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebErrorKind.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebErrorKind.java
new file mode 100644
index 00000000..f74bae74
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebErrorKind.java
@@ -0,0 +1,11 @@
1package tools.refinery.language.web.xtext.server.message;
2
3import com.google.gson.annotations.SerializedName;
4
5public enum XtextWebErrorKind {
6 @SerializedName("request")
7 REQUEST_ERROR,
8
9 @SerializedName("server")
10 SERVER_ERROR,
11}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebErrorResponse.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebErrorResponse.java
new file mode 100644
index 00000000..01d78c31
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebErrorResponse.java
@@ -0,0 +1,79 @@
1package tools.refinery.language.web.xtext.server.message;
2
3import java.util.Objects;
4
5import com.google.gson.annotations.SerializedName;
6
7public final class XtextWebErrorResponse implements XtextWebResponse {
8 private String id;
9
10 @SerializedName("error")
11 private XtextWebErrorKind errorKind;
12
13 @SerializedName("message")
14 private String errorMessage;
15
16 public XtextWebErrorResponse(String id, XtextWebErrorKind errorKind, String errorMessage) {
17 super();
18 this.id = id;
19 this.errorKind = errorKind;
20 this.errorMessage = errorMessage;
21 }
22
23 public XtextWebErrorResponse(XtextWebRequest request, XtextWebErrorKind errorKind,
24 String errorMessage) {
25 this(request.getId(), errorKind, errorMessage);
26 }
27
28 public XtextWebErrorResponse(XtextWebRequest request, XtextWebErrorKind errorKind, Throwable t) {
29 this(request, errorKind, t.getMessage());
30 }
31
32 public String getId() {
33 return id;
34 }
35
36 public void setId(String id) {
37 this.id = id;
38 }
39
40 public XtextWebErrorKind getErrorKind() {
41 return errorKind;
42 }
43
44 public void setErrorKind(XtextWebErrorKind errorKind) {
45 this.errorKind = errorKind;
46 }
47
48 public String getErrorMessage() {
49 return errorMessage;
50 }
51
52 public void setErrorMessage(String errorMessage) {
53 this.errorMessage = errorMessage;
54 }
55
56 @Override
57 public int hashCode() {
58 return Objects.hash(errorKind, errorMessage, id);
59 }
60
61 @Override
62 public boolean equals(Object obj) {
63 if (this == obj)
64 return true;
65 if (obj == null)
66 return false;
67 if (getClass() != obj.getClass())
68 return false;
69 XtextWebErrorResponse other = (XtextWebErrorResponse) obj;
70 return errorKind == other.errorKind && Objects.equals(errorMessage, other.errorMessage)
71 && Objects.equals(id, other.id);
72 }
73
74 @Override
75 public String toString() {
76 return "XtextWebSocketErrorResponse [id=" + id + ", errorKind=" + errorKind + ", errorMessage=" + errorMessage
77 + "]";
78 }
79}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebOkResponse.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebOkResponse.java
new file mode 100644
index 00000000..8af27247
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebOkResponse.java
@@ -0,0 +1,72 @@
1package tools.refinery.language.web.xtext.server.message;
2
3import java.util.Objects;
4
5import org.eclipse.xtext.web.server.IServiceResult;
6import org.eclipse.xtext.web.server.IUnwrappableServiceResult;
7
8import com.google.gson.annotations.SerializedName;
9
10public final class XtextWebOkResponse implements XtextWebResponse {
11 private String id;
12
13 @SerializedName("response")
14 private Object responseData;
15
16 public XtextWebOkResponse(String id, Object responseData) {
17 super();
18 this.id = id;
19 this.responseData = responseData;
20 }
21
22 public XtextWebOkResponse(XtextWebRequest request, IServiceResult result) {
23 this(request.getId(), maybeUnwrap(result));
24 }
25
26 public String getId() {
27 return id;
28 }
29
30 public void setId(String id) {
31 this.id = id;
32 }
33
34 public Object getResponseData() {
35 return responseData;
36 }
37
38 public void setResponseData(Object responseData) {
39 this.responseData = responseData;
40 }
41
42 @Override
43 public int hashCode() {
44 return Objects.hash(id, responseData);
45 }
46
47 @Override
48 public boolean equals(Object obj) {
49 if (this == obj)
50 return true;
51 if (obj == null)
52 return false;
53 if (getClass() != obj.getClass())
54 return false;
55 XtextWebOkResponse other = (XtextWebOkResponse) obj;
56 return Objects.equals(id, other.id) && Objects.equals(responseData, other.responseData);
57 }
58
59 @Override
60 public String toString() {
61 return "XtextWebSocketOkResponse [id=" + id + ", responseData=" + responseData + "]";
62 }
63
64 private static Object maybeUnwrap(IServiceResult result) {
65 if (result instanceof IUnwrappableServiceResult unwrappableServiceResult
66 && unwrappableServiceResult.getContent() != null) {
67 return unwrappableServiceResult.getContent();
68 } else {
69 return result;
70 }
71 }
72}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebPushMessage.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebPushMessage.java
new file mode 100644
index 00000000..c9432e1c
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebPushMessage.java
@@ -0,0 +1,81 @@
1package tools.refinery.language.web.xtext.server.message;
2
3import java.util.Objects;
4
5import com.google.gson.annotations.SerializedName;
6
7public final class XtextWebPushMessage implements XtextWebResponse {
8 @SerializedName("resource")
9 private String resourceId;
10
11 private String stateId;
12
13 private String service;
14
15 @SerializedName("push")
16 private Object pushData;
17
18 public XtextWebPushMessage(String resourceId, String stateId, String service, Object pushData) {
19 super();
20 this.resourceId = resourceId;
21 this.stateId = stateId;
22 this.service = service;
23 this.pushData = pushData;
24 }
25
26 public String getResourceId() {
27 return resourceId;
28 }
29
30 public void setResourceId(String resourceId) {
31 this.resourceId = resourceId;
32 }
33
34 public String getStateId() {
35 return stateId;
36 }
37
38 public void setStateId(String stateId) {
39 this.stateId = stateId;
40 }
41
42 public String getService() {
43 return service;
44 }
45
46 public void setService(String service) {
47 this.service = service;
48 }
49
50 public Object getPushData() {
51 return pushData;
52 }
53
54 public void setPushData(Object pushData) {
55 this.pushData = pushData;
56 }
57
58 @Override
59 public int hashCode() {
60 return Objects.hash(pushData, resourceId, service, stateId);
61 }
62
63 @Override
64 public boolean equals(Object obj) {
65 if (this == obj)
66 return true;
67 if (obj == null)
68 return false;
69 if (getClass() != obj.getClass())
70 return false;
71 XtextWebPushMessage other = (XtextWebPushMessage) obj;
72 return Objects.equals(pushData, other.pushData) && Objects.equals(resourceId, other.resourceId)
73 && Objects.equals(service, other.service) && Objects.equals(stateId, other.stateId);
74 }
75
76 @Override
77 public String toString() {
78 return "XtextWebPushMessage [resourceId=" + resourceId + ", stateId=" + stateId + ", service=" + service
79 + ", pushData=" + pushData + "]";
80 }
81}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebRequest.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebRequest.java
new file mode 100644
index 00000000..959749f8
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebRequest.java
@@ -0,0 +1,57 @@
1package tools.refinery.language.web.xtext.server.message;
2
3import java.util.Map;
4import java.util.Objects;
5
6import com.google.gson.annotations.SerializedName;
7
8public class XtextWebRequest {
9 private String id;
10
11 @SerializedName("request")
12 private Map<String, String> requestData;
13
14 public XtextWebRequest(String id, Map<String, String> requestData) {
15 super();
16 this.id = id;
17 this.requestData = requestData;
18 }
19
20 public String getId() {
21 return id;
22 }
23
24 public void setId(String id) {
25 this.id = id;
26 }
27
28 public Map<String, String> getRequestData() {
29 return requestData;
30 }
31
32 public void setRequestData(Map<String, String> requestData) {
33 this.requestData = requestData;
34 }
35
36 @Override
37 public int hashCode() {
38 return Objects.hash(id, requestData);
39 }
40
41 @Override
42 public boolean equals(Object obj) {
43 if (this == obj)
44 return true;
45 if (obj == null)
46 return false;
47 if (getClass() != obj.getClass())
48 return false;
49 XtextWebRequest other = (XtextWebRequest) obj;
50 return Objects.equals(id, other.id) && Objects.equals(requestData, other.requestData);
51 }
52
53 @Override
54 public String toString() {
55 return "XtextWebSocketRequest [id=" + id + ", requestData=" + requestData + "]";
56 }
57}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebResponse.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebResponse.java
new file mode 100644
index 00000000..3bd13047
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/message/XtextWebResponse.java
@@ -0,0 +1,4 @@
1package tools.refinery.language.web.xtext.server.message;
2
3public sealed interface XtextWebResponse permits XtextWebOkResponse,XtextWebErrorResponse,XtextWebPushMessage {
4}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PrecomputationListener.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PrecomputationListener.java
new file mode 100644
index 00000000..79a284db
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PrecomputationListener.java
@@ -0,0 +1,15 @@
1package tools.refinery.language.web.xtext.server.push;
2
3import org.eclipse.xtext.web.server.IServiceResult;
4
5import tools.refinery.language.web.xtext.server.ResponseHandlerException;
6
7@FunctionalInterface
8public interface PrecomputationListener {
9 void onPrecomputedServiceResult(String resourceId, String stateId, String serviceName, IServiceResult serviceResult)
10 throws ResponseHandlerException;
11
12 default void onSubscribeToPrecomputationEvents(String resourceId, PushWebDocument document) {
13 // Nothing to handle by default.
14 }
15}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushServiceDispatcher.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushServiceDispatcher.java
new file mode 100644
index 00000000..c7b8108d
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushServiceDispatcher.java
@@ -0,0 +1,23 @@
1package tools.refinery.language.web.xtext.server.push;
2
3import org.eclipse.xtext.web.server.IServiceContext;
4import org.eclipse.xtext.web.server.XtextServiceDispatcher;
5import org.eclipse.xtext.web.server.model.XtextWebDocument;
6
7import com.google.inject.Singleton;
8
9import tools.refinery.language.web.xtext.server.SubscribingServiceContext;
10
11@Singleton
12public class PushServiceDispatcher extends XtextServiceDispatcher {
13
14 @Override
15 protected XtextWebDocument getFullTextDocument(String fullText, String resourceId, IServiceContext context) {
16 var document = super.getFullTextDocument(fullText, resourceId, context);
17 if (document instanceof PushWebDocument pushWebDocument
18 && context instanceof SubscribingServiceContext subscribingContext) {
19 pushWebDocument.addPrecomputationListener(subscribingContext.subscriber());
20 }
21 return document;
22 }
23}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java
new file mode 100644
index 00000000..906b9e30
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java
@@ -0,0 +1,89 @@
1package tools.refinery.language.web.xtext.server.push;
2
3import java.util.ArrayList;
4import java.util.HashMap;
5import java.util.List;
6import java.util.Map;
7
8import org.eclipse.xtext.util.CancelIndicator;
9import org.eclipse.xtext.web.server.IServiceResult;
10import org.eclipse.xtext.web.server.model.AbstractCachedService;
11import org.eclipse.xtext.web.server.model.DocumentSynchronizer;
12import org.eclipse.xtext.web.server.model.XtextWebDocument;
13import org.slf4j.Logger;
14import org.slf4j.LoggerFactory;
15
16import com.google.common.collect.ImmutableList;
17
18import tools.refinery.language.web.xtext.server.ResponseHandlerException;
19
20public class PushWebDocument extends XtextWebDocument {
21 private static final Logger LOG = LoggerFactory.getLogger(PushWebDocument.class);
22
23 private final List<PrecomputationListener> precomputationListeners = new ArrayList<>();
24
25 private final Map<Class<?>, IServiceResult> precomputedServices = new HashMap<>();
26
27 public PushWebDocument(String resourceId, DocumentSynchronizer synchronizer) {
28 super(resourceId, synchronizer);
29 if (resourceId == null) {
30 throw new IllegalArgumentException("resourceId must not be null");
31 }
32 }
33
34 public boolean addPrecomputationListener(PrecomputationListener listener) {
35 synchronized (precomputationListeners) {
36 if (precomputationListeners.contains(listener)) {
37 return false;
38 }
39 precomputationListeners.add(listener);
40 listener.onSubscribeToPrecomputationEvents(getResourceId(), this);
41 return true;
42 }
43 }
44
45 public boolean removePrecomputationListener(PrecomputationListener listener) {
46 synchronized (precomputationListeners) {
47 return precomputationListeners.remove(listener);
48 }
49 }
50
51 public <T extends IServiceResult> void precomputeServiceResult(AbstractCachedService<T> service, String serviceName,
52 CancelIndicator cancelIndicator, boolean logCacheMiss) {
53 var result = getCachedServiceResult(service, cancelIndicator, logCacheMiss);
54 if (result == null) {
55 LOG.error("{} service returned null result", serviceName);
56 return;
57 }
58 var serviceClass = service.getClass();
59 var previousResult = precomputedServices.get(serviceClass);
60 if (previousResult != null && previousResult.equals(result)) {
61 return;
62 }
63 precomputedServices.put(serviceClass, result);
64 notifyPrecomputationListeners(serviceName, result);
65 }
66
67 private <T extends IServiceResult> void notifyPrecomputationListeners(String serviceName, T result) {
68 var resourceId = getResourceId();
69 var stateId = getStateId();
70 List<PrecomputationListener> copyOfListeners;
71 synchronized (precomputationListeners) {
72 copyOfListeners = ImmutableList.copyOf(precomputationListeners);
73 }
74 var toRemove = new ArrayList<PrecomputationListener>();
75 for (var listener : copyOfListeners) {
76 try {
77 listener.onPrecomputedServiceResult(resourceId, stateId, serviceName, result);
78 } catch (ResponseHandlerException e) {
79 LOG.error("Delivering precomputation push message failed", e);
80 toRemove.add(listener);
81 }
82 }
83 if (!toRemove.isEmpty()) {
84 synchronized (precomputationListeners) {
85 precomputationListeners.removeAll(toRemove);
86 }
87 }
88 }
89}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentAccess.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentAccess.java
new file mode 100644
index 00000000..b3666a86
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentAccess.java
@@ -0,0 +1,68 @@
1package tools.refinery.language.web.xtext.server.push;
2
3import org.eclipse.xtext.service.OperationCanceledManager;
4import org.eclipse.xtext.util.CancelIndicator;
5import org.eclipse.xtext.util.concurrent.CancelableUnitOfWork;
6import org.eclipse.xtext.web.server.IServiceResult;
7import org.eclipse.xtext.web.server.model.AbstractCachedService;
8import org.eclipse.xtext.web.server.model.IXtextWebDocument;
9import org.eclipse.xtext.web.server.model.PrecomputedServiceRegistry;
10import org.eclipse.xtext.web.server.model.XtextWebDocument;
11import org.eclipse.xtext.web.server.model.XtextWebDocumentAccess;
12import org.eclipse.xtext.web.server.syntaxcoloring.HighlightingService;
13import org.eclipse.xtext.web.server.validation.ValidationService;
14
15import com.google.inject.Inject;
16
17public class PushWebDocumentAccess extends XtextWebDocumentAccess {
18
19 @Inject
20 private PrecomputedServiceRegistry preComputedServiceRegistry;
21
22 @Inject
23 private OperationCanceledManager operationCanceledManager;
24
25 private PushWebDocument pushDocument;
26
27 @Override
28 protected void init(XtextWebDocument document, String requiredStateId, boolean skipAsyncWork) {
29 super.init(document, requiredStateId, skipAsyncWork);
30 if (document instanceof PushWebDocument newPushDocument) {
31 pushDocument = newPushDocument;
32 }
33 }
34
35 @Override
36 protected void performPrecomputation(CancelIndicator cancelIndicator) {
37 if (pushDocument == null) {
38 super.performPrecomputation(cancelIndicator);
39 return;
40 }
41 for (AbstractCachedService<? extends IServiceResult> service : preComputedServiceRegistry
42 .getPrecomputedServices()) {
43 operationCanceledManager.checkCanceled(cancelIndicator);
44 precomputeServiceResult(service, false);
45 }
46 }
47
48 protected <T extends IServiceResult> void precomputeServiceResult(AbstractCachedService<T> service, boolean logCacheMiss) {
49 var serviceName = getPrecomputedServiceName(service);
50 readOnly(new CancelableUnitOfWork<Void, IXtextWebDocument>() {
51 @Override
52 public java.lang.Void exec(IXtextWebDocument d, CancelIndicator cancelIndicator) throws Exception {
53 pushDocument.precomputeServiceResult(service, serviceName, cancelIndicator, logCacheMiss);
54 return null;
55 }
56 });
57 }
58
59 protected String getPrecomputedServiceName(AbstractCachedService<? extends IServiceResult> service) {
60 if (service instanceof ValidationService) {
61 return "validate";
62 }
63 if (service instanceof HighlightingService) {
64 return "highlight";
65 }
66 throw new IllegalArgumentException("Unknown precomputed service: " + service);
67 }
68}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java
new file mode 100644
index 00000000..fc45f74a
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java
@@ -0,0 +1,33 @@
1package tools.refinery.language.web.xtext.server.push;
2
3import org.eclipse.xtext.web.server.IServiceContext;
4import org.eclipse.xtext.web.server.model.DocumentSynchronizer;
5import org.eclipse.xtext.web.server.model.IWebDocumentProvider;
6import org.eclipse.xtext.web.server.model.XtextWebDocument;
7
8import com.google.inject.Inject;
9import com.google.inject.Provider;
10import com.google.inject.Singleton;
11
12/**
13 * Based on
14 * {@link org.eclipse.xtext.web.server.model.IWebDocumentProvider.DefaultImpl}.
15 *
16 * @author Kristóf Marussy
17 */
18@Singleton
19public class PushWebDocumentProvider implements IWebDocumentProvider {
20 @Inject
21 private Provider<DocumentSynchronizer> synchronizerProvider;
22
23 @Override
24 public XtextWebDocument get(String resourceId, IServiceContext serviceContext) {
25 if (resourceId == null) {
26 return new XtextWebDocument(resourceId, synchronizerProvider.get());
27 } else {
28 // We only need to send push messages if a resourceId is specified.
29 return new PushWebDocument(resourceId,
30 serviceContext.getSession().get(DocumentSynchronizer.class, () -> this.synchronizerProvider.get()));
31 }
32 }
33}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/SimpleServiceContext.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/SimpleServiceContext.java
new file mode 100644
index 00000000..43e37160
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/SimpleServiceContext.java
@@ -0,0 +1,26 @@
1package tools.refinery.language.web.xtext.servlet;
2
3import java.util.Map;
4import java.util.Set;
5
6import org.eclipse.xtext.web.server.IServiceContext;
7import org.eclipse.xtext.web.server.ISession;
8
9import com.google.common.collect.ImmutableSet;
10
11public record SimpleServiceContext(ISession session, Map<String, String> parameters) implements IServiceContext {
12 @Override
13 public Set<String> getParameterKeys() {
14 return ImmutableSet.copyOf(parameters.keySet());
15 }
16
17 @Override
18 public String getParameter(String key) {
19 return parameters.get(key);
20 }
21
22 @Override
23 public ISession getSession() {
24 return session;
25 }
26}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/SimpleSession.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/SimpleSession.java
new file mode 100644
index 00000000..09c055a2
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/SimpleSession.java
@@ -0,0 +1,35 @@
1package tools.refinery.language.web.xtext.servlet;
2
3import java.util.HashMap;
4import java.util.Map;
5
6import org.eclipse.xtext.web.server.ISession;
7import org.eclipse.xtext.xbase.lib.Functions.Function0;
8
9public class SimpleSession implements ISession {
10 private Map<Object, Object> map = new HashMap<>();
11
12 @Override
13 public <T> T get(Object key) {
14 @SuppressWarnings("unchecked")
15 var value = (T) map.get(key);
16 return value;
17 }
18
19 @Override
20 public <T> T get(Object key, Function0<? extends T> factory) {
21 @SuppressWarnings("unchecked")
22 var value = (T) map.computeIfAbsent(key, absentKey -> factory.apply());
23 return value;
24 }
25
26 @Override
27 public void put(Object key, Object value) {
28 map.put(key, value);
29 }
30
31 @Override
32 public void remove(Object key) {
33 map.remove(key);
34 }
35}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextStatusCode.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextStatusCode.java
new file mode 100644
index 00000000..0cd229e8
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextStatusCode.java
@@ -0,0 +1,9 @@
1package tools.refinery.language.web.xtext.servlet;
2
3public final class XtextStatusCode {
4 public static final int INVALID_JSON = 4007;
5
6 private XtextStatusCode() {
7 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
8 }
9}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java
new file mode 100644
index 00000000..fd41f213
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java
@@ -0,0 +1,133 @@
1package tools.refinery.language.web.xtext.servlet;
2
3import java.io.IOException;
4import java.io.Reader;
5
6import org.eclipse.jetty.websocket.api.Session;
7import org.eclipse.jetty.websocket.api.StatusCode;
8import org.eclipse.jetty.websocket.api.WriteCallback;
9import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
10import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
11import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
12import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
13import org.eclipse.jetty.websocket.api.annotations.WebSocket;
14import org.eclipse.xtext.resource.IResourceServiceProvider;
15import org.eclipse.xtext.web.server.ISession;
16import org.slf4j.Logger;
17import org.slf4j.LoggerFactory;
18
19import com.google.gson.Gson;
20import com.google.gson.JsonIOException;
21import com.google.gson.JsonParseException;
22
23import tools.refinery.language.web.xtext.server.ResponseHandler;
24import tools.refinery.language.web.xtext.server.ResponseHandlerException;
25import tools.refinery.language.web.xtext.server.TransactionExecutor;
26import tools.refinery.language.web.xtext.server.message.XtextWebRequest;
27import tools.refinery.language.web.xtext.server.message.XtextWebResponse;
28
29@WebSocket
30public class XtextWebSocket implements WriteCallback, ResponseHandler {
31 private static final Logger LOG = LoggerFactory.getLogger(XtextWebSocket.class);
32
33 private final Gson gson = new Gson();
34
35 private final TransactionExecutor executor;
36
37 private Session webSocketSession;
38
39 public XtextWebSocket(TransactionExecutor executor) {
40 this.executor = executor;
41 executor.setResponseHandler(this);
42 }
43
44 public XtextWebSocket(ISession session, IResourceServiceProvider.Registry resourceServiceProviderRegistry) {
45 this(new TransactionExecutor(session, resourceServiceProviderRegistry));
46 }
47
48 @OnWebSocketConnect
49 public void onConnect(Session webSocketSession) {
50 if (this.webSocketSession != null) {
51 LOG.error("Websocket session onConnect when already connected");
52 return;
53 }
54 LOG.debug("New websocket connection from {}", webSocketSession.getRemoteAddress());
55 this.webSocketSession = webSocketSession;
56 }
57
58 @OnWebSocketClose
59 public void onClose(int statusCode, String reason) {
60 executor.dispose();
61 if (webSocketSession == null) {
62 return;
63 }
64 if (statusCode == StatusCode.NORMAL || statusCode == StatusCode.SHUTDOWN) {
65 LOG.debug("{} closed connection normally: {}", webSocketSession.getRemoteAddress(), reason);
66 } else {
67 LOG.warn("{} closed connection with status code {}: {}", webSocketSession.getRemoteAddress(), statusCode,
68 reason);
69 }
70 webSocketSession = null;
71 }
72
73 @OnWebSocketError
74 public void onError(Throwable error) {
75 if (webSocketSession == null) {
76 return;
77 }
78 LOG.error("Internal websocket error in connection from" + webSocketSession.getRemoteAddress(), error);
79 }
80
81 @OnWebSocketMessage
82 public void onMessage(Reader reader) {
83 if (webSocketSession == null) {
84 LOG.error("Trying to receive message when websocket is disconnected");
85 return;
86 }
87 XtextWebRequest request;
88 try {
89 request = gson.fromJson(reader, XtextWebRequest.class);
90 } catch (JsonIOException e) {
91 LOG.error("Cannot read from websocket from" + webSocketSession.getRemoteAddress(), e);
92 if (webSocketSession.isOpen()) {
93 webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot read payload");
94 }
95 return;
96 } catch (JsonParseException e) {
97 LOG.warn("Malformed websocket request from" + webSocketSession.getRemoteAddress(), e);
98 webSocketSession.close(XtextStatusCode.INVALID_JSON, "Invalid JSON payload");
99 return;
100 }
101 try {
102 executor.handleRequest(request);
103 } catch (ResponseHandlerException e) {
104 LOG.warn("Cannot write websocket response", e);
105 if (webSocketSession.isOpen()) {
106 webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot write response");
107 }
108 }
109 }
110
111 @Override
112 public void onResponse(XtextWebResponse response) throws ResponseHandlerException {
113 if (webSocketSession == null) {
114 throw new ResponseHandlerException("Trying to send message when websocket is disconnected");
115 }
116 var responseString = gson.toJson(response);
117 try {
118 webSocketSession.getRemote().sendPartialString(responseString, true, this);
119 } catch (IOException e) {
120 throw new ResponseHandlerException(
121 "Cannot initiaite async write to websocket " + webSocketSession.getRemoteAddress(), e);
122 }
123 }
124
125 @Override
126 public void writeFailed(Throwable x) {
127 if (webSocketSession == null) {
128 LOG.error("Cannot complete async write to disconnected websocket", x);
129 return;
130 }
131 LOG.warn("Cannot complete async write to websocket " + webSocketSession.getRemoteAddress(), x);
132 }
133}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketServlet.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketServlet.java
new file mode 100644
index 00000000..942ca380
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketServlet.java
@@ -0,0 +1,83 @@
1package tools.refinery.language.web.xtext.servlet;
2
3import java.io.IOException;
4import java.time.Duration;
5import java.util.Set;
6
7import org.eclipse.jetty.websocket.server.JettyServerUpgradeRequest;
8import org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse;
9import org.eclipse.jetty.websocket.server.JettyWebSocketCreator;
10import org.eclipse.jetty.websocket.server.JettyWebSocketServlet;
11import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory;
12import org.eclipse.xtext.resource.IResourceServiceProvider;
13import org.slf4j.Logger;
14import org.slf4j.LoggerFactory;
15
16import jakarta.servlet.ServletConfig;
17import jakarta.servlet.ServletException;
18
19public abstract class XtextWebSocketServlet extends JettyWebSocketServlet implements JettyWebSocketCreator {
20
21 private static final long serialVersionUID = -3772740838165122685L;
22
23 public static final String ALLOWED_ORIGINS_SEPARATOR = ";";
24
25 public static final String ALLOWED_ORIGINS_INIT_PARAM = "tools.refinery.language.web.xtext.XtextWebSocketServlet.allowedOrigin";
26
27 public static final String XTEXT_SUBPROTOCOL_V1 = "tools.refinery.language.web.xtext.v1";
28
29 /**
30 * Maximum message size should be large enough to upload a full model file.
31 */
32 private static final long MAX_FRAME_SIZE = 4L * 1024L * 1024L;
33
34 private static final Duration IDLE_TIMEOUT = Duration.ofSeconds(30);
35
36 private transient Logger log = LoggerFactory.getLogger(getClass());
37
38 private transient Set<String> allowedOrigins = null;
39
40 @Override
41 public void init(ServletConfig config) throws ServletException {
42 var allowedOriginsStr = config.getInitParameter(ALLOWED_ORIGINS_INIT_PARAM);
43 if (allowedOriginsStr == null) {
44 log.warn("All WebSocket origins are allowed! This setting should not be used in production!");
45 } else {
46 allowedOrigins = Set.of(allowedOriginsStr.split(ALLOWED_ORIGINS_SEPARATOR));
47 log.info("Allowed origins: {}", allowedOrigins);
48 }
49 super.init(config);
50 }
51
52 @Override
53 protected void configure(JettyWebSocketServletFactory factory) {
54 factory.setMaxFrameSize(MAX_FRAME_SIZE);
55 factory.setIdleTimeout(IDLE_TIMEOUT);
56 factory.addMapping("/", this);
57 }
58
59 @Override
60 public Object createWebSocket(JettyServerUpgradeRequest req, JettyServerUpgradeResponse resp) {
61 if (allowedOrigins != null) {
62 var origin = req.getOrigin();
63 if (origin == null || !allowedOrigins.contains(origin.toLowerCase())) {
64 log.error("Connection from {} from forbidden origin {}", req.getRemoteSocketAddress(), origin);
65 try {
66 resp.sendForbidden("Origin not allowed");
67 } catch (IOException e) {
68 log.error("Cannot send forbidden origin error", e);
69 }
70 return null;
71 }
72 }
73 if (req.getSubProtocols().contains(XTEXT_SUBPROTOCOL_V1)) {
74 resp.setAcceptedSubProtocol(XTEXT_SUBPROTOCOL_V1);
75 } else {
76 log.error("None of the subprotocols {} offered by {} are supported", req.getSubProtocols(),
77 req.getRemoteSocketAddress());
78 resp.setAcceptedSubProtocol(null);
79 }
80 var session = new SimpleSession();
81 return new XtextWebSocket(session, IResourceServiceProvider.Registry.INSTANCE);
82 }
83}
diff --git a/subprojects/language-web/src/main/js/App.tsx b/subprojects/language-web/src/main/js/App.tsx
new file mode 100644
index 00000000..54f92f9a
--- /dev/null
+++ b/subprojects/language-web/src/main/js/App.tsx
@@ -0,0 +1,60 @@
1import AppBar from '@mui/material/AppBar';
2import Box from '@mui/material/Box';
3import IconButton from '@mui/material/IconButton';
4import Toolbar from '@mui/material/Toolbar';
5import Typography from '@mui/material/Typography';
6import MenuIcon from '@mui/icons-material/Menu';
7import React from 'react';
8
9import { EditorArea } from './editor/EditorArea';
10import { EditorButtons } from './editor/EditorButtons';
11import { GenerateButton } from './editor/GenerateButton';
12
13export function App(): JSX.Element {
14 return (
15 <Box
16 display="flex"
17 flexDirection="column"
18 sx={{ height: '100vh' }}
19 >
20 <AppBar
21 position="static"
22 color="inherit"
23 >
24 <Toolbar>
25 <IconButton
26 edge="start"
27 sx={{ mr: 2 }}
28 color="inherit"
29 aria-label="menu"
30 >
31 <MenuIcon />
32 </IconButton>
33 <Typography
34 variant="h6"
35 component="h1"
36 flexGrow={1}
37 >
38 Refinery
39 </Typography>
40 </Toolbar>
41 </AppBar>
42 <Box
43 display="flex"
44 justifyContent="space-between"
45 alignItems="center"
46 p={1}
47 >
48 <EditorButtons />
49 <GenerateButton />
50 </Box>
51 <Box
52 flexGrow={1}
53 flexShrink={1}
54 sx={{ overflow: 'auto' }}
55 >
56 <EditorArea />
57 </Box>
58 </Box>
59 );
60}
diff --git a/subprojects/language-web/src/main/js/RootStore.tsx b/subprojects/language-web/src/main/js/RootStore.tsx
new file mode 100644
index 00000000..baf0b61e
--- /dev/null
+++ b/subprojects/language-web/src/main/js/RootStore.tsx
@@ -0,0 +1,39 @@
1import React, { createContext, useContext } from 'react';
2
3import { EditorStore } from './editor/EditorStore';
4import { ThemeStore } from './theme/ThemeStore';
5
6export class RootStore {
7 editorStore;
8
9 themeStore;
10
11 constructor(initialValue: string) {
12 this.themeStore = new ThemeStore();
13 this.editorStore = new EditorStore(initialValue, this.themeStore);
14 }
15}
16
17const StoreContext = createContext<RootStore | undefined>(undefined);
18
19export interface RootStoreProviderProps {
20 children: JSX.Element;
21
22 rootStore: RootStore;
23}
24
25export function RootStoreProvider({ children, rootStore }: RootStoreProviderProps): JSX.Element {
26 return (
27 <StoreContext.Provider value={rootStore}>
28 {children}
29 </StoreContext.Provider>
30 );
31}
32
33export const useRootStore = (): RootStore => {
34 const rootStore = useContext(StoreContext);
35 if (!rootStore) {
36 throw new Error('useRootStore must be used within RootStoreProvider');
37 }
38 return rootStore;
39};
diff --git a/subprojects/language-web/src/main/js/editor/EditorArea.tsx b/subprojects/language-web/src/main/js/editor/EditorArea.tsx
new file mode 100644
index 00000000..dba20f6e
--- /dev/null
+++ b/subprojects/language-web/src/main/js/editor/EditorArea.tsx
@@ -0,0 +1,152 @@
1import { Command, EditorView } from '@codemirror/view';
2import { closeSearchPanel, openSearchPanel } from '@codemirror/search';
3import { closeLintPanel, openLintPanel } from '@codemirror/lint';
4import { observer } from 'mobx-react-lite';
5import React, {
6 useCallback,
7 useEffect,
8 useRef,
9 useState,
10} from 'react';
11
12import { EditorParent } from './EditorParent';
13import { useRootStore } from '../RootStore';
14import { getLogger } from '../utils/logger';
15
16const log = getLogger('editor.EditorArea');
17
18function usePanel(
19 panelId: string,
20 stateToSet: boolean,
21 editorView: EditorView | null,
22 openCommand: Command,
23 closeCommand: Command,
24 closeCallback: () => void,
25) {
26 const [cachedViewState, setCachedViewState] = useState<boolean>(false);
27 useEffect(() => {
28 if (editorView === null || cachedViewState === stateToSet) {
29 return;
30 }
31 if (stateToSet) {
32 openCommand(editorView);
33 const buttonQuery = `.cm-${panelId}.cm-panel button[name="close"]`;
34 const closeButton = editorView.dom.querySelector(buttonQuery);
35 if (closeButton) {
36 log.debug('Addig close button callback to', panelId, 'panel');
37 // We must remove the event listener added by CodeMirror from the button
38 // that dispatches a transaction without going through `EditorStorre`.
39 // Cloning a DOM node removes event listeners,
40 // see https://stackoverflow.com/a/9251864
41 const closeButtonWithoutListeners = closeButton.cloneNode(true);
42 closeButtonWithoutListeners.addEventListener('click', (event) => {
43 closeCallback();
44 event.preventDefault();
45 });
46 closeButton.replaceWith(closeButtonWithoutListeners);
47 } else {
48 log.error('Opened', panelId, 'panel has no close button');
49 }
50 } else {
51 closeCommand(editorView);
52 }
53 setCachedViewState(stateToSet);
54 }, [
55 stateToSet,
56 editorView,
57 cachedViewState,
58 panelId,
59 openCommand,
60 closeCommand,
61 closeCallback,
62 ]);
63 return setCachedViewState;
64}
65
66function fixCodeMirrorAccessibility(editorView: EditorView) {
67 // Reported by Lighthouse 8.3.0.
68 const { contentDOM } = editorView;
69 contentDOM.removeAttribute('aria-expanded');
70 contentDOM.setAttribute('aria-label', 'Code editor');
71}
72
73export const EditorArea = observer(() => {
74 const { editorStore } = useRootStore();
75 const editorParentRef = useRef<HTMLDivElement | null>(null);
76 const [editorViewState, setEditorViewState] = useState<EditorView | null>(null);
77
78 const setSearchPanelOpen = usePanel(
79 'search',
80 editorStore.showSearchPanel,
81 editorViewState,
82 openSearchPanel,
83 closeSearchPanel,
84 useCallback(() => editorStore.setSearchPanelOpen(false), [editorStore]),
85 );
86
87 const setLintPanelOpen = usePanel(
88 'panel-lint',
89 editorStore.showLintPanel,
90 editorViewState,
91 openLintPanel,
92 closeLintPanel,
93 useCallback(() => editorStore.setLintPanelOpen(false), [editorStore]),
94 );
95
96 useEffect(() => {
97 if (editorParentRef.current === null) {
98 return () => {
99 // Nothing to clean up.
100 };
101 }
102
103 const editorView = new EditorView({
104 state: editorStore.state,
105 parent: editorParentRef.current,
106 dispatch: (transaction) => {
107 editorStore.onTransaction(transaction);
108 editorView.update([transaction]);
109 if (editorView.state !== editorStore.state) {
110 log.error(
111 'Failed to synchronize editor state - store state:',
112 editorStore.state,
113 'view state:',
114 editorView.state,
115 );
116 }
117 },
118 });
119 fixCodeMirrorAccessibility(editorView);
120 setEditorViewState(editorView);
121 setSearchPanelOpen(false);
122 setLintPanelOpen(false);
123 // `dispatch` is bound to the view instance,
124 // so it does not have to be called as a method.
125 // eslint-disable-next-line @typescript-eslint/unbound-method
126 editorStore.updateDispatcher(editorView.dispatch);
127 log.info('Editor created');
128
129 return () => {
130 editorStore.updateDispatcher(null);
131 editorView.destroy();
132 log.info('Editor destroyed');
133 };
134 }, [
135 editorParentRef,
136 editorStore,
137 setSearchPanelOpen,
138 setLintPanelOpen,
139 ]);
140
141 return (
142 <EditorParent
143 className="dark"
144 sx={{
145 '.cm-lineNumbers': editorStore.showLineNumbers ? {} : {
146 display: 'none !important',
147 },
148 }}
149 ref={editorParentRef}
150 />
151 );
152});
diff --git a/subprojects/language-web/src/main/js/editor/EditorButtons.tsx b/subprojects/language-web/src/main/js/editor/EditorButtons.tsx
new file mode 100644
index 00000000..150aa00d
--- /dev/null
+++ b/subprojects/language-web/src/main/js/editor/EditorButtons.tsx
@@ -0,0 +1,98 @@
1import type { Diagnostic } from '@codemirror/lint';
2import { observer } from 'mobx-react-lite';
3import IconButton from '@mui/material/IconButton';
4import Stack from '@mui/material/Stack';
5import ToggleButton from '@mui/material/ToggleButton';
6import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
7import CheckIcon from '@mui/icons-material/Check';
8import ErrorIcon from '@mui/icons-material/Error';
9import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered';
10import FormatPaint from '@mui/icons-material/FormatPaint';
11import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
12import RedoIcon from '@mui/icons-material/Redo';
13import SearchIcon from '@mui/icons-material/Search';
14import UndoIcon from '@mui/icons-material/Undo';
15import WarningIcon from '@mui/icons-material/Warning';
16import React from 'react';
17
18import { useRootStore } from '../RootStore';
19
20// Exhastive switch as proven by TypeScript.
21// eslint-disable-next-line consistent-return
22function getLintIcon(severity: Diagnostic['severity'] | null) {
23 switch (severity) {
24 case 'error':
25 return <ErrorIcon fontSize="small" />;
26 case 'warning':
27 return <WarningIcon fontSize="small" />;
28 case 'info':
29 return <InfoOutlinedIcon fontSize="small" />;
30 case null:
31 return <CheckIcon fontSize="small" />;
32 }
33}
34
35export const EditorButtons = observer(() => {
36 const { editorStore } = useRootStore();
37
38 return (
39 <Stack
40 direction="row"
41 spacing={1}
42 >
43 <Stack
44 direction="row"
45 alignItems="center"
46 >
47 <IconButton
48 disabled={!editorStore.canUndo}
49 onClick={() => editorStore.undo()}
50 aria-label="Undo"
51 >
52 <UndoIcon fontSize="small" />
53 </IconButton>
54 <IconButton
55 disabled={!editorStore.canRedo}
56 onClick={() => editorStore.redo()}
57 aria-label="Redo"
58 >
59 <RedoIcon fontSize="small" />
60 </IconButton>
61 </Stack>
62 <ToggleButtonGroup
63 size="small"
64 >
65 <ToggleButton
66 selected={editorStore.showLineNumbers}
67 onClick={() => editorStore.toggleLineNumbers()}
68 aria-label="Show line numbers"
69 value="show-line-numbers"
70 >
71 <FormatListNumberedIcon fontSize="small" />
72 </ToggleButton>
73 <ToggleButton
74 selected={editorStore.showSearchPanel}
75 onClick={() => editorStore.toggleSearchPanel()}
76 aria-label="Show find/replace"
77 value="show-search-panel"
78 >
79 <SearchIcon fontSize="small" />
80 </ToggleButton>
81 <ToggleButton
82 selected={editorStore.showLintPanel}
83 onClick={() => editorStore.toggleLintPanel()}
84 aria-label="Show diagnostics panel"
85 value="show-lint-panel"
86 >
87 {getLintIcon(editorStore.highestDiagnosticLevel)}
88 </ToggleButton>
89 </ToggleButtonGroup>
90 <IconButton
91 onClick={() => editorStore.formatText()}
92 aria-label="Automatic format"
93 >
94 <FormatPaint fontSize="small" />
95 </IconButton>
96 </Stack>
97 );
98});
diff --git a/subprojects/language-web/src/main/js/editor/EditorParent.ts b/subprojects/language-web/src/main/js/editor/EditorParent.ts
new file mode 100644
index 00000000..94ca24ea
--- /dev/null
+++ b/subprojects/language-web/src/main/js/editor/EditorParent.ts
@@ -0,0 +1,205 @@
1import { styled } from '@mui/material/styles';
2
3/**
4 * Returns a squiggly underline background image encoded as a CSS `url()` data URI with Base64.
5 *
6 * Based on
7 * https://github.com/codemirror/lint/blob/f524b4a53b0183bb343ac1e32b228d28030d17af/src/lint.ts#L501
8 *
9 * @param color the color of the underline
10 * @returns the CSS `url()`
11 */
12function underline(color: string) {
13 const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="6" height="3">
14 <path d="m0 3 l2 -2 l1 0 l2 2 l1 0" stroke="${color}" fill="none" stroke-width=".7"/>
15 </svg>`;
16 const svgBase64 = window.btoa(svg);
17 return `url('data:image/svg+xml;base64,${svgBase64}')`;
18}
19
20export const EditorParent = styled('div')(({ theme }) => {
21 const codeMirrorLintStyle: Record<string, unknown> = {};
22 (['error', 'warning', 'info'] as const).forEach((severity) => {
23 const color = theme.palette[severity].main;
24 codeMirrorLintStyle[`.cm-diagnostic-${severity}`] = {
25 borderLeftColor: color,
26 };
27 codeMirrorLintStyle[`.cm-lintRange-${severity}`] = {
28 backgroundImage: underline(color),
29 };
30 });
31
32 return {
33 background: theme.palette.background.default,
34 '&, .cm-editor': {
35 height: '100%',
36 },
37 '.cm-content': {
38 padding: 0,
39 },
40 '.cm-scroller, .cm-tooltip-autocomplete, .cm-completionLabel, .cm-completionDetail': {
41 fontSize: 16,
42 fontFamily: '"JetBrains MonoVariable", "JetBrains Mono", monospace',
43 fontFeatureSettings: '"liga", "calt"',
44 fontWeight: 400,
45 letterSpacing: 0,
46 textRendering: 'optimizeLegibility',
47 },
48 '.cm-scroller': {
49 color: theme.palette.text.secondary,
50 },
51 '.cm-gutters': {
52 background: 'rgba(255, 255, 255, 0.1)',
53 color: theme.palette.text.disabled,
54 border: 'none',
55 },
56 '.cm-specialChar': {
57 color: theme.palette.secondary.main,
58 },
59 '.cm-activeLine': {
60 background: 'rgba(0, 0, 0, 0.3)',
61 },
62 '.cm-activeLineGutter': {
63 background: 'transparent',
64 },
65 '.cm-lineNumbers .cm-activeLineGutter': {
66 color: theme.palette.text.primary,
67 },
68 '.cm-cursor, .cm-cursor-primary': {
69 borderColor: theme.palette.primary.main,
70 background: theme.palette.common.black,
71 },
72 '.cm-selectionBackground': {
73 background: '#3e4453',
74 },
75 '.cm-focused': {
76 outline: 'none',
77 '.cm-selectionBackground': {
78 background: '#3e4453',
79 },
80 },
81 '.cm-panels-top': {
82 color: theme.palette.text.secondary,
83 },
84 '.cm-panel': {
85 '&, & button, & input': {
86 fontFamily: '"Roboto","Helvetica","Arial",sans-serif',
87 },
88 background: theme.palette.background.paper,
89 borderTop: `1px solid ${theme.palette.divider}`,
90 'button[name="close"]': {
91 background: 'transparent',
92 color: theme.palette.text.secondary,
93 cursor: 'pointer',
94 },
95 },
96 '.cm-panel.cm-panel-lint': {
97 'button[name="close"]': {
98 // Close button interferes with scrollbar, so we better hide it.
99 // The panel can still be closed from the toolbar.
100 display: 'none',
101 },
102 ul: {
103 li: {
104 borderBottom: `1px solid ${theme.palette.divider}`,
105 cursor: 'pointer',
106 },
107 '[aria-selected]': {
108 background: '#3e4453',
109 color: theme.palette.text.primary,
110 },
111 '&:focus [aria-selected]': {
112 background: theme.palette.primary.main,
113 color: theme.palette.primary.contrastText,
114 },
115 },
116 },
117 '.cm-foldPlaceholder': {
118 background: theme.palette.background.paper,
119 borderColor: theme.palette.text.disabled,
120 color: theme.palette.text.secondary,
121 },
122 '.cmt-comment': {
123 fontStyle: 'italic',
124 color: theme.palette.text.disabled,
125 },
126 '.cmt-number': {
127 color: '#6188a6',
128 },
129 '.cmt-string': {
130 color: theme.palette.secondary.dark,
131 },
132 '.cmt-keyword': {
133 color: theme.palette.primary.main,
134 },
135 '.cmt-typeName, .cmt-macroName, .cmt-atom': {
136 color: theme.palette.text.primary,
137 },
138 '.cmt-variableName': {
139 color: '#c8ae9d',
140 },
141 '.cmt-problem-node': {
142 '&, & .cmt-variableName': {
143 color: theme.palette.text.secondary,
144 },
145 },
146 '.cmt-problem-individual': {
147 '&, & .cmt-variableName': {
148 color: theme.palette.text.primary,
149 },
150 },
151 '.cmt-problem-abstract, .cmt-problem-new': {
152 fontStyle: 'italic',
153 },
154 '.cmt-problem-containment': {
155 fontWeight: 700,
156 },
157 '.cmt-problem-error': {
158 '&, & .cmt-typeName': {
159 color: theme.palette.error.main,
160 },
161 },
162 '.cmt-problem-builtin': {
163 '&, & .cmt-typeName, & .cmt-atom, & .cmt-variableName': {
164 color: theme.palette.primary.main,
165 fontWeight: 400,
166 fontStyle: 'normal',
167 },
168 },
169 '.cm-tooltip-autocomplete': {
170 background: theme.palette.background.paper,
171 boxShadow: `0px 2px 4px -1px rgb(0 0 0 / 20%),
172 0px 4px 5px 0px rgb(0 0 0 / 14%),
173 0px 1px 10px 0px rgb(0 0 0 / 12%)`,
174 '.cm-completionIcon': {
175 color: theme.palette.text.secondary,
176 },
177 '.cm-completionLabel': {
178 color: theme.palette.text.primary,
179 },
180 '.cm-completionDetail': {
181 color: theme.palette.text.secondary,
182 fontStyle: 'normal',
183 },
184 '[aria-selected]': {
185 background: `${theme.palette.primary.main} !important`,
186 '.cm-completionIcon, .cm-completionLabel, .cm-completionDetail': {
187 color: theme.palette.primary.contrastText,
188 },
189 },
190 },
191 '.cm-completionIcon': {
192 width: 16,
193 padding: 0,
194 marginRight: '0.5em',
195 textAlign: 'center',
196 },
197 ...codeMirrorLintStyle,
198 '.cm-problem-write': {
199 background: 'rgba(255, 255, 128, 0.3)',
200 },
201 '.cm-problem-read': {
202 background: 'rgba(255, 255, 255, 0.15)',
203 },
204 };
205});
diff --git a/subprojects/language-web/src/main/js/editor/EditorStore.ts b/subprojects/language-web/src/main/js/editor/EditorStore.ts
new file mode 100644
index 00000000..5760de28
--- /dev/null
+++ b/subprojects/language-web/src/main/js/editor/EditorStore.ts
@@ -0,0 +1,289 @@
1import { autocompletion, completionKeymap } from '@codemirror/autocomplete';
2import { closeBrackets, closeBracketsKeymap } from '@codemirror/closebrackets';
3import { defaultKeymap, indentWithTab } from '@codemirror/commands';
4import { commentKeymap } from '@codemirror/comment';
5import { foldGutter, foldKeymap } from '@codemirror/fold';
6import { highlightActiveLineGutter, lineNumbers } from '@codemirror/gutter';
7import { classHighlightStyle } from '@codemirror/highlight';
8import {
9 history,
10 historyKeymap,
11 redo,
12 redoDepth,
13 undo,
14 undoDepth,
15} from '@codemirror/history';
16import { indentOnInput } from '@codemirror/language';
17import {
18 Diagnostic,
19 lintKeymap,
20 setDiagnostics,
21} from '@codemirror/lint';
22import { bracketMatching } from '@codemirror/matchbrackets';
23import { rectangularSelection } from '@codemirror/rectangular-selection';
24import { searchConfig, searchKeymap } from '@codemirror/search';
25import {
26 EditorState,
27 StateCommand,
28 StateEffect,
29 Transaction,
30 TransactionSpec,
31} from '@codemirror/state';
32import {
33 drawSelection,
34 EditorView,
35 highlightActiveLine,
36 highlightSpecialChars,
37 keymap,
38} from '@codemirror/view';
39import {
40 makeAutoObservable,
41 observable,
42 reaction,
43} from 'mobx';
44
45import { findOccurrences, IOccurrence, setOccurrences } from './findOccurrences';
46import { problemLanguageSupport } from '../language/problemLanguageSupport';
47import {
48 IHighlightRange,
49 semanticHighlighting,
50 setSemanticHighlighting,
51} from './semanticHighlighting';
52import type { ThemeStore } from '../theme/ThemeStore';
53import { getLogger } from '../utils/logger';
54import { XtextClient } from '../xtext/XtextClient';
55
56const log = getLogger('editor.EditorStore');
57
58export class EditorStore {
59 private readonly themeStore;
60
61 state: EditorState;
62
63 private readonly client: XtextClient;
64
65 showLineNumbers = false;
66
67 showSearchPanel = false;
68
69 showLintPanel = false;
70
71 errorCount = 0;
72
73 warningCount = 0;
74
75 infoCount = 0;
76
77 private readonly defaultDispatcher = (tr: Transaction): void => {
78 this.onTransaction(tr);
79 };
80
81 private dispatcher = this.defaultDispatcher;
82
83 constructor(initialValue: string, themeStore: ThemeStore) {
84 this.themeStore = themeStore;
85 this.state = EditorState.create({
86 doc: initialValue,
87 extensions: [
88 autocompletion({
89 activateOnTyping: true,
90 override: [
91 (context) => this.client.contentAssist(context),
92 ],
93 }),
94 classHighlightStyle.extension,
95 closeBrackets(),
96 bracketMatching(),
97 drawSelection(),
98 EditorState.allowMultipleSelections.of(true),
99 EditorView.theme({}, {
100 dark: this.themeStore.darkMode,
101 }),
102 findOccurrences,
103 highlightActiveLine(),
104 highlightActiveLineGutter(),
105 highlightSpecialChars(),
106 history(),
107 indentOnInput(),
108 rectangularSelection(),
109 searchConfig({
110 top: true,
111 matchCase: true,
112 }),
113 semanticHighlighting,
114 // We add the gutters to `extensions` in the order we want them to appear.
115 lineNumbers(),
116 foldGutter(),
117 keymap.of([
118 { key: 'Mod-Shift-f', run: () => this.formatText() },
119 ...closeBracketsKeymap,
120 ...commentKeymap,
121 ...completionKeymap,
122 ...foldKeymap,
123 ...historyKeymap,
124 indentWithTab,
125 // Override keys in `lintKeymap` to go through the `EditorStore`.
126 { key: 'Mod-Shift-m', run: () => this.setLintPanelOpen(true) },
127 ...lintKeymap,
128 // Override keys in `searchKeymap` to go through the `EditorStore`.
129 { key: 'Mod-f', run: () => this.setSearchPanelOpen(true), scope: 'editor search-panel' },
130 { key: 'Escape', run: () => this.setSearchPanelOpen(false), scope: 'editor search-panel' },
131 ...searchKeymap,
132 ...defaultKeymap,
133 ]),
134 problemLanguageSupport(),
135 ],
136 });
137 this.client = new XtextClient(this);
138 reaction(
139 () => this.themeStore.darkMode,
140 (darkMode) => {
141 log.debug('Update editor dark mode', darkMode);
142 this.dispatch({
143 effects: [
144 StateEffect.appendConfig.of(EditorView.theme({}, {
145 dark: darkMode,
146 })),
147 ],
148 });
149 },
150 );
151 makeAutoObservable(this, {
152 state: observable.ref,
153 });
154 }
155
156 updateDispatcher(newDispatcher: ((tr: Transaction) => void) | null): void {
157 this.dispatcher = newDispatcher || this.defaultDispatcher;
158 }
159
160 onTransaction(tr: Transaction): void {
161 log.trace('Editor transaction', tr);
162 this.state = tr.state;
163 this.client.onTransaction(tr);
164 }
165
166 dispatch(...specs: readonly TransactionSpec[]): void {
167 this.dispatcher(this.state.update(...specs));
168 }
169
170 doStateCommand(command: StateCommand): boolean {
171 return command({
172 state: this.state,
173 dispatch: this.dispatcher,
174 });
175 }
176
177 updateDiagnostics(diagnostics: Diagnostic[]): void {
178 this.dispatch(setDiagnostics(this.state, diagnostics));
179 this.errorCount = 0;
180 this.warningCount = 0;
181 this.infoCount = 0;
182 diagnostics.forEach(({ severity }) => {
183 switch (severity) {
184 case 'error':
185 this.errorCount += 1;
186 break;
187 case 'warning':
188 this.warningCount += 1;
189 break;
190 case 'info':
191 this.infoCount += 1;
192 break;
193 }
194 });
195 }
196
197 get highestDiagnosticLevel(): Diagnostic['severity'] | null {
198 if (this.errorCount > 0) {
199 return 'error';
200 }
201 if (this.warningCount > 0) {
202 return 'warning';
203 }
204 if (this.infoCount > 0) {
205 return 'info';
206 }
207 return null;
208 }
209
210 updateSemanticHighlighting(ranges: IHighlightRange[]): void {
211 this.dispatch(setSemanticHighlighting(ranges));
212 }
213
214 updateOccurrences(write: IOccurrence[], read: IOccurrence[]): void {
215 this.dispatch(setOccurrences(write, read));
216 }
217
218 /**
219 * @returns `true` if there is history to undo
220 */
221 get canUndo(): boolean {
222 return undoDepth(this.state) > 0;
223 }
224
225 // eslint-disable-next-line class-methods-use-this
226 undo(): void {
227 log.debug('Undo', this.doStateCommand(undo));
228 }
229
230 /**
231 * @returns `true` if there is history to redo
232 */
233 get canRedo(): boolean {
234 return redoDepth(this.state) > 0;
235 }
236
237 // eslint-disable-next-line class-methods-use-this
238 redo(): void {
239 log.debug('Redo', this.doStateCommand(redo));
240 }
241
242 toggleLineNumbers(): void {
243 this.showLineNumbers = !this.showLineNumbers;
244 log.debug('Show line numbers', this.showLineNumbers);
245 }
246
247 /**
248 * Sets whether the CodeMirror search panel should be open.
249 *
250 * This method can be used as a CodeMirror command,
251 * because it returns `false` if it didn't execute,
252 * allowing other commands for the same keybind to run instead.
253 * This matches the behavior of the `openSearchPanel` and `closeSearchPanel`
254 * commands from `'@codemirror/search'`.
255 *
256 * @param newShosSearchPanel whether we should show the search panel
257 * @returns `true` if the state was changed, `false` otherwise
258 */
259 setSearchPanelOpen(newShowSearchPanel: boolean): boolean {
260 if (this.showSearchPanel === newShowSearchPanel) {
261 return false;
262 }
263 this.showSearchPanel = newShowSearchPanel;
264 log.debug('Show search panel', this.showSearchPanel);
265 return true;
266 }
267
268 toggleSearchPanel(): void {
269 this.setSearchPanelOpen(!this.showSearchPanel);
270 }
271
272 setLintPanelOpen(newShowLintPanel: boolean): boolean {
273 if (this.showLintPanel === newShowLintPanel) {
274 return false;
275 }
276 this.showLintPanel = newShowLintPanel;
277 log.debug('Show lint panel', this.showLintPanel);
278 return true;
279 }
280
281 toggleLintPanel(): void {
282 this.setLintPanelOpen(!this.showLintPanel);
283 }
284
285 formatText(): boolean {
286 this.client.formatText();
287 return true;
288 }
289}
diff --git a/subprojects/language-web/src/main/js/editor/GenerateButton.tsx b/subprojects/language-web/src/main/js/editor/GenerateButton.tsx
new file mode 100644
index 00000000..3834cec4
--- /dev/null
+++ b/subprojects/language-web/src/main/js/editor/GenerateButton.tsx
@@ -0,0 +1,44 @@
1import { observer } from 'mobx-react-lite';
2import Button from '@mui/material/Button';
3import PlayArrowIcon from '@mui/icons-material/PlayArrow';
4import React from 'react';
5
6import { useRootStore } from '../RootStore';
7
8const GENERATE_LABEL = 'Generate';
9
10export const GenerateButton = observer(() => {
11 const { editorStore } = useRootStore();
12 const { errorCount, warningCount } = editorStore;
13
14 const diagnostics: string[] = [];
15 if (errorCount > 0) {
16 diagnostics.push(`${errorCount} error${errorCount === 1 ? '' : 's'}`);
17 }
18 if (warningCount > 0) {
19 diagnostics.push(`${warningCount} warning${warningCount === 1 ? '' : 's'}`);
20 }
21 const summary = diagnostics.join(' and ');
22
23 if (errorCount > 0) {
24 return (
25 <Button
26 variant="outlined"
27 color="error"
28 onClick={() => editorStore.toggleLintPanel()}
29 >
30 {summary}
31 </Button>
32 );
33 }
34
35 return (
36 <Button
37 variant="outlined"
38 color={warningCount > 0 ? 'warning' : 'primary'}
39 startIcon={<PlayArrowIcon />}
40 >
41 {summary === '' ? GENERATE_LABEL : `${GENERATE_LABEL} (${summary})`}
42 </Button>
43 );
44});
diff --git a/subprojects/language-web/src/main/js/editor/decorationSetExtension.ts b/subprojects/language-web/src/main/js/editor/decorationSetExtension.ts
new file mode 100644
index 00000000..2d630c20
--- /dev/null
+++ b/subprojects/language-web/src/main/js/editor/decorationSetExtension.ts
@@ -0,0 +1,39 @@
1import { StateEffect, StateField, TransactionSpec } from '@codemirror/state';
2import { EditorView, Decoration, DecorationSet } from '@codemirror/view';
3
4export type TransactionSpecFactory = (decorations: DecorationSet) => TransactionSpec;
5
6export function decorationSetExtension(): [TransactionSpecFactory, StateField<DecorationSet>] {
7 const setEffect = StateEffect.define<DecorationSet>();
8 const field = StateField.define<DecorationSet>({
9 create() {
10 return Decoration.none;
11 },
12 update(currentDecorations, transaction) {
13 let newDecorations: DecorationSet | null = null;
14 transaction.effects.forEach((effect) => {
15 if (effect.is(setEffect)) {
16 newDecorations = effect.value;
17 }
18 });
19 if (newDecorations === null) {
20 if (transaction.docChanged) {
21 return currentDecorations.map(transaction.changes);
22 }
23 return currentDecorations;
24 }
25 return newDecorations;
26 },
27 provide: (f) => EditorView.decorations.from(f),
28 });
29
30 function transactionSpecFactory(decorations: DecorationSet) {
31 return {
32 effects: [
33 setEffect.of(decorations),
34 ],
35 };
36 }
37
38 return [transactionSpecFactory, field];
39}
diff --git a/subprojects/language-web/src/main/js/editor/findOccurrences.ts b/subprojects/language-web/src/main/js/editor/findOccurrences.ts
new file mode 100644
index 00000000..92102746
--- /dev/null
+++ b/subprojects/language-web/src/main/js/editor/findOccurrences.ts
@@ -0,0 +1,35 @@
1import { Range, RangeSet } from '@codemirror/rangeset';
2import type { TransactionSpec } from '@codemirror/state';
3import { Decoration } from '@codemirror/view';
4
5import { decorationSetExtension } from './decorationSetExtension';
6
7export interface IOccurrence {
8 from: number;
9
10 to: number;
11}
12
13const [setOccurrencesInteral, findOccurrences] = decorationSetExtension();
14
15const writeDecoration = Decoration.mark({
16 class: 'cm-problem-write',
17});
18
19const readDecoration = Decoration.mark({
20 class: 'cm-problem-read',
21});
22
23export function setOccurrences(write: IOccurrence[], read: IOccurrence[]): TransactionSpec {
24 const decorations: Range<Decoration>[] = [];
25 write.forEach(({ from, to }) => {
26 decorations.push(writeDecoration.range(from, to));
27 });
28 read.forEach(({ from, to }) => {
29 decorations.push(readDecoration.range(from, to));
30 });
31 const rangeSet = RangeSet.of(decorations, true);
32 return setOccurrencesInteral(rangeSet);
33}
34
35export { findOccurrences };
diff --git a/subprojects/language-web/src/main/js/editor/semanticHighlighting.ts b/subprojects/language-web/src/main/js/editor/semanticHighlighting.ts
new file mode 100644
index 00000000..2aed421b
--- /dev/null
+++ b/subprojects/language-web/src/main/js/editor/semanticHighlighting.ts
@@ -0,0 +1,24 @@
1import { RangeSet } from '@codemirror/rangeset';
2import type { TransactionSpec } from '@codemirror/state';
3import { Decoration } from '@codemirror/view';
4
5import { decorationSetExtension } from './decorationSetExtension';
6
7export interface IHighlightRange {
8 from: number;
9
10 to: number;
11
12 classes: string[];
13}
14
15const [setSemanticHighlightingInternal, semanticHighlighting] = decorationSetExtension();
16
17export function setSemanticHighlighting(ranges: IHighlightRange[]): TransactionSpec {
18 const rangeSet = RangeSet.of(ranges.map(({ from, to, classes }) => Decoration.mark({
19 class: classes.map((c) => `cmt-problem-${c}`).join(' '),
20 }).range(from, to)), true);
21 return setSemanticHighlightingInternal(rangeSet);
22}
23
24export { semanticHighlighting };
diff --git a/subprojects/language-web/src/main/js/global.d.ts b/subprojects/language-web/src/main/js/global.d.ts
new file mode 100644
index 00000000..0533a46e
--- /dev/null
+++ b/subprojects/language-web/src/main/js/global.d.ts
@@ -0,0 +1,11 @@
1declare const DEBUG: boolean;
2
3declare const PACKAGE_NAME: string;
4
5declare const PACKAGE_VERSION: string;
6
7declare module '*.module.scss' {
8 const cssVariables: { [key in string]?: string };
9 // eslint-disable-next-line import/no-default-export
10 export default cssVariables;
11}
diff --git a/subprojects/language-web/src/main/js/index.tsx b/subprojects/language-web/src/main/js/index.tsx
new file mode 100644
index 00000000..d368c9ba
--- /dev/null
+++ b/subprojects/language-web/src/main/js/index.tsx
@@ -0,0 +1,69 @@
1import React from 'react';
2import { render } from 'react-dom';
3import CssBaseline from '@mui/material/CssBaseline';
4
5import { App } from './App';
6import { RootStore, RootStoreProvider } from './RootStore';
7import { ThemeProvider } from './theme/ThemeProvider';
8
9import '../css/index.scss';
10
11const initialValue = `class Family {
12 contains Person[] members
13}
14
15class Person {
16 Person[] children opposite parent
17 Person[0..1] parent opposite children
18 int age
19 TaxStatus taxStatus
20}
21
22enum TaxStatus {
23 child, student, adult, retired
24}
25
26% A child cannot have any dependents.
27pred invalidTaxStatus(Person p) <->
28 taxStatus(p, child),
29 children(p, _q)
30 ; taxStatus(p, retired),
31 parent(p, q),
32 !taxStatus(q, retired).
33
34direct rule createChild(p):
35 children(p, newPerson) = unknown,
36 equals(newPerson, newPerson) = unknown
37 ~> new q,
38 children(p, q) = true,
39 taxStatus(q, child) = true.
40
41indiv family.
42Family(family).
43members(family, anne).
44members(family, bob).
45members(family, ciri).
46children(anne, ciri).
47?children(bob, ciri).
48default children(ciri, *): false.
49taxStatus(anne, adult).
50age(anne, 35).
51bobAge: 27.
52age(bob, bobAge).
53!age(ciri, bobAge).
54
55scope Family = 1, Person += 5..10.
56`;
57
58const rootStore = new RootStore(initialValue);
59
60const app = (
61 <RootStoreProvider rootStore={rootStore}>
62 <ThemeProvider>
63 <CssBaseline />
64 <App />
65 </ThemeProvider>
66 </RootStoreProvider>
67);
68
69render(app, document.getElementById('app'));
diff --git a/subprojects/language-web/src/main/js/language/folding.ts b/subprojects/language-web/src/main/js/language/folding.ts
new file mode 100644
index 00000000..5d51f796
--- /dev/null
+++ b/subprojects/language-web/src/main/js/language/folding.ts
@@ -0,0 +1,115 @@
1import { EditorState } from '@codemirror/state';
2import type { SyntaxNode } from '@lezer/common';
3
4export type FoldRange = { from: number, to: number };
5
6/**
7 * Folds a block comment between its delimiters.
8 *
9 * @param node the node to fold
10 * @returns the folding range or `null` is there is nothing to fold
11 */
12export function foldBlockComment(node: SyntaxNode): FoldRange {
13 return {
14 from: node.from + 2,
15 to: node.to - 2,
16 };
17}
18
19/**
20 * Folds a declaration after the first element if it appears on the opening line,
21 * otherwise folds after the opening keyword.
22 *
23 * @example
24 * First element on the opening line:
25 * ```
26 * scope Family = 1,
27 * Person += 5..10.
28 * ```
29 * becomes
30 * ```
31 * scope Family = 1,[...].
32 * ```
33 *
34 * @example
35 * First element not on the opening line:
36 * ```
37 * scope Family
38 * = 1,
39 * Person += 5..10.
40 * ```
41 * becomes
42 * ```
43 * scope [...].
44 * ```
45 *
46 * @param node the node to fold
47 * @param state the editor state
48 * @returns the folding range or `null` is there is nothing to fold
49 */
50export function foldDeclaration(node: SyntaxNode, state: EditorState): FoldRange | null {
51 const { firstChild: open, lastChild: close } = node;
52 if (open === null || close === null) {
53 return null;
54 }
55 const { cursor } = open;
56 const lineEnd = state.doc.lineAt(open.from).to;
57 let foldFrom = open.to;
58 while (cursor.next() && cursor.from < lineEnd) {
59 if (cursor.type.name === ',') {
60 foldFrom = cursor.to;
61 break;
62 }
63 }
64 return {
65 from: foldFrom,
66 to: close.from,
67 };
68}
69
70/**
71 * Folds a node only if it has at least one sibling of the same type.
72 *
73 * The folding range will be the entire `node`.
74 *
75 * @param node the node to fold
76 * @returns the folding range or `null` is there is nothing to fold
77 */
78function foldWithSibling(node: SyntaxNode): FoldRange | null {
79 const { parent } = node;
80 if (parent === null) {
81 return null;
82 }
83 const { firstChild } = parent;
84 if (firstChild === null) {
85 return null;
86 }
87 const { cursor } = firstChild;
88 let nSiblings = 0;
89 while (cursor.nextSibling()) {
90 if (cursor.type === node.type) {
91 nSiblings += 1;
92 }
93 if (nSiblings >= 2) {
94 return {
95 from: node.from,
96 to: node.to,
97 };
98 }
99 }
100 return null;
101}
102
103export function foldWholeNode(node: SyntaxNode): FoldRange {
104 return {
105 from: node.from,
106 to: node.to,
107 };
108}
109
110export function foldConjunction(node: SyntaxNode): FoldRange | null {
111 if (node.parent?.type?.name === 'PredicateBody') {
112 return foldWithSibling(node);
113 }
114 return foldWholeNode(node);
115}
diff --git a/subprojects/language-web/src/main/js/language/indentation.ts b/subprojects/language-web/src/main/js/language/indentation.ts
new file mode 100644
index 00000000..6d36ed3b
--- /dev/null
+++ b/subprojects/language-web/src/main/js/language/indentation.ts
@@ -0,0 +1,87 @@
1import { TreeIndentContext } from '@codemirror/language';
2
3/**
4 * Finds the `from` of first non-skipped token, if any,
5 * after the opening keyword in the first line of the declaration.
6 *
7 * Based on
8 * https://github.com/codemirror/language/blob/cd7f7e66fa51ddbce96cf9396b1b6127d0ca4c94/src/indent.ts#L246
9 *
10 * @param context the indentation context
11 * @returns the alignment or `null` if there is no token after the opening keyword
12 */
13function findAlignmentAfterOpening(context: TreeIndentContext): number | null {
14 const {
15 node: tree,
16 simulatedBreak,
17 } = context;
18 const openingToken = tree.childAfter(tree.from);
19 if (openingToken === null) {
20 return null;
21 }
22 const openingLine = context.state.doc.lineAt(openingToken.from);
23 const lineEnd = simulatedBreak == null || simulatedBreak <= openingLine.from
24 ? openingLine.to
25 : Math.min(openingLine.to, simulatedBreak);
26 const { cursor } = openingToken;
27 while (cursor.next() && cursor.from < lineEnd) {
28 if (!cursor.type.isSkipped) {
29 return cursor.from;
30 }
31 }
32 return null;
33}
34
35/**
36 * Indents text after declarations by a single unit if it begins on a new line,
37 * otherwise it aligns with the text after the declaration.
38 *
39 * Based on
40 * https://github.com/codemirror/language/blob/cd7f7e66fa51ddbce96cf9396b1b6127d0ca4c94/src/indent.ts#L275
41 *
42 * @example
43 * Result with no hanging indent (indent unit = 2 spaces, units = 1):
44 * ```
45 * scope
46 * Family = 1,
47 * Person += 5..10.
48 * ```
49 *
50 * @example
51 * Result with hanging indent:
52 * ```
53 * scope Family = 1,
54 * Person += 5..10.
55 * ```
56 *
57 * @param context the indentation context
58 * @param units the number of units to indent
59 * @returns the desired indentation level
60 */
61function indentDeclarationStrategy(context: TreeIndentContext, units: number): number {
62 const alignment = findAlignmentAfterOpening(context);
63 if (alignment !== null) {
64 return context.column(alignment);
65 }
66 return context.baseIndent + units * context.unit;
67}
68
69export function indentBlockComment(): number {
70 // Do not indent.
71 return -1;
72}
73
74export function indentDeclaration(context: TreeIndentContext): number {
75 return indentDeclarationStrategy(context, 1);
76}
77
78export function indentPredicateOrRule(context: TreeIndentContext): number {
79 const clauseIndent = indentDeclarationStrategy(context, 1);
80 if (/^\s+[;.]/.exec(context.textAfter) !== null) {
81 return clauseIndent - 2;
82 }
83 if (/^\s+(~>)/.exec(context.textAfter) !== null) {
84 return clauseIndent - 3;
85 }
86 return clauseIndent;
87}
diff --git a/subprojects/language-web/src/main/js/language/problem.grammar b/subprojects/language-web/src/main/js/language/problem.grammar
new file mode 100644
index 00000000..bccc2e31
--- /dev/null
+++ b/subprojects/language-web/src/main/js/language/problem.grammar
@@ -0,0 +1,149 @@
1@detectDelim
2
3@external prop implicitCompletion from '../../../../src/main/js/language/props.ts'
4
5@top Problem { statement* }
6
7statement {
8 ProblemDeclaration {
9 ckw<"problem"> QualifiedName "."
10 } |
11 ClassDefinition {
12 ckw<"abstract">? ckw<"class"> RelationName
13 (ckw<"extends"> sep<",", RelationName>)?
14 (ClassBody { "{" ReferenceDeclaration* "}" } | ".")
15 } |
16 EnumDefinition {
17 ckw<"enum"> RelationName
18 (EnumBody { "{" sep<",", IndividualNodeName> "}" } | ".")
19 } |
20 PredicateDefinition {
21 (ckw<"error"> ckw<"pred">? | ckw<"direct">? ckw<"pred">)
22 RelationName ParameterList<Parameter>?
23 PredicateBody { ("<->" sep<OrOp, Conjunction>)? "." }
24 } |
25 RuleDefinition {
26 ckw<"direct">? ckw<"rule">
27 RuleName ParameterList<Parameter>?
28 RuleBody { ":" sep<OrOp, Conjunction> "~>" sep<OrOp, Action> "." }
29 } |
30 Assertion {
31 kw<"default">? (NotOp | UnknownOp)? RelationName
32 ParameterList<AssertionArgument> (":" LogicValue)? "."
33 } |
34 NodeValueAssertion {
35 IndividualNodeName ":" Constant "."
36 } |
37 IndividualDeclaration {
38 ckw<"indiv"> sep<",", IndividualNodeName> "."
39 } |
40 ScopeDeclaration {
41 kw<"scope"> sep<",", ScopeElement> "."
42 }
43}
44
45ReferenceDeclaration {
46 (kw<"refers"> | kw<"contains">)?
47 RelationName
48 RelationName
49 ( "[" Multiplicity? "]" )?
50 (kw<"opposite"> RelationName)?
51 ";"?
52}
53
54Parameter { RelationName? VariableName }
55
56Conjunction { ("," | Literal)+ }
57
58OrOp { ";" }
59
60Literal { NotOp? Atom (("=" | ":") sep1<"|", LogicValue>)? }
61
62Atom { RelationName "+"? ParameterList<Argument> }
63
64Action { ("," | ActionLiteral)+ }
65
66ActionLiteral {
67 ckw<"new"> VariableName |
68 ckw<"delete"> VariableName |
69 Literal
70}
71
72Argument { VariableName | Constant }
73
74AssertionArgument { NodeName | StarArgument | Constant }
75
76Constant { Real | String }
77
78LogicValue {
79 ckw<"true"> | ckw<"false"> | ckw<"unknown"> | ckw<"error">
80}
81
82ScopeElement { RelationName ("=" | "+=") Multiplicity }
83
84Multiplicity { (IntMult "..")? (IntMult | StarMult)}
85
86RelationName { QualifiedName }
87
88RuleName { QualifiedName }
89
90IndividualNodeName { QualifiedName }
91
92VariableName { QualifiedName }
93
94NodeName { QualifiedName }
95
96QualifiedName[implicitCompletion=true] { identifier ("::" identifier)* }
97
98kw<term> { @specialize[@name={term},implicitCompletion=true]<identifier, term> }
99
100ckw<term> { @extend[@name={term},implicitCompletion=true]<identifier, term> }
101
102ParameterList<content> { "(" sep<",", content> ")" }
103
104sep<separator, content> { sep1<separator, content>? }
105
106sep1<separator, content> { content (separator content)* }
107
108@skip { LineComment | BlockComment | whitespace }
109
110@tokens {
111 whitespace { std.whitespace+ }
112
113 LineComment { ("//" | "%") ![\n]* }
114
115 BlockComment { "/*" blockCommentRest }
116
117 blockCommentRest { ![*] blockCommentRest | "*" blockCommentAfterStar }
118
119 blockCommentAfterStar { "/" | "*" blockCommentAfterStar | ![/*] blockCommentRest }
120
121 @precedence { BlockComment, LineComment }
122
123 identifier { $[A-Za-z_] $[a-zA-Z0-9_]* }
124
125 int { $[0-9]+ }
126
127 IntMult { int }
128
129 StarMult { "*" }
130
131 Real { "-"? (exponential | int ("." (int | exponential))?) }
132
133 exponential { int ("e" | "E") ("+" | "-")? int }
134
135 String {
136 "'" (![\\'\n] | "\\" ![\n] | "\\\n")+ "'" |
137 "\"" (![\\"\n] | "\\" (![\n] | "\n"))* "\""
138 }
139
140 NotOp { "!" }
141
142 UnknownOp { "?" }
143
144 StarArgument { "*" }
145
146 "{" "}" "(" ")" "[" "]" "." ".." "," ":" "<->" "~>"
147}
148
149@detectDelim
diff --git a/subprojects/language-web/src/main/js/language/problemLanguageSupport.ts b/subprojects/language-web/src/main/js/language/problemLanguageSupport.ts
new file mode 100644
index 00000000..6508a2c0
--- /dev/null
+++ b/subprojects/language-web/src/main/js/language/problemLanguageSupport.ts
@@ -0,0 +1,92 @@
1import { styleTags, tags as t } from '@codemirror/highlight';
2import {
3 foldInside,
4 foldNodeProp,
5 indentNodeProp,
6 indentUnit,
7 LanguageSupport,
8 LRLanguage,
9} from '@codemirror/language';
10import { LRParser } from '@lezer/lr';
11
12import { parser } from '../../../../build/generated/sources/lezer/problem';
13import {
14 foldBlockComment,
15 foldConjunction,
16 foldDeclaration,
17 foldWholeNode,
18} from './folding';
19import {
20 indentBlockComment,
21 indentDeclaration,
22 indentPredicateOrRule,
23} from './indentation';
24
25const parserWithMetadata = (parser as LRParser).configure({
26 props: [
27 styleTags({
28 LineComment: t.lineComment,
29 BlockComment: t.blockComment,
30 'problem class enum pred rule indiv scope': t.definitionKeyword,
31 'abstract extends refers contains opposite error direct default': t.modifier,
32 'true false unknown error': t.keyword,
33 'new delete': t.operatorKeyword,
34 NotOp: t.keyword,
35 UnknownOp: t.keyword,
36 OrOp: t.keyword,
37 StarArgument: t.keyword,
38 'IntMult StarMult Real': t.number,
39 StarMult: t.number,
40 String: t.string,
41 'RelationName/QualifiedName': t.typeName,
42 'RuleName/QualifiedName': t.macroName,
43 'IndividualNodeName/QualifiedName': t.atom,
44 'VariableName/QualifiedName': t.variableName,
45 '{ }': t.brace,
46 '( )': t.paren,
47 '[ ]': t.squareBracket,
48 '. .. , :': t.separator,
49 '<-> ~>': t.definitionOperator,
50 }),
51 indentNodeProp.add({
52 ProblemDeclaration: indentDeclaration,
53 UniqueDeclaration: indentDeclaration,
54 ScopeDeclaration: indentDeclaration,
55 PredicateBody: indentPredicateOrRule,
56 RuleBody: indentPredicateOrRule,
57 BlockComment: indentBlockComment,
58 }),
59 foldNodeProp.add({
60 ClassBody: foldInside,
61 EnumBody: foldInside,
62 ParameterList: foldInside,
63 PredicateBody: foldInside,
64 RuleBody: foldInside,
65 Conjunction: foldConjunction,
66 Action: foldWholeNode,
67 UniqueDeclaration: foldDeclaration,
68 ScopeDeclaration: foldDeclaration,
69 BlockComment: foldBlockComment,
70 }),
71 ],
72});
73
74const problemLanguage = LRLanguage.define({
75 parser: parserWithMetadata,
76 languageData: {
77 commentTokens: {
78 block: {
79 open: '/*',
80 close: '*/',
81 },
82 line: '%',
83 },
84 indentOnInput: /^\s*(?:\{|\}|\(|\)|;|\.|~>)$/,
85 },
86});
87
88export function problemLanguageSupport(): LanguageSupport {
89 return new LanguageSupport(problemLanguage, [
90 indentUnit.of(' '),
91 ]);
92}
diff --git a/subprojects/language-web/src/main/js/language/props.ts b/subprojects/language-web/src/main/js/language/props.ts
new file mode 100644
index 00000000..8e488bf5
--- /dev/null
+++ b/subprojects/language-web/src/main/js/language/props.ts
@@ -0,0 +1,7 @@
1import { NodeProp } from '@lezer/common';
2
3export const implicitCompletion = new NodeProp({
4 deserialize(s: string) {
5 return s === 'true';
6 },
7});
diff --git a/subprojects/language-web/src/main/js/theme/EditorTheme.ts b/subprojects/language-web/src/main/js/theme/EditorTheme.ts
new file mode 100644
index 00000000..1b0dd5de
--- /dev/null
+++ b/subprojects/language-web/src/main/js/theme/EditorTheme.ts
@@ -0,0 +1,47 @@
1import type { PaletteMode } from '@mui/material';
2
3import cssVariables from '../../css/themeVariables.module.scss';
4
5export enum EditorTheme {
6 Light,
7 Dark,
8}
9
10export class EditorThemeData {
11 className: string;
12
13 paletteMode: PaletteMode;
14
15 toggleDarkMode: EditorTheme;
16
17 foreground!: string;
18
19 foregroundHighlight!: string;
20
21 background!: string;
22
23 primary!: string;
24
25 secondary!: string;
26
27 constructor(className: string, paletteMode: PaletteMode, toggleDarkMode: EditorTheme) {
28 this.className = className;
29 this.paletteMode = paletteMode;
30 this.toggleDarkMode = toggleDarkMode;
31 Reflect.ownKeys(this).forEach((key) => {
32 if (!Reflect.get(this, key)) {
33 const cssKey = `${this.className}--${key.toString()}`;
34 if (cssKey in cssVariables) {
35 Reflect.set(this, key, cssVariables[cssKey]);
36 }
37 }
38 });
39 }
40}
41
42export const DEFAULT_THEME = EditorTheme.Dark;
43
44export const EDITOR_THEMES: { [key in EditorTheme]: EditorThemeData } = {
45 [EditorTheme.Light]: new EditorThemeData('light', 'light', EditorTheme.Dark),
46 [EditorTheme.Dark]: new EditorThemeData('dark', 'dark', EditorTheme.Light),
47};
diff --git a/subprojects/language-web/src/main/js/theme/ThemeProvider.tsx b/subprojects/language-web/src/main/js/theme/ThemeProvider.tsx
new file mode 100644
index 00000000..f5b50be1
--- /dev/null
+++ b/subprojects/language-web/src/main/js/theme/ThemeProvider.tsx
@@ -0,0 +1,15 @@
1import { observer } from 'mobx-react-lite';
2import { ThemeProvider as MaterialUiThemeProvider } from '@mui/material/styles';
3import React from 'react';
4
5import { useRootStore } from '../RootStore';
6
7export const ThemeProvider: React.FC = observer(({ children }) => {
8 const { themeStore } = useRootStore();
9
10 return (
11 <MaterialUiThemeProvider theme={themeStore.materialUiTheme}>
12 {children}
13 </MaterialUiThemeProvider>
14 );
15});
diff --git a/subprojects/language-web/src/main/js/theme/ThemeStore.ts b/subprojects/language-web/src/main/js/theme/ThemeStore.ts
new file mode 100644
index 00000000..ffaf6dde
--- /dev/null
+++ b/subprojects/language-web/src/main/js/theme/ThemeStore.ts
@@ -0,0 +1,64 @@
1import { makeAutoObservable } from 'mobx';
2import {
3 Theme,
4 createTheme,
5 responsiveFontSizes,
6} from '@mui/material/styles';
7
8import {
9 EditorTheme,
10 EditorThemeData,
11 DEFAULT_THEME,
12 EDITOR_THEMES,
13} from './EditorTheme';
14
15export class ThemeStore {
16 currentTheme: EditorTheme = DEFAULT_THEME;
17
18 constructor() {
19 makeAutoObservable(this);
20 }
21
22 toggleDarkMode(): void {
23 this.currentTheme = this.currentThemeData.toggleDarkMode;
24 }
25
26 private get currentThemeData(): EditorThemeData {
27 return EDITOR_THEMES[this.currentTheme];
28 }
29
30 get materialUiTheme(): Theme {
31 const themeData = this.currentThemeData;
32 const materialUiTheme = createTheme({
33 palette: {
34 mode: themeData.paletteMode,
35 background: {
36 default: themeData.background,
37 paper: themeData.background,
38 },
39 primary: {
40 main: themeData.primary,
41 },
42 secondary: {
43 main: themeData.secondary,
44 },
45 error: {
46 main: themeData.secondary,
47 },
48 text: {
49 primary: themeData.foregroundHighlight,
50 secondary: themeData.foreground,
51 },
52 },
53 });
54 return responsiveFontSizes(materialUiTheme);
55 }
56
57 get darkMode(): boolean {
58 return this.currentThemeData.paletteMode === 'dark';
59 }
60
61 get className(): string {
62 return this.currentThemeData.className;
63 }
64}
diff --git a/subprojects/language-web/src/main/js/utils/ConditionVariable.ts b/subprojects/language-web/src/main/js/utils/ConditionVariable.ts
new file mode 100644
index 00000000..0910dfa6
--- /dev/null
+++ b/subprojects/language-web/src/main/js/utils/ConditionVariable.ts
@@ -0,0 +1,64 @@
1import { getLogger } from './logger';
2import { PendingTask } from './PendingTask';
3
4const log = getLogger('utils.ConditionVariable');
5
6export type Condition = () => boolean;
7
8export class ConditionVariable {
9 condition: Condition;
10
11 defaultTimeout: number;
12
13 listeners: PendingTask<void>[] = [];
14
15 constructor(condition: Condition, defaultTimeout = 0) {
16 this.condition = condition;
17 this.defaultTimeout = defaultTimeout;
18 }
19
20 async waitFor(timeoutMs: number | null = null): Promise<void> {
21 if (this.condition()) {
22 return;
23 }
24 const timeoutOrDefault = timeoutMs || this.defaultTimeout;
25 let nowMs = Date.now();
26 const endMs = nowMs + timeoutOrDefault;
27 while (!this.condition() && nowMs < endMs) {
28 const remainingMs = endMs - nowMs;
29 const promise = new Promise<void>((resolve, reject) => {
30 if (this.condition()) {
31 resolve();
32 return;
33 }
34 const task = new PendingTask(resolve, reject, remainingMs);
35 this.listeners.push(task);
36 });
37 // We must keep waiting until the update has completed,
38 // so the tasks can't be started in parallel.
39 // eslint-disable-next-line no-await-in-loop
40 await promise;
41 nowMs = Date.now();
42 }
43 if (!this.condition()) {
44 log.error('Condition still does not hold after', timeoutOrDefault, 'ms');
45 throw new Error('Failed to wait for condition');
46 }
47 }
48
49 notifyAll(): void {
50 this.clearListenersWith((listener) => listener.resolve());
51 }
52
53 rejectAll(error: unknown): void {
54 this.clearListenersWith((listener) => listener.reject(error));
55 }
56
57 private clearListenersWith(callback: (listener: PendingTask<void>) => void) {
58 // Copy `listeners` so that we don't get into a race condition
59 // if one of the listeners adds another listener.
60 const { listeners } = this;
61 this.listeners = [];
62 listeners.forEach(callback);
63 }
64}
diff --git a/subprojects/language-web/src/main/js/utils/PendingTask.ts b/subprojects/language-web/src/main/js/utils/PendingTask.ts
new file mode 100644
index 00000000..51b79fb0
--- /dev/null
+++ b/subprojects/language-web/src/main/js/utils/PendingTask.ts
@@ -0,0 +1,60 @@
1import { getLogger } from './logger';
2
3const log = getLogger('utils.PendingTask');
4
5export class PendingTask<T> {
6 private readonly resolveCallback: (value: T) => void;
7
8 private readonly rejectCallback: (reason?: unknown) => void;
9
10 private resolved = false;
11
12 private timeout: number | null;
13
14 constructor(
15 resolveCallback: (value: T) => void,
16 rejectCallback: (reason?: unknown) => void,
17 timeoutMs?: number,
18 timeoutCallback?: () => void,
19 ) {
20 this.resolveCallback = resolveCallback;
21 this.rejectCallback = rejectCallback;
22 if (timeoutMs) {
23 this.timeout = setTimeout(() => {
24 if (!this.resolved) {
25 this.reject(new Error('Request timed out'));
26 if (timeoutCallback) {
27 timeoutCallback();
28 }
29 }
30 }, timeoutMs);
31 } else {
32 this.timeout = null;
33 }
34 }
35
36 resolve(value: T): void {
37 if (this.resolved) {
38 log.warn('Trying to resolve already resolved promise');
39 return;
40 }
41 this.markResolved();
42 this.resolveCallback(value);
43 }
44
45 reject(reason?: unknown): void {
46 if (this.resolved) {
47 log.warn('Trying to reject already resolved promise');
48 return;
49 }
50 this.markResolved();
51 this.rejectCallback(reason);
52 }
53
54 private markResolved() {
55 this.resolved = true;
56 if (this.timeout !== null) {
57 clearTimeout(this.timeout);
58 }
59 }
60}
diff --git a/subprojects/language-web/src/main/js/utils/Timer.ts b/subprojects/language-web/src/main/js/utils/Timer.ts
new file mode 100644
index 00000000..8f653070
--- /dev/null
+++ b/subprojects/language-web/src/main/js/utils/Timer.ts
@@ -0,0 +1,33 @@
1export class Timer {
2 readonly callback: () => void;
3
4 readonly defaultTimeout: number;
5
6 timeout: number | null = null;
7
8 constructor(callback: () => void, defaultTimeout = 0) {
9 this.callback = () => {
10 this.timeout = null;
11 callback();
12 };
13 this.defaultTimeout = defaultTimeout;
14 }
15
16 schedule(timeout: number | null = null): void {
17 if (this.timeout === null) {
18 this.timeout = setTimeout(this.callback, timeout || this.defaultTimeout);
19 }
20 }
21
22 reschedule(timeout: number | null = null): void {
23 this.cancel();
24 this.schedule(timeout);
25 }
26
27 cancel(): void {
28 if (this.timeout !== null) {
29 clearTimeout(this.timeout);
30 this.timeout = null;
31 }
32 }
33}
diff --git a/subprojects/language-web/src/main/js/utils/logger.ts b/subprojects/language-web/src/main/js/utils/logger.ts
new file mode 100644
index 00000000..306d122c
--- /dev/null
+++ b/subprojects/language-web/src/main/js/utils/logger.ts
@@ -0,0 +1,49 @@
1import styles, { CSPair } from 'ansi-styles';
2import log from 'loglevel';
3import * as prefix from 'loglevel-plugin-prefix';
4
5const colors: Partial<Record<string, CSPair>> = {
6 TRACE: styles.magenta,
7 DEBUG: styles.cyan,
8 INFO: styles.blue,
9 WARN: styles.yellow,
10 ERROR: styles.red,
11};
12
13prefix.reg(log);
14
15if (DEBUG) {
16 log.setLevel(log.levels.DEBUG);
17} else {
18 log.setLevel(log.levels.WARN);
19}
20
21if ('chrome' in window) {
22 // Only Chromium supports console ANSI escape sequences.
23 prefix.apply(log, {
24 format(level, name, timestamp) {
25 const formattedTimestamp = `${styles.gray.open}[${timestamp.toString()}]${styles.gray.close}`;
26 const levelColor = colors[level.toUpperCase()] || styles.red;
27 const formattedLevel = `${levelColor.open}${level}${levelColor.close}`;
28 const formattedName = `${styles.green.open}(${name || 'root'})${styles.green.close}`;
29 return `${formattedTimestamp} ${formattedLevel} ${formattedName}`;
30 },
31 });
32} else {
33 prefix.apply(log, {
34 template: '[%t] %l (%n)',
35 });
36}
37
38const appLogger = log.getLogger(PACKAGE_NAME);
39
40appLogger.info('Version:', PACKAGE_NAME, PACKAGE_VERSION);
41appLogger.info('Debug mode:', DEBUG);
42
43export function getLoggerFromRoot(name: string | symbol): log.Logger {
44 return log.getLogger(name);
45}
46
47export function getLogger(name: string | symbol): log.Logger {
48 return getLoggerFromRoot(`${PACKAGE_NAME}.${name.toString()}`);
49}
diff --git a/subprojects/language-web/src/main/js/xtext/ContentAssistService.ts b/subprojects/language-web/src/main/js/xtext/ContentAssistService.ts
new file mode 100644
index 00000000..8b872e06
--- /dev/null
+++ b/subprojects/language-web/src/main/js/xtext/ContentAssistService.ts
@@ -0,0 +1,219 @@
1import type {
2 Completion,
3 CompletionContext,
4 CompletionResult,
5} from '@codemirror/autocomplete';
6import { syntaxTree } from '@codemirror/language';
7import type { Transaction } from '@codemirror/state';
8import escapeStringRegexp from 'escape-string-regexp';
9
10import { implicitCompletion } from '../language/props';
11import type { UpdateService } from './UpdateService';
12import { getLogger } from '../utils/logger';
13import type { ContentAssistEntry } from './xtextServiceResults';
14
15const PROPOSALS_LIMIT = 1000;
16
17const IDENTIFIER_REGEXP_STR = '[a-zA-Z0-9_]*';
18
19const HIGH_PRIORITY_KEYWORDS = ['<->', '~>'];
20
21const log = getLogger('xtext.ContentAssistService');
22
23interface IFoundToken {
24 from: number;
25
26 to: number;
27
28 implicitCompletion: boolean;
29
30 text: string;
31}
32
33function findToken({ pos, state }: CompletionContext): IFoundToken | null {
34 const token = syntaxTree(state).resolveInner(pos, -1);
35 if (token === null) {
36 return null;
37 }
38 if (token.firstChild !== null) {
39 // We only autocomplete terminal nodes. If the current node is nonterminal,
40 // returning `null` makes us autocomplete with the empty prefix instead.
41 return null;
42 }
43 return {
44 from: token.from,
45 to: token.to,
46 implicitCompletion: token.type.prop(implicitCompletion) || false,
47 text: state.sliceDoc(token.from, token.to),
48 };
49}
50
51function shouldCompleteImplicitly(token: IFoundToken | null, context: CompletionContext): boolean {
52 return token !== null
53 && token.implicitCompletion
54 && context.pos - token.from >= 2;
55}
56
57function computeSpan(prefix: string, entryCount: number): RegExp {
58 const escapedPrefix = escapeStringRegexp(prefix);
59 if (entryCount < PROPOSALS_LIMIT) {
60 // Proposals with the current prefix fit the proposals limit.
61 // We can filter client side as long as the current prefix is preserved.
62 return new RegExp(`^${escapedPrefix}${IDENTIFIER_REGEXP_STR}$`);
63 }
64 // The current prefix overflows the proposals limits,
65 // so we have to fetch the completions again on the next keypress.
66 // Hopefully, it'll return a shorter list and we'll be able to filter client side.
67 return new RegExp(`^${escapedPrefix}$`);
68}
69
70function createCompletion(entry: ContentAssistEntry): Completion {
71 let boost: number;
72 switch (entry.kind) {
73 case 'KEYWORD':
74 // Some hard-to-type operators should be on top.
75 boost = HIGH_PRIORITY_KEYWORDS.includes(entry.proposal) ? 10 : -99;
76 break;
77 case 'TEXT':
78 case 'SNIPPET':
79 boost = -90;
80 break;
81 default: {
82 // Penalize qualified names (vs available unqualified names).
83 const extraSegments = entry.proposal.match(/::/g)?.length || 0;
84 boost = Math.max(-5 * extraSegments, -50);
85 }
86 break;
87 }
88 return {
89 label: entry.proposal,
90 detail: entry.description,
91 info: entry.documentation,
92 type: entry.kind?.toLowerCase(),
93 boost,
94 };
95}
96
97export class ContentAssistService {
98 private readonly updateService: UpdateService;
99
100 private lastCompletion: CompletionResult | null = null;
101
102 constructor(updateService: UpdateService) {
103 this.updateService = updateService;
104 }
105
106 onTransaction(transaction: Transaction): void {
107 if (this.shouldInvalidateCachedCompletion(transaction)) {
108 this.lastCompletion = null;
109 }
110 }
111
112 async contentAssist(context: CompletionContext): Promise<CompletionResult> {
113 const tokenBefore = findToken(context);
114 if (!context.explicit && !shouldCompleteImplicitly(tokenBefore, context)) {
115 return {
116 from: context.pos,
117 options: [],
118 };
119 }
120 let range: { from: number, to: number };
121 let prefix = '';
122 if (tokenBefore === null) {
123 range = {
124 from: context.pos,
125 to: context.pos,
126 };
127 prefix = '';
128 } else {
129 range = {
130 from: tokenBefore.from,
131 to: tokenBefore.to,
132 };
133 const prefixLength = context.pos - tokenBefore.from;
134 if (prefixLength > 0) {
135 prefix = tokenBefore.text.substring(0, context.pos - tokenBefore.from);
136 }
137 }
138 if (!context.explicit && this.shouldReturnCachedCompletion(tokenBefore)) {
139 log.trace('Returning cached completion result');
140 // Postcondition of `shouldReturnCachedCompletion`: `lastCompletion !== null`
141 return {
142 ...this.lastCompletion as CompletionResult,
143 ...range,
144 };
145 }
146 this.lastCompletion = null;
147 const entries = await this.updateService.fetchContentAssist({
148 resource: this.updateService.resourceName,
149 serviceType: 'assist',
150 caretOffset: context.pos,
151 proposalsLimit: PROPOSALS_LIMIT,
152 }, context);
153 if (context.aborted) {
154 return {
155 ...range,
156 options: [],
157 };
158 }
159 const options: Completion[] = [];
160 entries.forEach((entry) => {
161 if (prefix === entry.prefix) {
162 // Xtext will generate completions that do not complete the current token,
163 // e.g., `(` after trying to complete an indetifier,
164 // but we ignore those, since CodeMirror won't filter for them anyways.
165 options.push(createCompletion(entry));
166 }
167 });
168 log.debug('Fetched', options.length, 'completions from server');
169 this.lastCompletion = {
170 ...range,
171 options,
172 span: computeSpan(prefix, entries.length),
173 };
174 return this.lastCompletion;
175 }
176
177 private shouldReturnCachedCompletion(
178 token: { from: number, to: number, text: string } | null,
179 ): boolean {
180 if (token === null || this.lastCompletion === null) {
181 return false;
182 }
183 const { from, to, text } = token;
184 const { from: lastFrom, to: lastTo, span } = this.lastCompletion;
185 if (!lastTo) {
186 return true;
187 }
188 const [transformedFrom, transformedTo] = this.mapRangeInclusive(lastFrom, lastTo);
189 return from >= transformedFrom
190 && to <= transformedTo
191 && typeof span !== 'undefined'
192 && span.exec(text) !== null;
193 }
194
195 private shouldInvalidateCachedCompletion(transaction: Transaction): boolean {
196 if (!transaction.docChanged || this.lastCompletion === null) {
197 return false;
198 }
199 const { from: lastFrom, to: lastTo } = this.lastCompletion;
200 if (!lastTo) {
201 return true;
202 }
203 const [transformedFrom, transformedTo] = this.mapRangeInclusive(lastFrom, lastTo);
204 let invalidate = false;
205 transaction.changes.iterChangedRanges((fromA, toA) => {
206 if (fromA < transformedFrom || toA > transformedTo) {
207 invalidate = true;
208 }
209 });
210 return invalidate;
211 }
212
213 private mapRangeInclusive(lastFrom: number, lastTo: number): [number, number] {
214 const changes = this.updateService.computeChangesSinceLastUpdate();
215 const transformedFrom = changes.mapPos(lastFrom);
216 const transformedTo = changes.mapPos(lastTo, 1);
217 return [transformedFrom, transformedTo];
218 }
219}
diff --git a/subprojects/language-web/src/main/js/xtext/HighlightingService.ts b/subprojects/language-web/src/main/js/xtext/HighlightingService.ts
new file mode 100644
index 00000000..dfbb4a19
--- /dev/null
+++ b/subprojects/language-web/src/main/js/xtext/HighlightingService.ts
@@ -0,0 +1,37 @@
1import type { EditorStore } from '../editor/EditorStore';
2import type { IHighlightRange } from '../editor/semanticHighlighting';
3import type { UpdateService } from './UpdateService';
4import { highlightingResult } from './xtextServiceResults';
5
6export class HighlightingService {
7 private readonly store: EditorStore;
8
9 private readonly updateService: UpdateService;
10
11 constructor(store: EditorStore, updateService: UpdateService) {
12 this.store = store;
13 this.updateService = updateService;
14 }
15
16 onPush(push: unknown): void {
17 const { regions } = highlightingResult.parse(push);
18 const allChanges = this.updateService.computeChangesSinceLastUpdate();
19 const ranges: IHighlightRange[] = [];
20 regions.forEach(({ offset, length, styleClasses }) => {
21 if (styleClasses.length === 0) {
22 return;
23 }
24 const from = allChanges.mapPos(offset);
25 const to = allChanges.mapPos(offset + length);
26 if (to <= from) {
27 return;
28 }
29 ranges.push({
30 from,
31 to,
32 classes: styleClasses,
33 });
34 });
35 this.store.updateSemanticHighlighting(ranges);
36 }
37}
diff --git a/subprojects/language-web/src/main/js/xtext/OccurrencesService.ts b/subprojects/language-web/src/main/js/xtext/OccurrencesService.ts
new file mode 100644
index 00000000..bc865537
--- /dev/null
+++ b/subprojects/language-web/src/main/js/xtext/OccurrencesService.ts
@@ -0,0 +1,127 @@
1import { Transaction } from '@codemirror/state';
2
3import type { EditorStore } from '../editor/EditorStore';
4import type { IOccurrence } from '../editor/findOccurrences';
5import type { UpdateService } from './UpdateService';
6import { getLogger } from '../utils/logger';
7import { Timer } from '../utils/Timer';
8import { XtextWebSocketClient } from './XtextWebSocketClient';
9import {
10 isConflictResult,
11 occurrencesResult,
12 TextRegion,
13} from './xtextServiceResults';
14
15const FIND_OCCURRENCES_TIMEOUT_MS = 1000;
16
17// Must clear occurrences asynchronously from `onTransaction`,
18// because we must not emit a conflicting transaction when handling the pending transaction.
19const CLEAR_OCCURRENCES_TIMEOUT_MS = 10;
20
21const log = getLogger('xtext.OccurrencesService');
22
23function transformOccurrences(regions: TextRegion[]): IOccurrence[] {
24 const occurrences: IOccurrence[] = [];
25 regions.forEach(({ offset, length }) => {
26 if (length > 0) {
27 occurrences.push({
28 from: offset,
29 to: offset + length,
30 });
31 }
32 });
33 return occurrences;
34}
35
36export class OccurrencesService {
37 private readonly store: EditorStore;
38
39 private readonly webSocketClient: XtextWebSocketClient;
40
41 private readonly updateService: UpdateService;
42
43 private hasOccurrences = false;
44
45 private readonly findOccurrencesTimer = new Timer(() => {
46 this.handleFindOccurrences();
47 }, FIND_OCCURRENCES_TIMEOUT_MS);
48
49 private readonly clearOccurrencesTimer = new Timer(() => {
50 this.clearOccurrences();
51 }, CLEAR_OCCURRENCES_TIMEOUT_MS);
52
53 constructor(
54 store: EditorStore,
55 webSocketClient: XtextWebSocketClient,
56 updateService: UpdateService,
57 ) {
58 this.store = store;
59 this.webSocketClient = webSocketClient;
60 this.updateService = updateService;
61 }
62
63 onTransaction(transaction: Transaction): void {
64 if (transaction.docChanged) {
65 this.clearOccurrencesTimer.schedule();
66 this.findOccurrencesTimer.reschedule();
67 }
68 if (transaction.isUserEvent('select')) {
69 this.findOccurrencesTimer.reschedule();
70 }
71 }
72
73 private handleFindOccurrences() {
74 this.clearOccurrencesTimer.cancel();
75 this.updateOccurrences().catch((error) => {
76 log.error('Unexpected error while updating occurrences', error);
77 this.clearOccurrences();
78 });
79 }
80
81 private async updateOccurrences() {
82 await this.updateService.update();
83 const result = await this.webSocketClient.send({
84 resource: this.updateService.resourceName,
85 serviceType: 'occurrences',
86 expectedStateId: this.updateService.xtextStateId,
87 caretOffset: this.store.state.selection.main.head,
88 });
89 const allChanges = this.updateService.computeChangesSinceLastUpdate();
90 if (!allChanges.empty || isConflictResult(result, 'canceled')) {
91 // Stale occurrences result, the user already made some changes.
92 // We can safely ignore the occurrences and schedule a new find occurrences call.
93 this.clearOccurrences();
94 this.findOccurrencesTimer.schedule();
95 return;
96 }
97 const parsedOccurrencesResult = occurrencesResult.safeParse(result);
98 if (!parsedOccurrencesResult.success) {
99 log.error(
100 'Unexpected occurences result',
101 result,
102 'not an OccurrencesResult: ',
103 parsedOccurrencesResult.error,
104 );
105 this.clearOccurrences();
106 return;
107 }
108 const { stateId, writeRegions, readRegions } = parsedOccurrencesResult.data;
109 if (stateId !== this.updateService.xtextStateId) {
110 log.error('Unexpected state id, expected:', this.updateService.xtextStateId, 'got:', stateId);
111 this.clearOccurrences();
112 return;
113 }
114 const write = transformOccurrences(writeRegions);
115 const read = transformOccurrences(readRegions);
116 this.hasOccurrences = write.length > 0 || read.length > 0;
117 log.debug('Found', write.length, 'write and', read.length, 'read occurrences');
118 this.store.updateOccurrences(write, read);
119 }
120
121 private clearOccurrences() {
122 if (this.hasOccurrences) {
123 this.store.updateOccurrences([], []);
124 this.hasOccurrences = false;
125 }
126 }
127}
diff --git a/subprojects/language-web/src/main/js/xtext/UpdateService.ts b/subprojects/language-web/src/main/js/xtext/UpdateService.ts
new file mode 100644
index 00000000..e78944a9
--- /dev/null
+++ b/subprojects/language-web/src/main/js/xtext/UpdateService.ts
@@ -0,0 +1,363 @@
1import {
2 ChangeDesc,
3 ChangeSet,
4 ChangeSpec,
5 StateEffect,
6 Transaction,
7} from '@codemirror/state';
8import { nanoid } from 'nanoid';
9
10import type { EditorStore } from '../editor/EditorStore';
11import type { XtextWebSocketClient } from './XtextWebSocketClient';
12import { ConditionVariable } from '../utils/ConditionVariable';
13import { getLogger } from '../utils/logger';
14import { Timer } from '../utils/Timer';
15import {
16 ContentAssistEntry,
17 contentAssistResult,
18 documentStateResult,
19 formattingResult,
20 isConflictResult,
21} from './xtextServiceResults';
22
23const UPDATE_TIMEOUT_MS = 500;
24
25const WAIT_FOR_UPDATE_TIMEOUT_MS = 1000;
26
27const log = getLogger('xtext.UpdateService');
28
29const setDirtyChanges = StateEffect.define<ChangeSet>();
30
31export interface IAbortSignal {
32 aborted: boolean;
33}
34
35export class UpdateService {
36 resourceName: string;
37
38 xtextStateId: string | null = null;
39
40 private readonly store: EditorStore;
41
42 /**
43 * The changes being synchronized to the server if a full or delta text update is running,
44 * `null` otherwise.
45 */
46 private pendingUpdate: ChangeSet | null = null;
47
48 /**
49 * Local changes not yet sychronized to the server and not part of the running update, if any.
50 */
51 private dirtyChanges: ChangeSet;
52
53 private readonly webSocketClient: XtextWebSocketClient;
54
55 private readonly updatedCondition = new ConditionVariable(
56 () => this.pendingUpdate === null && this.xtextStateId !== null,
57 WAIT_FOR_UPDATE_TIMEOUT_MS,
58 );
59
60 private readonly idleUpdateTimer = new Timer(() => {
61 this.handleIdleUpdate();
62 }, UPDATE_TIMEOUT_MS);
63
64 constructor(store: EditorStore, webSocketClient: XtextWebSocketClient) {
65 this.resourceName = `${nanoid(7)}.problem`;
66 this.store = store;
67 this.dirtyChanges = this.newEmptyChangeSet();
68 this.webSocketClient = webSocketClient;
69 }
70
71 onReconnect(): void {
72 this.xtextStateId = null;
73 this.updateFullText().catch((error) => {
74 log.error('Unexpected error during initial update', error);
75 });
76 }
77
78 onTransaction(transaction: Transaction): void {
79 const setDirtyChangesEffect = transaction.effects.find(
80 (effect) => effect.is(setDirtyChanges),
81 ) as StateEffect<ChangeSet> | undefined;
82 if (setDirtyChangesEffect) {
83 const { value } = setDirtyChangesEffect;
84 if (this.pendingUpdate !== null) {
85 this.pendingUpdate = ChangeSet.empty(value.length);
86 }
87 this.dirtyChanges = value;
88 return;
89 }
90 if (transaction.docChanged) {
91 this.dirtyChanges = this.dirtyChanges.compose(transaction.changes);
92 this.idleUpdateTimer.reschedule();
93 }
94 }
95
96 /**
97 * Computes the summary of any changes happened since the last complete update.
98 *
99 * The result reflects any changes that happened since the `xtextStateId`
100 * version was uploaded to the server.
101 *
102 * @return the summary of changes since the last update
103 */
104 computeChangesSinceLastUpdate(): ChangeDesc {
105 return this.pendingUpdate?.composeDesc(this.dirtyChanges.desc) || this.dirtyChanges.desc;
106 }
107
108 private handleIdleUpdate() {
109 if (!this.webSocketClient.isOpen || this.dirtyChanges.empty) {
110 return;
111 }
112 if (this.pendingUpdate === null) {
113 this.update().catch((error) => {
114 log.error('Unexpected error during scheduled update', error);
115 });
116 }
117 this.idleUpdateTimer.reschedule();
118 }
119
120 private newEmptyChangeSet() {
121 return ChangeSet.of([], this.store.state.doc.length);
122 }
123
124 async updateFullText(): Promise<void> {
125 await this.withUpdate(() => this.doUpdateFullText());
126 }
127
128 private async doUpdateFullText(): Promise<[string, void]> {
129 const result = await this.webSocketClient.send({
130 resource: this.resourceName,
131 serviceType: 'update',
132 fullText: this.store.state.doc.sliceString(0),
133 });
134 const { stateId } = documentStateResult.parse(result);
135 return [stateId, undefined];
136 }
137
138 /**
139 * Makes sure that the document state on the server reflects recent
140 * local changes.
141 *
142 * Performs either an update with delta text or a full text update if needed.
143 * If there are not local dirty changes, the promise resolves immediately.
144 *
145 * @return a promise resolving when the update is completed
146 */
147 async update(): Promise<void> {
148 await this.prepareForDeltaUpdate();
149 const delta = this.computeDelta();
150 if (delta === null) {
151 return;
152 }
153 log.trace('Editor delta', delta);
154 await this.withUpdate(async () => {
155 const result = await this.webSocketClient.send({
156 resource: this.resourceName,
157 serviceType: 'update',
158 requiredStateId: this.xtextStateId,
159 ...delta,
160 });
161 const parsedDocumentStateResult = documentStateResult.safeParse(result);
162 if (parsedDocumentStateResult.success) {
163 return [parsedDocumentStateResult.data.stateId, undefined];
164 }
165 if (isConflictResult(result, 'invalidStateId')) {
166 return this.doFallbackToUpdateFullText();
167 }
168 throw parsedDocumentStateResult.error;
169 });
170 }
171
172 private doFallbackToUpdateFullText() {
173 if (this.pendingUpdate === null) {
174 throw new Error('Only a pending update can be extended');
175 }
176 log.warn('Delta update failed, performing full text update');
177 this.xtextStateId = null;
178 this.pendingUpdate = this.pendingUpdate.compose(this.dirtyChanges);
179 this.dirtyChanges = this.newEmptyChangeSet();
180 return this.doUpdateFullText();
181 }
182
183 async fetchContentAssist(
184 params: Record<string, unknown>,
185 signal: IAbortSignal,
186 ): Promise<ContentAssistEntry[]> {
187 await this.prepareForDeltaUpdate();
188 if (signal.aborted) {
189 return [];
190 }
191 const delta = this.computeDelta();
192 if (delta !== null) {
193 log.trace('Editor delta', delta);
194 const entries = await this.withUpdate(async () => {
195 const result = await this.webSocketClient.send({
196 ...params,
197 requiredStateId: this.xtextStateId,
198 ...delta,
199 });
200 const parsedContentAssistResult = contentAssistResult.safeParse(result);
201 if (parsedContentAssistResult.success) {
202 const { stateId, entries: resultEntries } = parsedContentAssistResult.data;
203 return [stateId, resultEntries];
204 }
205 if (isConflictResult(result, 'invalidStateId')) {
206 log.warn('Server state invalid during content assist');
207 const [newStateId] = await this.doFallbackToUpdateFullText();
208 // We must finish this state update transaction to prepare for any push events
209 // before querying for content assist, so we just return `null` and will query
210 // the content assist service later.
211 return [newStateId, null];
212 }
213 throw parsedContentAssistResult.error;
214 });
215 if (entries !== null) {
216 return entries;
217 }
218 if (signal.aborted) {
219 return [];
220 }
221 }
222 // Poscondition of `prepareForDeltaUpdate`: `xtextStateId !== null`
223 return this.doFetchContentAssist(params, this.xtextStateId as string);
224 }
225
226 private async doFetchContentAssist(params: Record<string, unknown>, expectedStateId: string) {
227 const result = await this.webSocketClient.send({
228 ...params,
229 requiredStateId: expectedStateId,
230 });
231 const { stateId, entries } = contentAssistResult.parse(result);
232 if (stateId !== expectedStateId) {
233 throw new Error(`Unexpected state id, expected: ${expectedStateId} got: ${stateId}`);
234 }
235 return entries;
236 }
237
238 async formatText(): Promise<void> {
239 await this.update();
240 let { from, to } = this.store.state.selection.main;
241 if (to <= from) {
242 from = 0;
243 to = this.store.state.doc.length;
244 }
245 log.debug('Formatting from', from, 'to', to);
246 await this.withUpdate(async () => {
247 const result = await this.webSocketClient.send({
248 resource: this.resourceName,
249 serviceType: 'format',
250 selectionStart: from,
251 selectionEnd: to,
252 });
253 const { stateId, formattedText } = formattingResult.parse(result);
254 this.applyBeforeDirtyChanges({
255 from,
256 to,
257 insert: formattedText,
258 });
259 return [stateId, null];
260 });
261 }
262
263 private computeDelta() {
264 if (this.dirtyChanges.empty) {
265 return null;
266 }
267 let minFromA = Number.MAX_SAFE_INTEGER;
268 let maxToA = 0;
269 let minFromB = Number.MAX_SAFE_INTEGER;
270 let maxToB = 0;
271 this.dirtyChanges.iterChangedRanges((fromA, toA, fromB, toB) => {
272 minFromA = Math.min(minFromA, fromA);
273 maxToA = Math.max(maxToA, toA);
274 minFromB = Math.min(minFromB, fromB);
275 maxToB = Math.max(maxToB, toB);
276 });
277 return {
278 deltaOffset: minFromA,
279 deltaReplaceLength: maxToA - minFromA,
280 deltaText: this.store.state.doc.sliceString(minFromB, maxToB),
281 };
282 }
283
284 private applyBeforeDirtyChanges(changeSpec: ChangeSpec) {
285 const pendingChanges = this.pendingUpdate?.compose(this.dirtyChanges) || this.dirtyChanges;
286 const revertChanges = pendingChanges.invert(this.store.state.doc);
287 const applyBefore = ChangeSet.of(changeSpec, revertChanges.newLength);
288 const redoChanges = pendingChanges.map(applyBefore.desc);
289 const changeSet = revertChanges.compose(applyBefore).compose(redoChanges);
290 this.store.dispatch({
291 changes: changeSet,
292 effects: [
293 setDirtyChanges.of(redoChanges),
294 ],
295 });
296 }
297
298 /**
299 * Executes an asynchronous callback that updates the state on the server.
300 *
301 * Ensures that updates happen sequentially and manages `pendingUpdate`
302 * and `dirtyChanges` to reflect changes being synchronized to the server
303 * and not yet synchronized to the server, respectively.
304 *
305 * Optionally, `callback` may return a second value that is retured by this function.
306 *
307 * Once the remote procedure call to update the server state finishes
308 * and returns the new `stateId`, `callback` must return _immediately_
309 * to ensure that the local `stateId` is updated likewise to be able to handle
310 * push messages referring to the new `stateId` from the server.
311 * If additional work is needed to compute the second value in some cases,
312 * use `T | null` instead of `T` as a return type and signal the need for additional
313 * computations by returning `null`. Thus additional computations can be performed
314 * outside of the critical section.
315 *
316 * @param callback the asynchronous callback that updates the server state
317 * @return a promise resolving to the second value returned by `callback`
318 */
319 private async withUpdate<T>(callback: () => Promise<[string, T]>): Promise<T> {
320 if (this.pendingUpdate !== null) {
321 throw new Error('Another update is pending, will not perform update');
322 }
323 this.pendingUpdate = this.dirtyChanges;
324 this.dirtyChanges = this.newEmptyChangeSet();
325 let newStateId: string | null = null;
326 try {
327 let result: T;
328 [newStateId, result] = await callback();
329 this.xtextStateId = newStateId;
330 this.pendingUpdate = null;
331 this.updatedCondition.notifyAll();
332 return result;
333 } catch (e) {
334 log.error('Error while update', e);
335 if (this.pendingUpdate === null) {
336 log.error('pendingUpdate was cleared during update');
337 } else {
338 this.dirtyChanges = this.pendingUpdate.compose(this.dirtyChanges);
339 }
340 this.pendingUpdate = null;
341 this.webSocketClient.forceReconnectOnError();
342 this.updatedCondition.rejectAll(e);
343 throw e;
344 }
345 }
346
347 /**
348 * Ensures that there is some state available on the server (`xtextStateId`)
349 * and that there is not pending update.
350 *
351 * After this function resolves, a delta text update is possible.
352 *
353 * @return a promise resolving when there is a valid state id but no pending update
354 */
355 private async prepareForDeltaUpdate() {
356 // If no update is pending, but the full text hasn't been uploaded to the server yet,
357 // we must start a full text upload.
358 if (this.pendingUpdate === null && this.xtextStateId === null) {
359 await this.updateFullText();
360 }
361 await this.updatedCondition.waitFor();
362 }
363}
diff --git a/subprojects/language-web/src/main/js/xtext/ValidationService.ts b/subprojects/language-web/src/main/js/xtext/ValidationService.ts
new file mode 100644
index 00000000..ff7d3700
--- /dev/null
+++ b/subprojects/language-web/src/main/js/xtext/ValidationService.ts
@@ -0,0 +1,39 @@
1import type { Diagnostic } from '@codemirror/lint';
2
3import type { EditorStore } from '../editor/EditorStore';
4import type { UpdateService } from './UpdateService';
5import { validationResult } from './xtextServiceResults';
6
7export class ValidationService {
8 private readonly store: EditorStore;
9
10 private readonly updateService: UpdateService;
11
12 constructor(store: EditorStore, updateService: UpdateService) {
13 this.store = store;
14 this.updateService = updateService;
15 }
16
17 onPush(push: unknown): void {
18 const { issues } = validationResult.parse(push);
19 const allChanges = this.updateService.computeChangesSinceLastUpdate();
20 const diagnostics: Diagnostic[] = [];
21 issues.forEach(({
22 offset,
23 length,
24 severity,
25 description,
26 }) => {
27 if (severity === 'ignore') {
28 return;
29 }
30 diagnostics.push({
31 from: allChanges.mapPos(offset),
32 to: allChanges.mapPos(offset + length),
33 severity,
34 message: description,
35 });
36 });
37 this.store.updateDiagnostics(diagnostics);
38 }
39}
diff --git a/subprojects/language-web/src/main/js/xtext/XtextClient.ts b/subprojects/language-web/src/main/js/xtext/XtextClient.ts
new file mode 100644
index 00000000..0898e725
--- /dev/null
+++ b/subprojects/language-web/src/main/js/xtext/XtextClient.ts
@@ -0,0 +1,86 @@
1import type {
2 CompletionContext,
3 CompletionResult,
4} from '@codemirror/autocomplete';
5import type { Transaction } from '@codemirror/state';
6
7import type { EditorStore } from '../editor/EditorStore';
8import { ContentAssistService } from './ContentAssistService';
9import { HighlightingService } from './HighlightingService';
10import { OccurrencesService } from './OccurrencesService';
11import { UpdateService } from './UpdateService';
12import { getLogger } from '../utils/logger';
13import { ValidationService } from './ValidationService';
14import { XtextWebSocketClient } from './XtextWebSocketClient';
15import { XtextWebPushService } from './xtextMessages';
16
17const log = getLogger('xtext.XtextClient');
18
19export class XtextClient {
20 private readonly webSocketClient: XtextWebSocketClient;
21
22 private readonly updateService: UpdateService;
23
24 private readonly contentAssistService: ContentAssistService;
25
26 private readonly highlightingService: HighlightingService;
27
28 private readonly validationService: ValidationService;
29
30 private readonly occurrencesService: OccurrencesService;
31
32 constructor(store: EditorStore) {
33 this.webSocketClient = new XtextWebSocketClient(
34 () => this.updateService.onReconnect(),
35 (resource, stateId, service, push) => this.onPush(resource, stateId, service, push),
36 );
37 this.updateService = new UpdateService(store, this.webSocketClient);
38 this.contentAssistService = new ContentAssistService(this.updateService);
39 this.highlightingService = new HighlightingService(store, this.updateService);
40 this.validationService = new ValidationService(store, this.updateService);
41 this.occurrencesService = new OccurrencesService(
42 store,
43 this.webSocketClient,
44 this.updateService,
45 );
46 }
47
48 onTransaction(transaction: Transaction): void {
49 // `ContentAssistService.prototype.onTransaction` needs the dirty change desc
50 // _before_ the current edit, so we call it before `updateService`.
51 this.contentAssistService.onTransaction(transaction);
52 this.updateService.onTransaction(transaction);
53 this.occurrencesService.onTransaction(transaction);
54 }
55
56 private onPush(resource: string, stateId: string, service: XtextWebPushService, push: unknown) {
57 const { resourceName, xtextStateId } = this.updateService;
58 if (resource !== resourceName) {
59 log.error('Unknown resource name: expected:', resourceName, 'got:', resource);
60 return;
61 }
62 if (stateId !== xtextStateId) {
63 log.error('Unexpected xtext state id: expected:', xtextStateId, 'got:', stateId);
64 // The current push message might be stale (referring to a previous state),
65 // so this is not neccessarily an error and there is no need to force-reconnect.
66 return;
67 }
68 switch (service) {
69 case 'highlight':
70 this.highlightingService.onPush(push);
71 return;
72 case 'validate':
73 this.validationService.onPush(push);
74 }
75 }
76
77 contentAssist(context: CompletionContext): Promise<CompletionResult> {
78 return this.contentAssistService.contentAssist(context);
79 }
80
81 formatText(): void {
82 this.updateService.formatText().catch((e) => {
83 log.error('Error while formatting text', e);
84 });
85 }
86}
diff --git a/subprojects/language-web/src/main/js/xtext/XtextWebSocketClient.ts b/subprojects/language-web/src/main/js/xtext/XtextWebSocketClient.ts
new file mode 100644
index 00000000..2ce20a54
--- /dev/null
+++ b/subprojects/language-web/src/main/js/xtext/XtextWebSocketClient.ts
@@ -0,0 +1,362 @@
1import { nanoid } from 'nanoid';
2
3import { getLogger } from '../utils/logger';
4import { PendingTask } from '../utils/PendingTask';
5import { Timer } from '../utils/Timer';
6import {
7 xtextWebErrorResponse,
8 XtextWebRequest,
9 xtextWebOkResponse,
10 xtextWebPushMessage,
11 XtextWebPushService,
12} from './xtextMessages';
13import { pongResult } from './xtextServiceResults';
14
15const XTEXT_SUBPROTOCOL_V1 = 'tools.refinery.language.web.xtext.v1';
16
17const WEBSOCKET_CLOSE_OK = 1000;
18
19const RECONNECT_DELAY_MS = [200, 1000, 5000, 30_000];
20
21const MAX_RECONNECT_DELAY_MS = RECONNECT_DELAY_MS[RECONNECT_DELAY_MS.length - 1];
22
23const BACKGROUND_IDLE_TIMEOUT_MS = 5 * 60 * 1000;
24
25const PING_TIMEOUT_MS = 10 * 1000;
26
27const REQUEST_TIMEOUT_MS = 1000;
28
29const log = getLogger('xtext.XtextWebSocketClient');
30
31export type ReconnectHandler = () => void;
32
33export type PushHandler = (
34 resourceId: string,
35 stateId: string,
36 service: XtextWebPushService,
37 data: unknown,
38) => void;
39
40enum State {
41 Initial,
42 Opening,
43 TabVisible,
44 TabHiddenIdle,
45 TabHiddenWaiting,
46 Error,
47 TimedOut,
48}
49
50export class XtextWebSocketClient {
51 private nextMessageId = 0;
52
53 private connection!: WebSocket;
54
55 private readonly pendingRequests = new Map<string, PendingTask<unknown>>();
56
57 private readonly onReconnect: ReconnectHandler;
58
59 private readonly onPush: PushHandler;
60
61 private state = State.Initial;
62
63 private reconnectTryCount = 0;
64
65 private readonly idleTimer = new Timer(() => {
66 this.handleIdleTimeout();
67 }, BACKGROUND_IDLE_TIMEOUT_MS);
68
69 private readonly pingTimer = new Timer(() => {
70 this.sendPing();
71 }, PING_TIMEOUT_MS);
72
73 private readonly reconnectTimer = new Timer(() => {
74 this.handleReconnect();
75 });
76
77 constructor(onReconnect: ReconnectHandler, onPush: PushHandler) {
78 this.onReconnect = onReconnect;
79 this.onPush = onPush;
80 document.addEventListener('visibilitychange', () => {
81 this.handleVisibilityChange();
82 });
83 this.reconnect();
84 }
85
86 private get isLogicallyClosed(): boolean {
87 return this.state === State.Error || this.state === State.TimedOut;
88 }
89
90 get isOpen(): boolean {
91 return this.state === State.TabVisible
92 || this.state === State.TabHiddenIdle
93 || this.state === State.TabHiddenWaiting;
94 }
95
96 private reconnect() {
97 if (this.isOpen || this.state === State.Opening) {
98 log.error('Trying to reconnect from', this.state);
99 return;
100 }
101 this.state = State.Opening;
102 const webSocketServer = window.origin.replace(/^http/, 'ws');
103 const webSocketUrl = `${webSocketServer}/xtext-service`;
104 this.connection = new WebSocket(webSocketUrl, XTEXT_SUBPROTOCOL_V1);
105 this.connection.addEventListener('open', () => {
106 if (this.connection.protocol !== XTEXT_SUBPROTOCOL_V1) {
107 log.error('Unknown subprotocol', this.connection.protocol, 'selected by server');
108 this.forceReconnectOnError();
109 }
110 if (document.visibilityState === 'hidden') {
111 this.handleTabHidden();
112 } else {
113 this.handleTabVisibleConnected();
114 }
115 log.info('Connected to websocket');
116 this.nextMessageId = 0;
117 this.reconnectTryCount = 0;
118 this.pingTimer.schedule();
119 this.onReconnect();
120 });
121 this.connection.addEventListener('error', (event) => {
122 log.error('Unexpected websocket error', event);
123 this.forceReconnectOnError();
124 });
125 this.connection.addEventListener('message', (event) => {
126 this.handleMessage(event.data);
127 });
128 this.connection.addEventListener('close', (event) => {
129 if (this.isLogicallyClosed && event.code === WEBSOCKET_CLOSE_OK
130 && this.pendingRequests.size === 0) {
131 log.info('Websocket closed');
132 return;
133 }
134 log.error('Websocket closed unexpectedly', event.code, event.reason);
135 this.forceReconnectOnError();
136 });
137 }
138
139 private handleVisibilityChange() {
140 if (document.visibilityState === 'hidden') {
141 if (this.state === State.TabVisible) {
142 this.handleTabHidden();
143 }
144 return;
145 }
146 this.idleTimer.cancel();
147 if (this.state === State.TabHiddenIdle || this.state === State.TabHiddenWaiting) {
148 this.handleTabVisibleConnected();
149 return;
150 }
151 if (this.state === State.TimedOut) {
152 this.reconnect();
153 }
154 }
155
156 private handleTabHidden() {
157 log.debug('Tab hidden while websocket is connected');
158 this.state = State.TabHiddenIdle;
159 this.idleTimer.schedule();
160 }
161
162 private handleTabVisibleConnected() {
163 log.debug('Tab visible while websocket is connected');
164 this.state = State.TabVisible;
165 }
166
167 private handleIdleTimeout() {
168 log.trace('Waiting for pending tasks before disconnect');
169 if (this.state === State.TabHiddenIdle) {
170 this.state = State.TabHiddenWaiting;
171 this.handleWaitingForDisconnect();
172 }
173 }
174
175 private handleWaitingForDisconnect() {
176 if (this.state !== State.TabHiddenWaiting) {
177 return;
178 }
179 const pending = this.pendingRequests.size;
180 if (pending === 0) {
181 log.info('Closing idle websocket');
182 this.state = State.TimedOut;
183 this.closeConnection(1000, 'idle timeout');
184 return;
185 }
186 log.info('Waiting for', pending, 'pending requests before closing websocket');
187 }
188
189 private sendPing() {
190 if (!this.isOpen) {
191 return;
192 }
193 const ping = nanoid();
194 log.trace('Ping', ping);
195 this.send({ ping }).then((result) => {
196 const parsedPongResult = pongResult.safeParse(result);
197 if (parsedPongResult.success && parsedPongResult.data.pong === ping) {
198 log.trace('Pong', ping);
199 this.pingTimer.schedule();
200 } else {
201 log.error('Invalid pong:', parsedPongResult, 'expected:', ping);
202 this.forceReconnectOnError();
203 }
204 }).catch((error) => {
205 log.error('Error while waiting for ping', error);
206 this.forceReconnectOnError();
207 });
208 }
209
210 send(request: unknown): Promise<unknown> {
211 if (!this.isOpen) {
212 throw new Error('Not open');
213 }
214 const messageId = this.nextMessageId.toString(16);
215 if (messageId in this.pendingRequests) {
216 log.error('Message id wraparound still pending', messageId);
217 this.rejectRequest(messageId, new Error('Message id wraparound'));
218 }
219 if (this.nextMessageId >= Number.MAX_SAFE_INTEGER) {
220 this.nextMessageId = 0;
221 } else {
222 this.nextMessageId += 1;
223 }
224 const message = JSON.stringify({
225 id: messageId,
226 request,
227 } as XtextWebRequest);
228 log.trace('Sending message', message);
229 return new Promise((resolve, reject) => {
230 const task = new PendingTask(resolve, reject, REQUEST_TIMEOUT_MS, () => {
231 this.removePendingRequest(messageId);
232 });
233 this.pendingRequests.set(messageId, task);
234 this.connection.send(message);
235 });
236 }
237
238 private handleMessage(messageStr: unknown) {
239 if (typeof messageStr !== 'string') {
240 log.error('Unexpected binary message', messageStr);
241 this.forceReconnectOnError();
242 return;
243 }
244 log.trace('Incoming websocket message', messageStr);
245 let message: unknown;
246 try {
247 message = JSON.parse(messageStr);
248 } catch (error) {
249 log.error('Json parse error', error);
250 this.forceReconnectOnError();
251 return;
252 }
253 const okResponse = xtextWebOkResponse.safeParse(message);
254 if (okResponse.success) {
255 const { id, response } = okResponse.data;
256 this.resolveRequest(id, response);
257 return;
258 }
259 const errorResponse = xtextWebErrorResponse.safeParse(message);
260 if (errorResponse.success) {
261 const { id, error, message: errorMessage } = errorResponse.data;
262 this.rejectRequest(id, new Error(`${error} error: ${errorMessage}`));
263 if (error === 'server') {
264 log.error('Reconnecting due to server error: ', errorMessage);
265 this.forceReconnectOnError();
266 }
267 return;
268 }
269 const pushMessage = xtextWebPushMessage.safeParse(message);
270 if (pushMessage.success) {
271 const {
272 resource,
273 stateId,
274 service,
275 push,
276 } = pushMessage.data;
277 this.onPush(resource, stateId, service, push);
278 } else {
279 log.error(
280 'Unexpected websocket message:',
281 message,
282 'not ok response because:',
283 okResponse.error,
284 'not error response because:',
285 errorResponse.error,
286 'not push message because:',
287 pushMessage.error,
288 );
289 this.forceReconnectOnError();
290 }
291 }
292
293 private resolveRequest(messageId: string, value: unknown) {
294 const pendingRequest = this.pendingRequests.get(messageId);
295 if (pendingRequest) {
296 pendingRequest.resolve(value);
297 this.removePendingRequest(messageId);
298 return;
299 }
300 log.error('Trying to resolve unknown request', messageId, 'with', value);
301 }
302
303 private rejectRequest(messageId: string, reason?: unknown) {
304 const pendingRequest = this.pendingRequests.get(messageId);
305 if (pendingRequest) {
306 pendingRequest.reject(reason);
307 this.removePendingRequest(messageId);
308 return;
309 }
310 log.error('Trying to reject unknown request', messageId, 'with', reason);
311 }
312
313 private removePendingRequest(messageId: string) {
314 this.pendingRequests.delete(messageId);
315 this.handleWaitingForDisconnect();
316 }
317
318 forceReconnectOnError(): void {
319 if (this.isLogicallyClosed) {
320 return;
321 }
322 this.abortPendingRequests();
323 this.closeConnection(1000, 'reconnecting due to error');
324 log.error('Reconnecting after delay due to error');
325 this.handleErrorState();
326 }
327
328 private abortPendingRequests() {
329 this.pendingRequests.forEach((request) => {
330 request.reject(new Error('Websocket disconnect'));
331 });
332 this.pendingRequests.clear();
333 }
334
335 private closeConnection(code: number, reason: string) {
336 this.pingTimer.cancel();
337 const { readyState } = this.connection;
338 if (readyState !== WebSocket.CLOSING && readyState !== WebSocket.CLOSED) {
339 this.connection.close(code, reason);
340 }
341 }
342
343 private handleErrorState() {
344 this.state = State.Error;
345 this.reconnectTryCount += 1;
346 const delay = RECONNECT_DELAY_MS[this.reconnectTryCount - 1] || MAX_RECONNECT_DELAY_MS;
347 log.info('Reconnecting in', delay, 'ms');
348 this.reconnectTimer.schedule(delay);
349 }
350
351 private handleReconnect() {
352 if (this.state !== State.Error) {
353 log.error('Unexpected reconnect in', this.state);
354 return;
355 }
356 if (document.visibilityState === 'hidden') {
357 this.state = State.TimedOut;
358 } else {
359 this.reconnect();
360 }
361 }
362}
diff --git a/subprojects/language-web/src/main/js/xtext/xtextMessages.ts b/subprojects/language-web/src/main/js/xtext/xtextMessages.ts
new file mode 100644
index 00000000..c4305fcf
--- /dev/null
+++ b/subprojects/language-web/src/main/js/xtext/xtextMessages.ts
@@ -0,0 +1,40 @@
1import { z } from 'zod';
2
3export const xtextWebRequest = z.object({
4 id: z.string().nonempty(),
5 request: z.unknown(),
6});
7
8export type XtextWebRequest = z.infer<typeof xtextWebRequest>;
9
10export const xtextWebOkResponse = z.object({
11 id: z.string().nonempty(),
12 response: z.unknown(),
13});
14
15export type XtextWebOkResponse = z.infer<typeof xtextWebOkResponse>;
16
17export const xtextWebErrorKind = z.enum(['request', 'server']);
18
19export type XtextWebErrorKind = z.infer<typeof xtextWebErrorKind>;
20
21export const xtextWebErrorResponse = z.object({
22 id: z.string().nonempty(),
23 error: xtextWebErrorKind,
24 message: z.string(),
25});
26
27export type XtextWebErrorResponse = z.infer<typeof xtextWebErrorResponse>;
28
29export const xtextWebPushService = z.enum(['highlight', 'validate']);
30
31export type XtextWebPushService = z.infer<typeof xtextWebPushService>;
32
33export const xtextWebPushMessage = z.object({
34 resource: z.string().nonempty(),
35 stateId: z.string().nonempty(),
36 service: xtextWebPushService,
37 push: z.unknown(),
38});
39
40export type XtextWebPushMessage = z.infer<typeof xtextWebPushMessage>;
diff --git a/subprojects/language-web/src/main/js/xtext/xtextServiceResults.ts b/subprojects/language-web/src/main/js/xtext/xtextServiceResults.ts
new file mode 100644
index 00000000..f79b059c
--- /dev/null
+++ b/subprojects/language-web/src/main/js/xtext/xtextServiceResults.ts
@@ -0,0 +1,112 @@
1import { z } from 'zod';
2
3export const pongResult = z.object({
4 pong: z.string().nonempty(),
5});
6
7export type PongResult = z.infer<typeof pongResult>;
8
9export const documentStateResult = z.object({
10 stateId: z.string().nonempty(),
11});
12
13export type DocumentStateResult = z.infer<typeof documentStateResult>;
14
15export const conflict = z.enum(['invalidStateId', 'canceled']);
16
17export type Conflict = z.infer<typeof conflict>;
18
19export const serviceConflictResult = z.object({
20 conflict,
21});
22
23export type ServiceConflictResult = z.infer<typeof serviceConflictResult>;
24
25export function isConflictResult(result: unknown, conflictType: Conflict): boolean {
26 const parsedConflictResult = serviceConflictResult.safeParse(result);
27 return parsedConflictResult.success && parsedConflictResult.data.conflict === conflictType;
28}
29
30export const severity = z.enum(['error', 'warning', 'info', 'ignore']);
31
32export type Severity = z.infer<typeof severity>;
33
34export const issue = z.object({
35 description: z.string().nonempty(),
36 severity,
37 line: z.number().int(),
38 column: z.number().int().nonnegative(),
39 offset: z.number().int().nonnegative(),
40 length: z.number().int().nonnegative(),
41});
42
43export type Issue = z.infer<typeof issue>;
44
45export const validationResult = z.object({
46 issues: issue.array(),
47});
48
49export type ValidationResult = z.infer<typeof validationResult>;
50
51export const replaceRegion = z.object({
52 offset: z.number().int().nonnegative(),
53 length: z.number().int().nonnegative(),
54 text: z.string(),
55});
56
57export type ReplaceRegion = z.infer<typeof replaceRegion>;
58
59export const textRegion = z.object({
60 offset: z.number().int().nonnegative(),
61 length: z.number().int().nonnegative(),
62});
63
64export type TextRegion = z.infer<typeof textRegion>;
65
66export const contentAssistEntry = z.object({
67 prefix: z.string(),
68 proposal: z.string().nonempty(),
69 label: z.string().optional(),
70 description: z.string().nonempty().optional(),
71 documentation: z.string().nonempty().optional(),
72 escapePosition: z.number().int().nonnegative().optional(),
73 textReplacements: replaceRegion.array(),
74 editPositions: textRegion.array(),
75 kind: z.string().nonempty(),
76});
77
78export type ContentAssistEntry = z.infer<typeof contentAssistEntry>;
79
80export const contentAssistResult = documentStateResult.extend({
81 entries: contentAssistEntry.array(),
82});
83
84export type ContentAssistResult = z.infer<typeof contentAssistResult>;
85
86export const highlightingRegion = z.object({
87 offset: z.number().int().nonnegative(),
88 length: z.number().int().nonnegative(),
89 styleClasses: z.string().nonempty().array(),
90});
91
92export type HighlightingRegion = z.infer<typeof highlightingRegion>;
93
94export const highlightingResult = z.object({
95 regions: highlightingRegion.array(),
96});
97
98export type HighlightingResult = z.infer<typeof highlightingResult>;
99
100export const occurrencesResult = documentStateResult.extend({
101 writeRegions: textRegion.array(),
102 readRegions: textRegion.array(),
103});
104
105export type OccurrencesResult = z.infer<typeof occurrencesResult>;
106
107export const formattingResult = documentStateResult.extend({
108 formattedText: z.string(),
109 replaceRegion: textRegion,
110});
111
112export type FormattingResult = z.infer<typeof formattingResult>;
diff --git a/subprojects/language-web/src/test/java/tools/refinery/language/web/ProblemWebSocketServletIntegrationTest.java b/subprojects/language-web/src/test/java/tools/refinery/language/web/ProblemWebSocketServletIntegrationTest.java
new file mode 100644
index 00000000..a26ce040
--- /dev/null
+++ b/subprojects/language-web/src/test/java/tools/refinery/language/web/ProblemWebSocketServletIntegrationTest.java
@@ -0,0 +1,204 @@
1package tools.refinery.language.web;
2
3import static org.hamcrest.MatcherAssert.assertThat;
4import static org.hamcrest.Matchers.equalTo;
5import static org.hamcrest.Matchers.hasSize;
6import static org.hamcrest.Matchers.instanceOf;
7import static org.hamcrest.Matchers.startsWith;
8import static org.junit.jupiter.api.Assertions.assertThrows;
9
10import java.io.IOException;
11import java.net.InetSocketAddress;
12import java.net.URI;
13import java.util.concurrent.CompletableFuture;
14import java.util.concurrent.CompletionException;
15
16import org.eclipse.jetty.http.HttpHeader;
17import org.eclipse.jetty.http.HttpStatus;
18import org.eclipse.jetty.server.Server;
19import org.eclipse.jetty.servlet.ServletContextHandler;
20import org.eclipse.jetty.servlet.ServletHolder;
21import org.eclipse.jetty.websocket.api.Session;
22import org.eclipse.jetty.websocket.api.StatusCode;
23import org.eclipse.jetty.websocket.api.annotations.WebSocket;
24import org.eclipse.jetty.websocket.api.exceptions.UpgradeException;
25import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
26import org.eclipse.jetty.websocket.client.WebSocketClient;
27import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
28import org.eclipse.xtext.testing.GlobalRegistries;
29import org.eclipse.xtext.testing.GlobalRegistries.GlobalStateMemento;
30import org.junit.jupiter.api.AfterEach;
31import org.junit.jupiter.api.BeforeEach;
32import org.junit.jupiter.api.Test;
33import org.junit.jupiter.params.ParameterizedTest;
34import org.junit.jupiter.params.provider.ValueSource;
35
36import tools.refinery.language.web.tests.WebSocketIntegrationTestClient;
37import tools.refinery.language.web.xtext.servlet.XtextStatusCode;
38import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet;
39
40class ProblemWebSocketServletIntegrationTest {
41 private static int SERVER_PORT = 28080;
42
43 private static String SERVLET_URI = "/xtext-service";
44
45 private GlobalStateMemento stateBeforeInjectorCreation;
46
47 private Server server;
48
49 private WebSocketClient client;
50
51 @BeforeEach
52 void beforeEach() throws Exception {
53 stateBeforeInjectorCreation = GlobalRegistries.makeCopyOfGlobalState();
54 client = new WebSocketClient();
55 client.start();
56 }
57
58 @AfterEach
59 void afterEach() throws Exception {
60 client.stop();
61 client = null;
62 if (server != null) {
63 server.stop();
64 server = null;
65 }
66 stateBeforeInjectorCreation.restoreGlobalState();
67 stateBeforeInjectorCreation = null;
68 }
69
70 @Test
71 void updateTest() {
72 startServer(null);
73 var clientSocket = new UpdateTestClient();
74 var session = connect(clientSocket, null, XtextWebSocketServlet.XTEXT_SUBPROTOCOL_V1);
75 assertThat(session.getUpgradeResponse().getAcceptedSubProtocol(),
76 equalTo(XtextWebSocketServlet.XTEXT_SUBPROTOCOL_V1));
77 clientSocket.waitForTestResult();
78 assertThat(clientSocket.getCloseStatusCode(), equalTo(StatusCode.NORMAL));
79 var responses = clientSocket.getResponses();
80 assertThat(responses, hasSize(5));
81 assertThat(responses.get(0), equalTo("{\"id\":\"foo\",\"response\":{\"stateId\":\"-80000000\"}}"));
82 assertThat(responses.get(1), startsWith(
83 "{\"resource\":\"test.problem\",\"stateId\":\"-80000000\",\"service\":\"highlight\",\"push\":{\"regions\":["));
84 assertThat(responses.get(2), equalTo(
85 "{\"resource\":\"test.problem\",\"stateId\":\"-80000000\",\"service\":\"validate\",\"push\":{\"issues\":[]}}"));
86 assertThat(responses.get(3), equalTo("{\"id\":\"bar\",\"response\":{\"stateId\":\"-7fffffff\"}}"));
87 assertThat(responses.get(4), startsWith(
88 "{\"resource\":\"test.problem\",\"stateId\":\"-7fffffff\",\"service\":\"highlight\",\"push\":{\"regions\":["));
89 }
90
91 @WebSocket
92 public static class UpdateTestClient extends WebSocketIntegrationTestClient {
93 @Override
94 protected void arrange(Session session, int responsesReceived) throws IOException {
95 switch (responsesReceived) {
96 case 0 -> session.getRemote().sendString(
97 "{\"id\":\"foo\",\"request\":{\"resource\":\"test.problem\",\"serviceType\":\"update\",\"fullText\":\"class Person.\n\"}}");
98 case 3 -> session.getRemote().sendString(
99 "{\"id\":\"bar\",\"request\":{\"resource\":\"test.problem\",\"serviceType\":\"update\",\"requiredStateId\":\"-80000000\",\"deltaText\":\"indiv q.\nnode(q).\n\",\"deltaOffset\":\"0\",\"deltaReplaceLength\":\"0\"}}");
100 case 5 -> session.close();
101 }
102 }
103 }
104
105 @Test
106 void badSubProtocolTest() {
107 startServer(null);
108 var clientSocket = new CloseImmediatelyTestClient();
109 var session = connect(clientSocket, null, "<invalid sub-protocol>");
110 assertThat(session.getUpgradeResponse().getAcceptedSubProtocol(), equalTo(null));
111 clientSocket.waitForTestResult();
112 assertThat(clientSocket.getCloseStatusCode(), equalTo(StatusCode.NORMAL));
113 }
114
115 @WebSocket
116 public static class CloseImmediatelyTestClient extends WebSocketIntegrationTestClient {
117 @Override
118 protected void arrange(Session session, int responsesReceived) throws IOException {
119 session.close();
120 }
121 }
122
123 @Test
124 void subProtocolNegotiationTest() {
125 startServer(null);
126 var clientSocket = new CloseImmediatelyTestClient();
127 var session = connect(clientSocket, null, "<invalid sub-protocol>", XtextWebSocketServlet.XTEXT_SUBPROTOCOL_V1);
128 assertThat(session.getUpgradeResponse().getAcceptedSubProtocol(),
129 equalTo(XtextWebSocketServlet.XTEXT_SUBPROTOCOL_V1));
130 clientSocket.waitForTestResult();
131 assertThat(clientSocket.getCloseStatusCode(), equalTo(StatusCode.NORMAL));
132 }
133
134 @Test
135 void invalidJsonTest() {
136 startServer(null);
137 var clientSocket = new InvalidJsonTestClient();
138 connect(clientSocket, null, XtextWebSocketServlet.XTEXT_SUBPROTOCOL_V1);
139 clientSocket.waitForTestResult();
140 assertThat(clientSocket.getCloseStatusCode(), equalTo(XtextStatusCode.INVALID_JSON));
141 }
142
143 @WebSocket
144 public static class InvalidJsonTestClient extends WebSocketIntegrationTestClient {
145 @Override
146 protected void arrange(Session session, int responsesReceived) throws IOException {
147 session.getRemote().sendString("<invalid json>");
148 }
149 }
150
151 @ParameterizedTest(name = "Origin: {0}")
152 @ValueSource(strings = { "https://refinery.example", "https://refinery.example:443", "HTTPS://REFINERY.EXAMPLE" })
153 void validOriginTest(String origin) {
154 startServer("https://refinery.example;https://refinery.example:443");
155 var clientSocket = new CloseImmediatelyTestClient();
156 connect(clientSocket, origin, XtextWebSocketServlet.XTEXT_SUBPROTOCOL_V1);
157 clientSocket.waitForTestResult();
158 assertThat(clientSocket.getCloseStatusCode(), equalTo(StatusCode.NORMAL));
159 }
160
161 @Test
162 void invalidOriginTest() {
163 startServer("https://refinery.example;https://refinery.example:443");
164 var clientSocket = new CloseImmediatelyTestClient();
165 var exception = assertThrows(CompletionException.class,
166 () -> connect(clientSocket, "https://invalid.example", XtextWebSocketServlet.XTEXT_SUBPROTOCOL_V1));
167 var innerException = exception.getCause();
168 assertThat(innerException, instanceOf(UpgradeException.class));
169 assertThat(((UpgradeException) innerException).getResponseStatusCode(), equalTo(HttpStatus.FORBIDDEN_403));
170 }
171
172 private void startServer(String allowedOrigins) {
173 server = new Server(new InetSocketAddress(SERVER_PORT));
174 var handler = new ServletContextHandler();
175 var holder = new ServletHolder(ProblemWebSocketServlet.class);
176 if (allowedOrigins != null) {
177 holder.setInitParameter(ProblemWebSocketServlet.ALLOWED_ORIGINS_INIT_PARAM, allowedOrigins);
178 }
179 handler.addServlet(holder, SERVLET_URI);
180 JettyWebSocketServletContainerInitializer.configure(handler, null);
181 server.setHandler(handler);
182 try {
183 server.start();
184 } catch (Exception e) {
185 throw new RuntimeException("Failed to start websocket server");
186 }
187 }
188
189 private Session connect(Object webSocketClient, String origin, String... subProtocols) {
190 var upgradeRequest = new ClientUpgradeRequest();
191 if (origin != null) {
192 upgradeRequest.setHeader(HttpHeader.ORIGIN.name(), origin);
193 }
194 upgradeRequest.setSubProtocols(subProtocols);
195 CompletableFuture<Session> sessionFuture;
196 try {
197 sessionFuture = client.connect(webSocketClient, URI.create("ws://localhost:" + SERVER_PORT + SERVLET_URI),
198 upgradeRequest);
199 } catch (IOException e) {
200 throw new AssertionError("Unexpected exception while connection to websocket", e);
201 }
202 return sessionFuture.join();
203 }
204}
diff --git a/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/AwaitTerminationExecutorServiceProvider.java b/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/AwaitTerminationExecutorServiceProvider.java
new file mode 100644
index 00000000..b70d0ed5
--- /dev/null
+++ b/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/AwaitTerminationExecutorServiceProvider.java
@@ -0,0 +1,42 @@
1package tools.refinery.language.web.tests;
2
3import java.util.ArrayList;
4import java.util.List;
5import java.util.concurrent.ExecutorService;
6
7import org.eclipse.xtext.ide.ExecutorServiceProvider;
8
9import com.google.inject.Singleton;
10
11@Singleton
12public class AwaitTerminationExecutorServiceProvider extends ExecutorServiceProvider {
13 private List<RestartableCachedThreadPool> servicesToShutDown = new ArrayList<>();
14
15 @Override
16 protected ExecutorService createInstance(String key) {
17 var instance = new RestartableCachedThreadPool();
18 synchronized (servicesToShutDown) {
19 servicesToShutDown.add(instance);
20 }
21 return instance;
22 }
23
24 public void waitForAllTasksToFinish() {
25 synchronized (servicesToShutDown) {
26 for (var executorService : servicesToShutDown) {
27 executorService.waitForAllTasksToFinish();
28 }
29 }
30 }
31
32 @Override
33 public void dispose() {
34 super.dispose();
35 synchronized (servicesToShutDown) {
36 for (var executorService : servicesToShutDown) {
37 executorService.waitForTermination();
38 }
39 servicesToShutDown.clear();
40 }
41 }
42}
diff --git a/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/ProblemWebInjectorProvider.java b/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/ProblemWebInjectorProvider.java
new file mode 100644
index 00000000..43c12faa
--- /dev/null
+++ b/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/ProblemWebInjectorProvider.java
@@ -0,0 +1,47 @@
1package tools.refinery.language.web.tests;
2
3import org.eclipse.xtext.ide.ExecutorServiceProvider;
4import org.eclipse.xtext.util.DisposableRegistry;
5import org.eclipse.xtext.util.Modules2;
6
7import com.google.inject.Guice;
8import com.google.inject.Injector;
9
10import tools.refinery.language.ide.ProblemIdeModule;
11import tools.refinery.language.tests.ProblemInjectorProvider;
12import tools.refinery.language.web.ProblemWebModule;
13import tools.refinery.language.web.ProblemWebSetup;
14
15public class ProblemWebInjectorProvider extends ProblemInjectorProvider {
16
17 protected Injector internalCreateInjector() {
18 return new ProblemWebSetup() {
19 @Override
20 public Injector createInjector() {
21 return Guice.createInjector(
22 Modules2.mixin(createRuntimeModule(), new ProblemIdeModule(), createWebModule()));
23 }
24 }.createInjectorAndDoEMFRegistration();
25 }
26
27 protected ProblemWebModule createWebModule() {
28 // Await termination of the executor service to avoid race conditions between
29 // the tasks in the service and the {@link
30 // org.eclipse.xtext.testing.extensions.InjectionExtension}.
31 return new ProblemWebModule() {
32 @SuppressWarnings("unused")
33 public Class<? extends ExecutorServiceProvider> bindExecutorServiceProvider() {
34 return AwaitTerminationExecutorServiceProvider.class;
35 }
36 };
37 }
38
39 @Override
40 public void restoreRegistry() {
41 // Also make sure to dispose any IDisposable instances (that may depend on the
42 // global state) created by Xtext before restoring the global state.
43 var disposableRegistry = getInjector().getInstance(DisposableRegistry.class);
44 disposableRegistry.dispose();
45 super.restoreRegistry();
46 }
47}
diff --git a/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/RestartableCachedThreadPool.java b/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/RestartableCachedThreadPool.java
new file mode 100644
index 00000000..1468273d
--- /dev/null
+++ b/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/RestartableCachedThreadPool.java
@@ -0,0 +1,109 @@
1package tools.refinery.language.web.tests;
2
3import java.util.Collection;
4import java.util.List;
5import java.util.concurrent.Callable;
6import java.util.concurrent.ExecutionException;
7import java.util.concurrent.ExecutorService;
8import java.util.concurrent.Executors;
9import java.util.concurrent.Future;
10import java.util.concurrent.TimeUnit;
11import java.util.concurrent.TimeoutException;
12
13import org.slf4j.Logger;
14import org.slf4j.LoggerFactory;
15
16public class RestartableCachedThreadPool implements ExecutorService {
17 private static final Logger LOG = LoggerFactory.getLogger(RestartableCachedThreadPool.class);
18
19 private ExecutorService delegate;
20
21 public RestartableCachedThreadPool() {
22 delegate = createExecutorService();
23 }
24
25 public void waitForAllTasksToFinish() {
26 delegate.shutdown();
27 waitForTermination();
28 delegate = createExecutorService();
29 }
30
31 public void waitForTermination() {
32 try {
33 delegate.awaitTermination(1, TimeUnit.SECONDS);
34 } catch (InterruptedException e) {
35 LOG.warn("Interrupted while waiting for delegate executor to stop", e);
36 }
37 }
38
39 protected ExecutorService createExecutorService() {
40 return Executors.newCachedThreadPool();
41 }
42
43 @Override
44 public boolean awaitTermination(long arg0, TimeUnit arg1) throws InterruptedException {
45 return delegate.awaitTermination(arg0, arg1);
46 }
47
48 @Override
49 public void execute(Runnable arg0) {
50 delegate.execute(arg0);
51 }
52
53 @Override
54 public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> arg0, long arg1, TimeUnit arg2)
55 throws InterruptedException {
56 return delegate.invokeAll(arg0, arg1, arg2);
57 }
58
59 @Override
60 public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> arg0) throws InterruptedException {
61 return delegate.invokeAll(arg0);
62 }
63
64 @Override
65 public <T> T invokeAny(Collection<? extends Callable<T>> arg0, long arg1, TimeUnit arg2)
66 throws InterruptedException, ExecutionException, TimeoutException {
67 return delegate.invokeAny(arg0, arg1, arg2);
68 }
69
70 @Override
71 public <T> T invokeAny(Collection<? extends Callable<T>> arg0) throws InterruptedException, ExecutionException {
72 return delegate.invokeAny(arg0);
73 }
74
75 @Override
76 public boolean isShutdown() {
77 return delegate.isShutdown();
78 }
79
80 @Override
81 public boolean isTerminated() {
82 return delegate.isTerminated();
83 }
84
85 @Override
86 public void shutdown() {
87 delegate.shutdown();
88 }
89
90 @Override
91 public List<Runnable> shutdownNow() {
92 return delegate.shutdownNow();
93 }
94
95 @Override
96 public <T> Future<T> submit(Callable<T> arg0) {
97 return delegate.submit(arg0);
98 }
99
100 @Override
101 public <T> Future<T> submit(Runnable arg0, T arg1) {
102 return delegate.submit(arg0, arg1);
103 }
104
105 @Override
106 public Future<?> submit(Runnable arg0) {
107 return delegate.submit(arg0);
108 }
109}
diff --git a/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/WebSocketIntegrationTestClient.java b/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/WebSocketIntegrationTestClient.java
new file mode 100644
index 00000000..49464d27
--- /dev/null
+++ b/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/WebSocketIntegrationTestClient.java
@@ -0,0 +1,98 @@
1package tools.refinery.language.web.tests;
2
3import static org.junit.jupiter.api.Assertions.fail;
4
5import java.io.IOException;
6import java.time.Duration;
7import java.util.ArrayList;
8import java.util.List;
9
10import org.eclipse.jetty.websocket.api.Session;
11import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
12import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
13import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
14import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
15
16public abstract class WebSocketIntegrationTestClient {
17 private static long TIMEOUT_MILLIS = Duration.ofSeconds(1).toMillis();
18
19 private boolean finished = false;
20
21 private Object lock = new Object();
22
23 private Throwable error;
24
25 private int closeStatusCode;
26
27 private List<String> responses = new ArrayList<>();
28
29 public int getCloseStatusCode() {
30 return closeStatusCode;
31 }
32
33 public List<String> getResponses() {
34 return responses;
35 }
36
37 @OnWebSocketConnect
38 public void onConnect(Session session) {
39 arrangeAndCatchErrors(session);
40 }
41
42 private void arrangeAndCatchErrors(Session session) {
43 try {
44 arrange(session, responses.size());
45 } catch (Exception e) {
46 finishedWithError(e);
47 }
48 }
49
50 protected abstract void arrange(Session session, int responsesReceived) throws IOException;
51
52 @OnWebSocketClose
53 public void onClose(int statusCode, String reason) {
54 closeStatusCode = statusCode;
55 testFinished();
56 }
57
58 @OnWebSocketError
59 public void onError(Throwable error) {
60 finishedWithError(error);
61 }
62
63 @OnWebSocketMessage
64 public void onMessage(Session session, String message) {
65 responses.add(message);
66 arrangeAndCatchErrors(session);
67 }
68
69 private void finishedWithError(Throwable t) {
70 error = t;
71 testFinished();
72 }
73
74 private void testFinished() {
75 synchronized (lock) {
76 finished = true;
77 lock.notify();
78 }
79 }
80
81 public void waitForTestResult() {
82 synchronized (lock) {
83 if (!finished) {
84 try {
85 lock.wait(TIMEOUT_MILLIS);
86 } catch (InterruptedException e) {
87 fail("Unexpected InterruptedException", e);
88 }
89 }
90 }
91 if (!finished) {
92 fail("Test still not finished after timeout");
93 }
94 if (error != null) {
95 fail("Unexpected exception in websocket thread", error);
96 }
97 }
98}
diff --git a/subprojects/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/TransactionExecutorTest.java b/subprojects/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/TransactionExecutorTest.java
new file mode 100644
index 00000000..5b8fedba
--- /dev/null
+++ b/subprojects/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/TransactionExecutorTest.java
@@ -0,0 +1,165 @@
1package tools.refinery.language.web.xtext.servlet;
2
3import static org.hamcrest.MatcherAssert.assertThat;
4import static org.hamcrest.Matchers.equalTo;
5import static org.hamcrest.Matchers.hasProperty;
6import static org.hamcrest.Matchers.instanceOf;
7import static org.mockito.Mockito.mock;
8import static org.mockito.Mockito.times;
9import static org.mockito.Mockito.verify;
10
11import java.util.List;
12import java.util.Map;
13
14import org.eclipse.emf.common.util.URI;
15import org.eclipse.xtext.resource.IResourceServiceProvider;
16import org.eclipse.xtext.testing.InjectWith;
17import org.eclipse.xtext.testing.extensions.InjectionExtension;
18import org.eclipse.xtext.web.server.model.DocumentStateResult;
19import org.eclipse.xtext.web.server.syntaxcoloring.HighlightingResult;
20import org.eclipse.xtext.web.server.validation.ValidationResult;
21import org.junit.jupiter.api.BeforeEach;
22import org.junit.jupiter.api.Test;
23import org.junit.jupiter.api.extension.ExtendWith;
24import org.mockito.ArgumentCaptor;
25import org.mockito.junit.jupiter.MockitoExtension;
26
27import com.google.inject.Inject;
28
29import tools.refinery.language.web.tests.AwaitTerminationExecutorServiceProvider;
30import tools.refinery.language.web.tests.ProblemWebInjectorProvider;
31import tools.refinery.language.web.xtext.server.ResponseHandler;
32import tools.refinery.language.web.xtext.server.ResponseHandlerException;
33import tools.refinery.language.web.xtext.server.TransactionExecutor;
34import tools.refinery.language.web.xtext.server.message.XtextWebOkResponse;
35import tools.refinery.language.web.xtext.server.message.XtextWebRequest;
36import tools.refinery.language.web.xtext.server.message.XtextWebResponse;
37
38@ExtendWith(MockitoExtension.class)
39@ExtendWith(InjectionExtension.class)
40@InjectWith(ProblemWebInjectorProvider.class)
41class TransactionExecutorTest {
42 private static final String RESOURCE_NAME = "test.problem";
43
44 private static final String PROBLEM_CONTENT_TYPE = "application/x-tools.refinery.problem";
45
46 private static final String TEST_PROBLEM = """
47 class Person {
48 Person[0..*] friend opposite friend
49 }
50
51 friend(a, b).
52 """;
53
54 private static final Map<String, String> UPDATE_FULL_TEXT_PARAMS = Map.of("resource", RESOURCE_NAME, "serviceType",
55 "update", "fullText", TEST_PROBLEM);
56
57 @Inject
58 private IResourceServiceProvider.Registry resourceServiceProviderRegistry;
59
60 @Inject
61 private AwaitTerminationExecutorServiceProvider executorServices;
62
63 private TransactionExecutor transactionExecutor;
64
65 @BeforeEach
66 void beforeEach() {
67 transactionExecutor = new TransactionExecutor(new SimpleSession(), resourceServiceProviderRegistry);
68 }
69
70 @Test
71 void updateFullTextTest() throws ResponseHandlerException {
72 var captor = newCaptor();
73 var stateId = updateFullText(captor);
74 assertThatPrecomputedMessagesAreReceived(stateId, captor.getAllValues());
75 }
76
77 @Test
78 void updateDeltaTextHighlightAndValidationChange() throws ResponseHandlerException {
79 var stateId = updateFullText();
80 var responseHandler = sendRequestAndWaitForAllResponses(
81 new XtextWebRequest("bar", Map.of("resource", RESOURCE_NAME, "serviceType", "update", "requiredStateId",
82 stateId, "deltaText", "individual q.\nnode(q).\n<invalid text>\n", "deltaOffset", "0", "deltaReplaceLength", "0")));
83
84 var captor = newCaptor();
85 verify(responseHandler, times(3)).onResponse(captor.capture());
86 var newStateId = getStateId("bar", captor.getAllValues().get(0));
87 assertThatPrecomputedMessagesAreReceived(newStateId, captor.getAllValues());
88 }
89
90 @Test
91 void updateDeltaTextHighlightChangeOnly() throws ResponseHandlerException {
92 var stateId = updateFullText();
93 var responseHandler = sendRequestAndWaitForAllResponses(
94 new XtextWebRequest("bar", Map.of("resource", RESOURCE_NAME, "serviceType", "update", "requiredStateId",
95 stateId, "deltaText", "indiv q.\nnode(q).\n", "deltaOffset", "0", "deltaReplaceLength", "0")));
96
97 var captor = newCaptor();
98 verify(responseHandler, times(2)).onResponse(captor.capture());
99 var newStateId = getStateId("bar", captor.getAllValues().get(0));
100 assertHighlightingResponse(newStateId, captor.getAllValues().get(1));
101 }
102
103 @Test
104 void fullTextWithoutResourceTest() throws ResponseHandlerException {
105 var resourceServiceProvider = resourceServiceProviderRegistry
106 .getResourceServiceProvider(URI.createFileURI(RESOURCE_NAME));
107 resourceServiceProviderRegistry.getContentTypeToFactoryMap().put(PROBLEM_CONTENT_TYPE, resourceServiceProvider);
108 var responseHandler = sendRequestAndWaitForAllResponses(new XtextWebRequest("foo",
109 Map.of("contentType", PROBLEM_CONTENT_TYPE, "fullText", TEST_PROBLEM, "serviceType", "validate")));
110
111 var captor = newCaptor();
112 verify(responseHandler).onResponse(captor.capture());
113 var response = captor.getValue();
114 assertThat(response, hasProperty("id", equalTo("foo")));
115 assertThat(response, hasProperty("responseData", instanceOf(ValidationResult.class)));
116 }
117
118 private ArgumentCaptor<XtextWebResponse> newCaptor() {
119 return ArgumentCaptor.forClass(XtextWebResponse.class);
120 }
121
122 private String updateFullText() throws ResponseHandlerException {
123 return updateFullText(newCaptor());
124 }
125
126 private String updateFullText(ArgumentCaptor<XtextWebResponse> captor) throws ResponseHandlerException {
127 var responseHandler = sendRequestAndWaitForAllResponses(new XtextWebRequest("foo", UPDATE_FULL_TEXT_PARAMS));
128
129 verify(responseHandler, times(3)).onResponse(captor.capture());
130 return getStateId("foo", captor.getAllValues().get(0));
131 }
132
133 private ResponseHandler sendRequestAndWaitForAllResponses(XtextWebRequest request) throws ResponseHandlerException {
134 var responseHandler = mock(ResponseHandler.class);
135 transactionExecutor.setResponseHandler(responseHandler);
136 transactionExecutor.handleRequest(request);
137 executorServices.waitForAllTasksToFinish();
138 return responseHandler;
139 }
140
141 private String getStateId(String requestId, XtextWebResponse okResponse) {
142 assertThat(okResponse, hasProperty("id", equalTo(requestId)));
143 assertThat(okResponse, hasProperty("responseData", instanceOf(DocumentStateResult.class)));
144 return ((DocumentStateResult) ((XtextWebOkResponse) okResponse).getResponseData()).getStateId();
145 }
146
147 private void assertThatPrecomputedMessagesAreReceived(String stateId, List<XtextWebResponse> responses) {
148 assertHighlightingResponse(stateId, responses.get(1));
149 assertValidationResponse(stateId, responses.get(2));
150 }
151
152 private void assertHighlightingResponse(String stateId, XtextWebResponse highlightingResponse) {
153 assertThat(highlightingResponse, hasProperty("resourceId", equalTo(RESOURCE_NAME)));
154 assertThat(highlightingResponse, hasProperty("stateId", equalTo(stateId)));
155 assertThat(highlightingResponse, hasProperty("service", equalTo("highlight")));
156 assertThat(highlightingResponse, hasProperty("pushData", instanceOf(HighlightingResult.class)));
157 }
158
159 private void assertValidationResponse(String stateId, XtextWebResponse validationResponse) {
160 assertThat(validationResponse, hasProperty("resourceId", equalTo(RESOURCE_NAME)));
161 assertThat(validationResponse, hasProperty("stateId", equalTo(stateId)));
162 assertThat(validationResponse, hasProperty("service", equalTo("validate")));
163 assertThat(validationResponse, hasProperty("pushData", instanceOf(ValidationResult.class)));
164 }
165}
diff --git a/subprojects/language-web/tsconfig.json b/subprojects/language-web/tsconfig.json
new file mode 100644
index 00000000..cb5f6b13
--- /dev/null
+++ b/subprojects/language-web/tsconfig.json
@@ -0,0 +1,18 @@
1{
2 "compilerOptions": {
3 "target": "es2020",
4 "module": "esnext",
5 "moduleResolution": "node",
6 "esModuleInterop": true,
7 "allowSyntheticDefaultImports": true,
8 "jsx": "react",
9 "strict": true,
10 "noImplicitOverride": true,
11 "noImplicitReturns": true,
12 "exactOptionalPropertyTypes": false,
13 "noEmit": true,
14 "skipLibCheck": true
15 },
16 "include": ["./src/main/js/**/*"],
17 "exclude": ["./build/generated/sources/lezer/*"]
18}
diff --git a/subprojects/language-web/tsconfig.sonar.json b/subprojects/language-web/tsconfig.sonar.json
new file mode 100644
index 00000000..54eef68b
--- /dev/null
+++ b/subprojects/language-web/tsconfig.sonar.json
@@ -0,0 +1,17 @@
1{
2 "compilerOptions": {
3 "target": "es2020",
4 "module": "esnext",
5 "moduleResolution": "node",
6 "esModuleInterop": true,
7 "allowSyntheticDefaultImports": true,
8 "jsx": "react",
9 "strict": true,
10 "noImplicitOverride": true,
11 "noImplicitReturns": true,
12 "noEmit": true,
13 "skipLibCheck": true
14 },
15 "include": ["./src/main/js/**/*"],
16 "exclude": ["./src/main/js/xtext/**/*"]
17}
diff --git a/subprojects/language-web/webpack.config.js b/subprojects/language-web/webpack.config.js
new file mode 100644
index 00000000..801a705c
--- /dev/null
+++ b/subprojects/language-web/webpack.config.js
@@ -0,0 +1,232 @@
1const fs = require('fs');
2const path = require('path');
3
4const { DefinePlugin } = require('webpack');
5const HtmlWebpackPlugin = require('html-webpack-plugin');
6const HtmlWebpackInjectPreload = require('@principalstudio/html-webpack-inject-preload');
7const MiniCssExtractPlugin = require('mini-css-extract-plugin');
8const { SubresourceIntegrityPlugin } = require('webpack-subresource-integrity');
9
10const packageInfo = require('./package.json');
11
12const currentNodeEnv = process.env.NODE_ENV || 'development';
13const devMode = currentNodeEnv !== 'production';
14const outputPath = path.resolve(__dirname, 'build/webpack', currentNodeEnv);
15
16const portNumberOrElse = (envName, fallback) => {
17 const value = process.env[envName];
18 return value ? parseInt(value) : fallback;
19};
20const listenHost = process.env['LISTEN_HOST'] || 'localhost';
21const listenPort = portNumberOrElse('LISTEN_PORT', 1313);
22const apiHost = process.env['API_HOST'] || listenHost;
23const apiPort = portNumberOrElse('API_PORT', 1312);
24const publicHost = process.env['PUBLIC_HOST'] || listenHost;
25const publicPort = portNumberOrElse('PUBLIC_PORT', listenPort);
26
27const resolveSources = sources => path.resolve(__dirname, 'src', sources);
28const mainJsSources = resolveSources('main/js');
29const babelLoaderFilters = {
30 include: [mainJsSources],
31};
32const babelPresets = [
33 [
34 '@babel/preset-env',
35 {
36 targets: 'defaults',
37 },
38 ],
39 '@babel/preset-react',
40];
41const babelPlugins = [
42 '@babel/plugin-transform-runtime',
43]
44const magicCommentsLoader = {
45 loader: 'magic-comments-loader',
46 options: {
47 webpackChunkName: true,
48 }
49};
50
51module.exports = {
52 mode: devMode ? 'development' : 'production',
53 entry: './src/main/js',
54 output: {
55 path: outputPath,
56 publicPath: '/',
57 filename: devMode ? '[name].js' : '[name].[contenthash].js',
58 chunkFilename: devMode ? '[name].js' : '[name].[contenthash].js',
59 assetModuleFilename: devMode ? '[name].js' : '[name].[contenthash][ext]',
60 clean: true,
61 crossOriginLoading: 'anonymous',
62 },
63 module: {
64 rules: [
65 {
66 test: /\.jsx?$/i,
67 ...babelLoaderFilters,
68 use: [
69 {
70 loader: 'babel-loader',
71 options: {
72 presets: babelPresets,
73 plugins: [
74 [
75 '@babel/plugin-proposal-class-properties',
76 {
77 loose: false,
78 },
79 ...babelPlugins,
80 ],
81 ],
82 assumptions: {
83 'setPublicClassFields': false,
84 },
85 },
86 },
87 magicCommentsLoader,
88 ],
89 },
90 {
91 test: /.tsx?$/i,
92 ...babelLoaderFilters,
93 use: [
94 {
95 loader: 'babel-loader',
96 options: {
97 presets: [
98 ...babelPresets,
99 [
100 '@babel/preset-typescript',
101 {
102 isTSX: true,
103 allExtensions: true,
104 allowDeclareFields: true,
105 onlyRemoveTypeImports: true,
106 optimizeConstEnums: true,
107 },
108 ]
109 ],
110 plugins: babelPlugins,
111 },
112 },
113 magicCommentsLoader,
114 ],
115 },
116 {
117 test: /\.scss$/i,
118 use: [
119 devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
120 'css-loader',
121 {
122 loader: 'sass-loader',
123 options: {
124 implementation: require.resolve('sass'),
125 },
126 },
127 ],
128 },
129 {
130 test: /\.(gif|png|jpe?g|svg?)$/i,
131 use: [
132 {
133 loader: 'image-webpack-loader',
134 options: {
135 disable: true,
136 }
137 },
138 ],
139 type: 'asset',
140 },
141 {
142 test: /\.woff2?$/i,
143 type: 'asset/resource',
144 },
145 ],
146 },
147 resolve: {
148 modules: [
149 'node_modules',
150 mainJsSources,
151 ],
152 extensions: ['.js', '.jsx', '.ts', '.tsx'],
153 },
154 devtool: devMode ? 'inline-source-map' : 'source-map',
155 optimization: {
156 providedExports: !devMode,
157 sideEffects: devMode ? 'flag' : true,
158 splitChunks: {
159 chunks: 'all',
160 cacheGroups: {
161 defaultVendors: {
162 test: /[\\/]node_modules[\\/]/,
163 priority: -10,
164 reuseExistingChunk: true,
165 filename: devMode ? 'vendor.[id].js' : 'vendor.[contenthash].js',
166 },
167 default: {
168 minChunks: 2,
169 priority: -20,
170 reuseExistingChunk: true,
171 },
172 },
173 },
174 },
175 devServer: {
176 client: {
177 logging: 'info',
178 overlay: true,
179 progress: true,
180 webSocketURL: {
181 hostname: publicHost,
182 port: publicPort,
183 protocol: publicPort === 443 ? 'wss' : 'ws',
184 },
185 },
186 compress: true,
187 host: listenHost,
188 port: listenPort,
189 proxy: {
190 '/xtext-service': {
191 target: `${apiPort === 443 ? 'https' : 'http'}://${apiHost}:${apiPort}`,
192 ws: true,
193 },
194 },
195 },
196 plugins: [
197 new DefinePlugin({
198 'DEBUG': JSON.stringify(devMode),
199 'PACKAGE_NAME': JSON.stringify(packageInfo.name),
200 'PACKAGE_VERSION': JSON.stringify(packageInfo.version),
201 }),
202 new MiniCssExtractPlugin({
203 filename: '[name].[contenthash].css',
204 chunkFilename: '[name].[contenthash].css',
205 }),
206 new SubresourceIntegrityPlugin(),
207 new HtmlWebpackPlugin({
208 template: 'src/main/html/index.html',
209 minify: devMode ? false : {
210 collapseWhitespace: true,
211 removeComments: true,
212 removeOptionalTags: true,
213 removeRedundantAttributes: true,
214 removeScriptTypeAttributes: true,
215 removeStyleLinkTypeAttributes: true,
216 useShortDoctype: true,
217 },
218 }),
219 new HtmlWebpackInjectPreload({
220 files: [
221 {
222 match: /(roboto-latin-(400|500)-normal|jetbrains-mono-latin-variable).*\.woff2/,
223 attributes: {
224 as: 'font',
225 type: 'font/woff2',
226 crossorigin: 'anonymous',
227 },
228 },
229 ],
230 }),
231 ],
232};
diff --git a/subprojects/language/build.gradle b/subprojects/language/build.gradle
new file mode 100644
index 00000000..f7574ecc
--- /dev/null
+++ b/subprojects/language/build.gradle
@@ -0,0 +1,66 @@
1plugins {
2 id 'java-test-fixtures'
3 id 'refinery-java-library'
4 id 'refinery-mwe2'
5 id 'refinery-sonarqube'
6 id 'refinery-xtend'
7 id 'refinery-xtext-conventions'
8}
9
10dependencies {
11 api platform(libs.xtext.bom)
12 api libs.ecore
13 api libs.xtext.core
14 api libs.xtext.xbase
15 api project(':refinery-language-model')
16 testFixturesApi libs.xtext.testing
17 testFixturesApi testFixtures(project(':refinery-language-model'))
18 mwe2 libs.xtext.generator
19 mwe2 libs.xtext.generator.antlr
20}
21
22sourceSets {
23 testFixtures {
24 java.srcDirs += ['src/testFixtures/xtext-gen']
25 resources.srcDirs += ['src/testFixtures/xtext-gen']
26 }
27}
28
29tasks.named('jar') {
30 from(sourceSets.main.allSource) {
31 include '**/*.xtext'
32 }
33}
34
35def generateXtextLanguage = tasks.register('generateXtextLanguage', JavaExec) {
36 mainClass = 'org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher'
37 classpath = configurations.mwe2
38 inputs.file 'src/main/java/tools/refinery/language/GenerateProblem.mwe2'
39 inputs.file 'src/main/java/tools/refinery/language/Problem.xtext'
40 outputs.dir 'src/main/xtext-gen'
41 outputs.dir 'src/testFixtures/xtext-gen'
42 outputs.dir '../language-ide/src/main/xtext-gen'
43 outputs.dir '../language-web/src/main/xtext-gen'
44 args += 'src/main/java/tools/refinery/language/GenerateProblem.mwe2'
45 args += '-p'
46 args += "rootPath=/${projectDir}/.."
47}
48
49for (taskName in ['compileJava', 'processResources', 'generateXtext', 'generateEclipseSourceFolders']) {
50 tasks.named(taskName) {
51 dependsOn generateXtextLanguage
52 }
53}
54
55tasks.named('clean') {
56 delete 'src/main/xtext-gen'
57 delete 'src/testFixtures/xtext-gen'
58 delete '../language-ide/src/main/xtext-gen'
59 delete '../language-web/src/main/xtext-gen'
60}
61
62sonarqube.properties {
63 properties['sonar.exclusions'] += [
64 'src/testFixtures/xtext-gen/**',
65 ]
66}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/GenerateProblem.mwe2 b/subprojects/language/src/main/java/tools/refinery/language/GenerateProblem.mwe2
new file mode 100644
index 00000000..21ff456e
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/GenerateProblem.mwe2
@@ -0,0 +1,68 @@
1module tools.refinery.language.GenerateProblem
2
3import org.eclipse.xtext.xtext.generator.*
4import org.eclipse.xtext.xtext.generator.model.project.*
5
6var rootPath = '..'
7
8Workflow {
9 component = XtextGenerator {
10 configuration = {
11 project = StandardProjectConfig {
12 baseName = 'language'
13 rootPath = rootPath
14 runtimeTest = {
15 enabled = true
16 srcGen = 'src/testFixtures/xtext-gen'
17 }
18 genericIde = {
19 name = 'language-ide'
20 }
21 web = {
22 enabled = true
23 name = 'language-web'
24 }
25 mavenLayout = true
26 }
27 code = {
28 encoding = 'UTF-8'
29 lineDelimiter = '\n'
30 fileHeader = '/*\n * generated by Xtext \${version}\n */'
31 preferXtendStubs = false
32 }
33 }
34
35 language = StandardLanguage {
36 name = 'tools.refinery.language.Problem'
37 fileExtensions = 'problem'
38 referencedResource = 'platform:/resource/tools.refinery.refinery-language-model/model/problem.genmodel'
39 serializer = {
40 generateStub = false
41 }
42 formatter = {
43 generateStub = true
44 }
45 validator = {
46 generateDeprecationValidation = true
47 }
48 generator = {
49 generateStub = false
50 }
51 junitSupport = {
52 generateStub = false
53 skipXbaseTestingPackage = true
54 junitVersion = '5'
55 }
56 webSupport = {
57 // We only generate the {@code AbstractProblemWebModule},
58 // because we write our own integration code for CodeMirror 6.
59 framework = 'codemirror'
60 generateHtmlExample = false
61 generateJettyLauncher = false
62 generateJsHighlighting = false
63 generateServlet = false
64 generateWebXml = false
65 }
66 }
67 }
68}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
new file mode 100644
index 00000000..c94d40ab
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
@@ -0,0 +1,205 @@
1grammar tools.refinery.language.Problem with org.eclipse.xtext.common.Terminals
2
3import "http://www.eclipse.org/emf/2002/Ecore" as ecore
4import "https://refinery.tools/emf/2021/Problem"
5
6Problem:
7 ("problem" name=Identifier ".")?
8 statements+=Statement*;
9
10Statement:
11 ClassDeclaration | EnumDeclaration | PredicateDefinition | RuleDefinition | Assertion | NodeValueAssertion |
12 ScopeDeclaration |
13 IndividualDeclaration;
14
15ClassDeclaration:
16 abstract?="abstract"? "class"
17 name=Identifier
18 ("extends" superTypes+=[Relation|QualifiedName] ("," superTypes+=[Relation|QualifiedName])*)?
19 ("{" (referenceDeclarations+=ReferenceDeclaration ";"?)* "}" | ".");
20
21EnumDeclaration:
22 "enum"
23 name=Identifier
24 ("{" (literals+=EnumLiteral ("," literals+=EnumLiteral)* ("," | ";")?)? "}" | ".");
25
26EnumLiteral returns Node:
27 name=Identifier;
28
29ReferenceDeclaration:
30 (containment?="contains" | "refers")?
31 referenceType=[Relation|QualifiedName]
32 ("[" multiplicity=Multiplicity "]")?
33 name=Identifier
34 ("opposite" opposite=[ReferenceDeclaration|QualifiedName])?;
35
36enum PredicateKind:
37 DIRECT="direct";
38
39PredicateDefinition:
40 (error?="error" "pred"? | kind=PredicateKind? "pred")
41 name=Identifier
42 "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")"
43 ("<->" bodies+=Conjunction (";" bodies+=Conjunction)*)?
44 ".";
45
46enum RuleKind:
47 DIRECT="direct";
48
49RuleDefinition:
50 kind=RuleKind "rule"
51 name=Identifier
52 "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")"
53 (":" bodies+=Conjunction (";" bodies+=Conjunction)*
54 "~>" action=Action)?
55 ".";
56
57Parameter:
58 parameterType=[Relation|QualifiedName]? name=Identifier;
59
60Conjunction:
61 literals+=Literal ("," literals+=Literal)*;
62
63Action:
64 actionLiterals+=ActionLiteral ("," actionLiterals+=ActionLiteral)*;
65
66Literal:
67 Atom | ValueLiteral | NegativeLiteral;
68
69ValueLiteral:
70 atom=Atom
71 (refinement?=":" | "=")
72 values+=LogicConstant ("|" values+=LogicConstant)*;
73
74NegativeLiteral:
75 "!" atom=Atom;
76
77ActionLiteral:
78 ValueActionLiteral | DeleteActionLiteral | NewActionLiteral;
79
80ValueActionLiteral:
81 atom=Atom
82 (refinement?=":" | "=")
83 value=LogicValue;
84
85DeleteActionLiteral:
86 "delete" variableOrNode=[VariableOrNode|QualifiedName];
87
88NewActionLiteral:
89 "new" variable=NewVariable;
90
91NewVariable:
92 name=Identifier;
93
94Atom:
95 relation=[Relation|QualifiedName]
96 transitiveClosure?="+"?
97 "(" (arguments+=Argument ("," arguments+=Argument)*)? ")";
98
99LogicConstant:
100 value=LogicValue;
101
102Argument:
103 VariableOrNodeArgument | ConstantArgument;
104
105VariableOrNodeArgument:
106 variableOrNode=[VariableOrNode|QualifiedName];
107
108ConstantArgument:
109 constant=Constant;
110
111Assertion:
112 default?="default"?
113 (value=ShortLogicValue?
114 relation=[Relation|QualifiedName]
115 "(" (arguments+=AssertionArgument ("," arguments+=AssertionArgument)*)? ")"
116 | relation=[Relation|QualifiedName]
117 "(" (arguments+=AssertionArgument ("," arguments+=AssertionArgument)*)? ")"
118 ":" value=LogicValue)
119 ".";
120
121AssertionArgument:
122 NodeAssertionArgument | WildcardAssertionArgument | ConstantAssertionArgument;
123
124NodeAssertionArgument:
125 node=[Node|QualifiedName];
126
127WildcardAssertionArgument:
128 {WildcardAssertionArgument} "*";
129
130ConstantAssertionArgument:
131 constant=Constant;
132
133enum LogicValue:
134 TRUE="true" | FALSE="false" | UNKNOWN="unknown" | ERROR="error";
135
136enum ShortLogicValue returns LogicValue:
137 FALSE="!" | UNKNOWN="?";
138
139NodeValueAssertion:
140 node=[Node|QualifiedName] ":" value=Constant ".";
141
142Constant:
143 RealConstant | IntConstant | StringConstant;
144
145IntConstant:
146 intValue=Integer;
147
148RealConstant:
149 realValue=Real;
150
151StringConstant:
152 stringValue=STRING;
153
154ScopeDeclaration:
155 "scope" typeScopes+=TypeScope ("," typeScopes+=TypeScope)* ".";
156
157TypeScope:
158 targetType=[ClassDeclaration|QualifiedName]
159 (increment?="+=" | "=")
160 multiplicity=DefiniteMultiplicity;
161
162Multiplicity:
163 UnboundedMultiplicity | DefiniteMultiplicity;
164
165DefiniteMultiplicity returns Multiplicity:
166 RangeMultiplicity | ExactMultiplicity;
167
168UnboundedMultiplicity:
169 {UnboundedMultiplicity};
170
171RangeMultiplicity:
172 lowerBound=INT ".." upperBound=UpperBound;
173
174ExactMultiplicity:
175 exactValue=INT;
176
177IndividualDeclaration:
178 "indiv" nodes+=EnumLiteral ("," nodes+=EnumLiteral)* ".";
179
180UpperBound returns ecore::EInt:
181 INT | "*";
182
183QualifiedName hidden():
184 Identifier ("::" Identifier)*;
185
186Identifier:
187 ID | "true" | "false" | "unknown" | "error" | "class" | "abstract" | "extends" | "enum" | "pred" |
188 "indiv" | "problem" | "new" | "delete" | "direct" | "rule";
189
190Integer returns ecore::EInt hidden():
191 "-"? INT;
192
193Real returns ecore::EDouble:
194 "-"? (EXPONENTIAL | INT "." (INT | EXPONENTIAL));
195
196@Override
197terminal ID:
198 ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*;
199
200terminal EXPONENTIAL:
201 INT ("e" | "E") ("+" | "-")? INT;
202
203@Override
204terminal SL_COMMENT:
205 ('%' | '//') !('\n' | '\r')* ('\r'? '\n')?;
diff --git a/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java b/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java
new file mode 100644
index 00000000..dd7731b4
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java
@@ -0,0 +1,84 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language;
5
6import org.eclipse.xtext.conversion.IValueConverterService;
7import org.eclipse.xtext.naming.IQualifiedNameConverter;
8import org.eclipse.xtext.resource.DerivedStateAwareResource;
9import org.eclipse.xtext.resource.DerivedStateAwareResourceDescriptionManager;
10import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy;
11import org.eclipse.xtext.resource.IDerivedStateComputer;
12import org.eclipse.xtext.resource.ILocationInFileProvider;
13import org.eclipse.xtext.resource.IResourceDescription;
14import org.eclipse.xtext.resource.XtextResource;
15import org.eclipse.xtext.scoping.IGlobalScopeProvider;
16import org.eclipse.xtext.scoping.IScopeProvider;
17import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
18import org.eclipse.xtext.validation.IResourceValidator;
19import org.eclipse.xtext.xbase.annotations.validation.DerivedStateAwareResourceValidator;
20
21import com.google.inject.Binder;
22import com.google.inject.name.Names;
23
24import tools.refinery.language.conversion.ProblemValueConverterService;
25import tools.refinery.language.naming.ProblemQualifiedNameConverter;
26import tools.refinery.language.resource.ProblemDerivedStateComputer;
27import tools.refinery.language.resource.ProblemLocationInFileProvider;
28import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
29import tools.refinery.language.scoping.ProblemGlobalScopeProvider;
30import tools.refinery.language.scoping.ProblemLocalScopeProvider;
31
32/**
33 * Use this class to register components to be used at runtime / without the
34 * Equinox extension registry.
35 */
36public class ProblemRuntimeModule extends AbstractProblemRuntimeModule {
37 public Class<? extends IQualifiedNameConverter> bindIQualifiedNameConverter() {
38 return ProblemQualifiedNameConverter.class;
39 }
40
41 public Class<? extends IDefaultResourceDescriptionStrategy> bindIDefaultResourceDescriptionStrategy() {
42 return ProblemResourceDescriptionStrategy.class;
43 }
44
45 @Override
46 public Class<? extends IValueConverterService> bindIValueConverterService() {
47 return ProblemValueConverterService.class;
48 }
49
50 @Override
51 public Class<? extends IGlobalScopeProvider> bindIGlobalScopeProvider() {
52 return ProblemGlobalScopeProvider.class;
53 }
54
55 @Override
56 public void configureIScopeProviderDelegate(Binder binder) {
57 binder.bind(IScopeProvider.class).annotatedWith(Names.named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE))
58 .to(ProblemLocalScopeProvider.class);
59 }
60
61 @Override
62 public Class<? extends XtextResource> bindXtextResource() {
63 return DerivedStateAwareResource.class;
64 }
65
66 // Method name follows Xtext convention.
67 @SuppressWarnings("squid:S100")
68 public Class<? extends IResourceDescription.Manager> bindIResourceDescription$Manager() {
69 return DerivedStateAwareResourceDescriptionManager.class;
70 }
71
72 public Class<? extends IResourceValidator> bindIResourceValidator() {
73 return DerivedStateAwareResourceValidator.class;
74 }
75
76 public Class<? extends IDerivedStateComputer> bindIDerivedStateComputer() {
77 return ProblemDerivedStateComputer.class;
78 }
79
80 @Override
81 public Class<? extends ILocationInFileProvider> bindILocationInFileProvider() {
82 return ProblemLocationInFileProvider.class;
83 }
84}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/ProblemStandaloneSetup.java b/subprojects/language/src/main/java/tools/refinery/language/ProblemStandaloneSetup.java
new file mode 100644
index 00000000..d753a119
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/ProblemStandaloneSetup.java
@@ -0,0 +1,44 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language;
5
6import org.eclipse.emf.ecore.resource.Resource;
7import org.eclipse.xtext.resource.IResourceFactory;
8import org.eclipse.xtext.resource.IResourceServiceProvider;
9
10import com.google.inject.Guice;
11import com.google.inject.Injector;
12
13import tools.refinery.language.model.ProblemEMFSetup;
14
15/**
16 * Initialization support for running Xtext languages without Equinox extension
17 * registry.
18 */
19public class ProblemStandaloneSetup extends ProblemStandaloneSetupGenerated {
20
21 public static void doSetup() {
22 new ProblemStandaloneSetup().createInjectorAndDoEMFRegistration();
23 }
24
25 @Override
26 public Injector createInjectorAndDoEMFRegistration() {
27 ProblemEMFSetup.doEMFRegistration();
28 var xmiInjector = createXmiInjector();
29 registerXmiInjector(xmiInjector);
30 return super.createInjectorAndDoEMFRegistration();
31 }
32
33 protected Injector createXmiInjector() {
34 return Guice.createInjector(new ProblemXmiRuntimeModule());
35 }
36
37 protected void registerXmiInjector(Injector injector) {
38 IResourceFactory resourceFactory = injector.getInstance(IResourceFactory.class);
39 IResourceServiceProvider serviceProvider = injector.getInstance(IResourceServiceProvider.class);
40
41 Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put(ProblemEMFSetup.XMI_RESOURCE_EXTENSION, resourceFactory);
42 IResourceServiceProvider.Registry.INSTANCE.getExtensionToFactoryMap().put(ProblemEMFSetup.XMI_RESOURCE_EXTENSION, serviceProvider);
43 }
44}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/ProblemXmiRuntimeModule.java b/subprojects/language/src/main/java/tools/refinery/language/ProblemXmiRuntimeModule.java
new file mode 100644
index 00000000..03a33bee
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/ProblemXmiRuntimeModule.java
@@ -0,0 +1,35 @@
1package tools.refinery.language;
2
3import org.eclipse.xtext.naming.IQualifiedNameConverter;
4import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy;
5import org.eclipse.xtext.resource.IResourceFactory;
6import org.eclipse.xtext.resource.generic.AbstractGenericResourceRuntimeModule;
7
8import tools.refinery.language.model.ProblemEMFSetup;
9import tools.refinery.language.naming.ProblemQualifiedNameConverter;
10import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
11import tools.refinery.language.resource.ProblemXmiResourceFactory;
12
13public class ProblemXmiRuntimeModule extends AbstractGenericResourceRuntimeModule {
14 @Override
15 protected String getLanguageName() {
16 return "tools.refinery.language.ProblemXmi";
17 }
18
19 @Override
20 protected String getFileExtensions() {
21 return ProblemEMFSetup.XMI_RESOURCE_EXTENSION;
22 }
23
24 public Class<? extends IResourceFactory> bindIResourceFactory() {
25 return ProblemXmiResourceFactory.class;
26 }
27
28 public Class<? extends IQualifiedNameConverter> bindIQualifiedNameConverter() {
29 return ProblemQualifiedNameConverter.class;
30 }
31
32 public Class<? extends IDefaultResourceDescriptionStrategy> bindIDefaultResourceDescriptionStrategy() {
33 return ProblemResourceDescriptionStrategy.class;
34 }
35}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/conversion/ProblemValueConverterService.java b/subprojects/language/src/main/java/tools/refinery/language/conversion/ProblemValueConverterService.java
new file mode 100644
index 00000000..508688ed
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/conversion/ProblemValueConverterService.java
@@ -0,0 +1,19 @@
1package tools.refinery.language.conversion;
2
3import org.eclipse.xtext.common.services.DefaultTerminalConverters;
4import org.eclipse.xtext.conversion.IValueConverter;
5import org.eclipse.xtext.conversion.ValueConverter;
6
7import com.google.inject.Inject;
8
9public class ProblemValueConverterService extends DefaultTerminalConverters {
10 @Inject
11 private UpperBoundValueConverter upperBoundValueConverter;
12
13 @ValueConverter(rule = "UpperBound")
14 // Method name follows Xtext convention.
15 @SuppressWarnings("squid:S100")
16 public IValueConverter<Integer> UpperBound() {
17 return upperBoundValueConverter;
18 }
19}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/conversion/UpperBoundValueConverter.java b/subprojects/language/src/main/java/tools/refinery/language/conversion/UpperBoundValueConverter.java
new file mode 100644
index 00000000..be0d15ad
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/conversion/UpperBoundValueConverter.java
@@ -0,0 +1,35 @@
1package tools.refinery.language.conversion;
2
3import org.eclipse.xtext.conversion.ValueConverterException;
4import org.eclipse.xtext.conversion.impl.AbstractValueConverter;
5import org.eclipse.xtext.conversion.impl.INTValueConverter;
6import org.eclipse.xtext.nodemodel.INode;
7
8import com.google.inject.Inject;
9import com.google.inject.Singleton;
10
11@Singleton
12public class UpperBoundValueConverter extends AbstractValueConverter<Integer> {
13 public static final String INFINITY = "*";
14
15 @Inject
16 private INTValueConverter intValueConverter;
17
18 @Override
19 public Integer toValue(String string, INode node) throws ValueConverterException {
20 if (INFINITY.equals(string)) {
21 return -1;
22 } else {
23 return intValueConverter.toValue(string, node);
24 }
25 }
26
27 @Override
28 public String toString(Integer value) throws ValueConverterException {
29 if (value < 0) {
30 return INFINITY;
31 } else {
32 return intValueConverter.toString(value);
33 }
34 }
35}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java b/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java
new file mode 100644
index 00000000..903347f7
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java
@@ -0,0 +1,183 @@
1/*
2 * generated by Xtext 2.26.0.M2
3 */
4package tools.refinery.language.formatting2;
5
6import org.eclipse.emf.ecore.EObject;
7import org.eclipse.xtext.formatting2.AbstractJavaFormatter;
8import org.eclipse.xtext.formatting2.IFormattableDocument;
9import org.eclipse.xtext.formatting2.IHiddenRegionFormatter;
10import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionsFinder;
11import org.eclipse.xtext.formatting2.regionaccess.ISequentialRegion;
12import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
13
14import tools.refinery.language.model.problem.Assertion;
15import tools.refinery.language.model.problem.Atom;
16import tools.refinery.language.model.problem.ClassDeclaration;
17import tools.refinery.language.model.problem.Conjunction;
18import tools.refinery.language.model.problem.IndividualDeclaration;
19import tools.refinery.language.model.problem.NegativeLiteral;
20import tools.refinery.language.model.problem.Parameter;
21import tools.refinery.language.model.problem.PredicateDefinition;
22import tools.refinery.language.model.problem.Problem;
23import tools.refinery.language.model.problem.ProblemPackage;
24
25public class ProblemFormatter extends AbstractJavaFormatter {
26
27 protected void format(Problem problem, IFormattableDocument doc) {
28 doc.prepend(problem, this::noSpace);
29 var region = regionFor(problem);
30 doc.append(region.keyword("problem"), this::oneSpace);
31 doc.prepend(region.keyword("."), this::noSpace);
32 appendNewLines(doc, region.keyword("."), this::twoNewLines);
33 for (var statement : problem.getStatements()) {
34 doc.format(statement);
35 }
36 }
37
38 protected void format(Assertion assertion, IFormattableDocument doc) {
39 surroundNewLines(doc, assertion, this::singleNewLine);
40 var region = regionFor(assertion);
41 doc.append(region.feature(ProblemPackage.Literals.ASSERTION__DEFAULT), this::oneSpace);
42 doc.append(region.feature(ProblemPackage.Literals.ASSERTION__VALUE), this::noSpace);
43 doc.append(region.feature(ProblemPackage.Literals.ASSERTION__RELATION), this::noSpace);
44 formatParenthesizedList(region, doc);
45 doc.prepend(region.keyword(":"), this::noSpace);
46 doc.append(region.keyword(":"), this::oneSpace);
47 doc.prepend(region.keyword("."), this::noSpace);
48 for (var argument : assertion.getArguments()) {
49 doc.format(argument);
50 }
51 }
52
53 protected void format(ClassDeclaration classDeclaration, IFormattableDocument doc) {
54 surroundNewLines(doc, classDeclaration, this::twoNewLines);
55 var region = regionFor(classDeclaration);
56 doc.append(region.feature(ProblemPackage.Literals.CLASS_DECLARATION__ABSTRACT), this::oneSpace);
57 doc.append(region.keyword("class"), this::oneSpace);
58 doc.surround(region.keyword("extends"), this::oneSpace);
59 formatList(region, ",", doc);
60 doc.prepend(region.keyword("{"), this::oneSpace);
61 doc.append(region.keyword("{"), it -> it.setNewLines(1, 1, 2));
62 doc.prepend(region.keyword("}"), it -> it.setNewLines(1, 1, 2));
63 doc.prepend(region.keyword("."), this::noSpace);
64 for (var referenceDeclaration : classDeclaration.getReferenceDeclarations()) {
65 doc.format(referenceDeclaration);
66 }
67 }
68
69 protected void format(PredicateDefinition predicateDefinition, IFormattableDocument doc) {
70 surroundNewLines(doc, predicateDefinition, this::twoNewLines);
71 var region = regionFor(predicateDefinition);
72 doc.append(region.feature(ProblemPackage.Literals.PREDICATE_DEFINITION__KIND), this::oneSpace);
73 doc.append(region.keyword("pred"), this::oneSpace);
74 doc.append(region.feature(ProblemPackage.Literals.NAMED_ELEMENT__NAME), this::noSpace);
75 formatParenthesizedList(region, doc);
76 doc.surround(region.keyword("<->"), this::oneSpace);
77 formatList(region, ";", doc);
78 doc.prepend(region.keyword("."), this::noSpace);
79 for (var parameter : predicateDefinition.getParameters()) {
80 doc.format(parameter);
81 }
82 for (var body : predicateDefinition.getBodies()) {
83 doc.format(body);
84 }
85 }
86
87 protected void format(Parameter parameter, IFormattableDocument doc) {
88 doc.append(regionFor(parameter).feature(ProblemPackage.Literals.PARAMETER__PARAMETER_TYPE), this::oneSpace);
89 }
90
91 protected void format(Conjunction conjunction, IFormattableDocument doc) {
92 var region = regionFor(conjunction);
93 formatList(region, ",", doc);
94 for (var literal : conjunction.getLiterals()) {
95 doc.format(literal);
96 }
97 }
98
99 protected void format(NegativeLiteral literal, IFormattableDocument doc) {
100 var region = regionFor(literal);
101 doc.append(region.keyword("!"), this::noSpace);
102 doc.format(literal.getAtom());
103 }
104
105 protected void format(Atom atom, IFormattableDocument doc) {
106 var region = regionFor(atom);
107 doc.append(region.feature(ProblemPackage.Literals.ATOM__RELATION), this::noSpace);
108 doc.append(region.feature(ProblemPackage.Literals.ATOM__TRANSITIVE_CLOSURE), this::noSpace);
109 formatParenthesizedList(region, doc);
110 for (var argument : atom.getArguments()) {
111 doc.format(argument);
112 }
113 }
114
115 protected void format(IndividualDeclaration individualDeclaration, IFormattableDocument doc) {
116 surroundNewLines(doc, individualDeclaration, this::singleNewLine);
117 var region = regionFor(individualDeclaration);
118 doc.append(region.keyword("indiv"), this::oneSpace);
119 formatList(region, ",", doc);
120 doc.prepend(region.keyword("."), this::noSpace);
121 }
122
123 protected void formatParenthesizedList(ISemanticRegionsFinder region, IFormattableDocument doc) {
124 doc.append(region.keyword("("), this::noSpace);
125 doc.prepend(region.keyword(")"), this::noSpace);
126 formatList(region, ",", doc);
127 }
128
129 protected void formatList(ISemanticRegionsFinder region, String separator, IFormattableDocument doc) {
130 for (var comma : region.keywords(separator)) {
131 doc.prepend(comma, this::noSpace);
132 doc.append(comma, this::oneSpace);
133 }
134 }
135
136 protected void singleNewLine(IHiddenRegionFormatter it) {
137 it.setNewLines(1, 1, 2);
138 }
139
140 protected void twoNewLines(IHiddenRegionFormatter it) {
141 it.highPriority();
142 it.setNewLines(2);
143 }
144
145 protected void surroundNewLines(IFormattableDocument doc, EObject eObject,
146 Procedure1<? super IHiddenRegionFormatter> init) {
147 var region = doc.getRequest().getTextRegionAccess().regionForEObject(eObject);
148 preprendNewLines(doc, region, init);
149 appendNewLines(doc, region, init);
150 }
151
152 protected void preprendNewLines(IFormattableDocument doc, ISequentialRegion region,
153 Procedure1<? super IHiddenRegionFormatter> init) {
154 if (region == null) {
155 return;
156 }
157 var previousHiddenRegion = region.getPreviousHiddenRegion();
158 if (previousHiddenRegion == null) {
159 return;
160 }
161 if (previousHiddenRegion.getPreviousSequentialRegion() == null) {
162 doc.set(previousHiddenRegion, it -> it.setNewLines(0));
163 } else {
164 doc.set(previousHiddenRegion, init);
165 }
166 }
167
168 protected void appendNewLines(IFormattableDocument doc, ISequentialRegion region,
169 Procedure1<? super IHiddenRegionFormatter> init) {
170 if (region == null) {
171 return;
172 }
173 var nextHiddenRegion = region.getNextHiddenRegion();
174 if (nextHiddenRegion == null) {
175 return;
176 }
177 if (nextHiddenRegion.getNextSequentialRegion() == null) {
178 doc.set(nextHiddenRegion, it -> it.setNewLines(1));
179 } else {
180 doc.set(nextHiddenRegion, init);
181 }
182 }
183}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java b/subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java
new file mode 100644
index 00000000..e959be74
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java
@@ -0,0 +1,25 @@
1package tools.refinery.language.naming;
2
3import java.util.regex.Pattern;
4
5public final class NamingUtil {
6 private static final String SINGLETON_VARIABLE_PREFIX = "_";
7
8 private static final Pattern ID_REGEX = Pattern.compile("[_a-zA-Z][_0-9a-zA-Z]*");
9
10 private NamingUtil() {
11 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
12 }
13
14 public static boolean isNullOrEmpty(String name) {
15 return name == null || name.isEmpty();
16 }
17
18 public static boolean isSingletonVariableName(String name) {
19 return name != null && name.startsWith(SINGLETON_VARIABLE_PREFIX);
20 }
21
22 public static boolean isValidId(String name) {
23 return name != null && ID_REGEX.matcher(name).matches();
24 }
25}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java
new file mode 100644
index 00000000..5453906f
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java
@@ -0,0 +1,15 @@
1package tools.refinery.language.naming;
2
3import org.eclipse.xtext.naming.IQualifiedNameConverter;
4
5import com.google.inject.Singleton;
6
7@Singleton
8public class ProblemQualifiedNameConverter extends IQualifiedNameConverter.DefaultImpl {
9 public static final String DELIMITER = "::";
10
11 @Override
12 public String getDelimiter() {
13 return DELIMITER;
14 }
15}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java b/subprojects/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java
new file mode 100644
index 00000000..bb1226c4
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java
@@ -0,0 +1,194 @@
1package tools.refinery.language.resource;
2
3import java.util.HashSet;
4import java.util.List;
5import java.util.Set;
6
7import org.eclipse.xtext.linking.impl.LinkingHelper;
8import org.eclipse.xtext.naming.IQualifiedNameConverter;
9import org.eclipse.xtext.nodemodel.INode;
10import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
11import org.eclipse.xtext.scoping.IScope;
12import org.eclipse.xtext.scoping.IScopeProvider;
13import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
14
15import com.google.inject.Inject;
16import com.google.inject.Singleton;
17import com.google.inject.name.Named;
18
19import tools.refinery.language.model.problem.Argument;
20import tools.refinery.language.model.problem.Atom;
21import tools.refinery.language.model.problem.Conjunction;
22import tools.refinery.language.model.problem.ExistentialQuantifier;
23import tools.refinery.language.model.problem.ImplicitVariable;
24import tools.refinery.language.model.problem.Literal;
25import tools.refinery.language.model.problem.NegativeLiteral;
26import tools.refinery.language.model.problem.Parameter;
27import tools.refinery.language.model.problem.ParametricDefinition;
28import tools.refinery.language.model.problem.Problem;
29import tools.refinery.language.model.problem.ProblemFactory;
30import tools.refinery.language.model.problem.ProblemPackage;
31import tools.refinery.language.model.problem.Statement;
32import tools.refinery.language.model.problem.ValueLiteral;
33import tools.refinery.language.model.problem.VariableOrNodeArgument;
34import tools.refinery.language.naming.NamingUtil;
35
36@Singleton
37public class DerivedVariableComputer {
38 @Inject
39 private LinkingHelper linkingHelper;
40
41 @Inject
42 private IQualifiedNameConverter qualifiedNameConverter;
43
44 @Inject
45 @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE)
46 private IScopeProvider scopeProvider;
47
48 public void installDerivedVariables(Problem problem, Set<String> nodeNames) {
49 for (Statement statement : problem.getStatements()) {
50 if (statement instanceof ParametricDefinition definition) {
51 installDerivedParametricDefinitionState(definition, nodeNames);
52 }
53 }
54 }
55
56 protected void installDerivedParametricDefinitionState(ParametricDefinition definition, Set<String> nodeNames) {
57 Set<String> knownVariables = new HashSet<>();
58 knownVariables.addAll(nodeNames);
59 for (Parameter parameter : definition.getParameters()) {
60 String name = parameter.getName();
61 if (name != null) {
62 knownVariables.add(name);
63 }
64 }
65 for (Conjunction body : definition.getBodies()) {
66 installDeriveConjunctionState(body, knownVariables);
67 }
68 }
69
70 protected void installDeriveConjunctionState(Conjunction conjunction, Set<String> knownVariables) {
71 Set<String> newVariables = new HashSet<>();
72 for (Literal literal : conjunction.getLiterals()) {
73 if (literal instanceof Atom atom) {
74 createSigletonVariablesAndCollectVariables(atom, knownVariables, newVariables);
75 } else
76 if (literal instanceof ValueLiteral valueLiteral) {
77 createSigletonVariablesAndCollectVariables(valueLiteral.getAtom(), knownVariables, newVariables);
78 }
79 }
80 createVariables(conjunction, newVariables);
81 newVariables.addAll(knownVariables);
82 for (Literal literal : conjunction.getLiterals()) {
83 if (literal instanceof NegativeLiteral negativeLiteral) {
84 installDeriveNegativeLiteralState(negativeLiteral, newVariables);
85 }
86 }
87 }
88
89 protected void installDeriveNegativeLiteralState(NegativeLiteral negativeLiteral, Set<String> knownVariables) {
90 Set<String> newVariables = new HashSet<>();
91 createSigletonVariablesAndCollectVariables(negativeLiteral.getAtom(), knownVariables, newVariables);
92 createVariables(negativeLiteral, newVariables);
93 }
94
95 protected void createSigletonVariablesAndCollectVariables(Atom atom, Set<String> knownVariables,
96 Set<String> newVariables) {
97 for (Argument argument : atom.getArguments()) {
98 if (argument instanceof VariableOrNodeArgument variableOrNodeArgument) {
99 IScope scope = scopeProvider.getScope(variableOrNodeArgument,
100 ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE);
101 List<INode> nodes = NodeModelUtils.findNodesForFeature(variableOrNodeArgument,
102 ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE);
103 for (INode node : nodes) {
104 var variableName = linkingHelper.getCrossRefNodeAsString(node, true);
105 var created = tryCreateVariableForArgument(variableOrNodeArgument, variableName, scope,
106 knownVariables, newVariables);
107 if (created) {
108 break;
109 }
110 }
111 }
112 }
113 }
114
115 protected boolean tryCreateVariableForArgument(VariableOrNodeArgument variableOrNodeArgument, String variableName,
116 IScope scope, Set<String> knownVariables, Set<String> newVariables) {
117 if (!NamingUtil.isValidId(variableName)) {
118 return false;
119 }
120 var qualifiedName = qualifiedNameConverter.toQualifiedName(variableName);
121 if (scope.getSingleElement(qualifiedName) != null) {
122 return false;
123 }
124 if (NamingUtil.isSingletonVariableName(variableName)) {
125 createSingletonVariable(variableOrNodeArgument, variableName);
126 return true;
127 }
128 if (!knownVariables.contains(variableName)) {
129 newVariables.add(variableName);
130 return true;
131 }
132 return false;
133 }
134
135 protected void createVariables(ExistentialQuantifier quantifier, Set<String> newVariables) {
136 for (String variableName : newVariables) {
137 createVariable(quantifier, variableName);
138 }
139 }
140
141 protected void createVariable(ExistentialQuantifier quantifier, String variableName) {
142 if (NamingUtil.isValidId(variableName)) {
143 ImplicitVariable variable = createNamedVariable(variableName);
144 quantifier.getImplicitVariables().add(variable);
145 }
146 }
147
148 protected void createSingletonVariable(VariableOrNodeArgument argument, String variableName) {
149 if (NamingUtil.isValidId(variableName)) {
150 ImplicitVariable variable = createNamedVariable(variableName);
151 argument.setSingletonVariable(variable);
152 }
153 }
154
155 protected ImplicitVariable createNamedVariable(String variableName) {
156 var variable = ProblemFactory.eINSTANCE.createImplicitVariable();
157 variable.setName(variableName);
158 return variable;
159 }
160
161 public void discardDerivedVariables(Problem problem) {
162 for (Statement statement : problem.getStatements()) {
163 if (statement instanceof ParametricDefinition parametricDefinition) {
164 discardParametricDefinitionState(parametricDefinition);
165 }
166 }
167 }
168
169 protected void discardParametricDefinitionState(ParametricDefinition definition) {
170 for (Conjunction body : definition.getBodies()) {
171 body.getImplicitVariables().clear();
172 for (Literal literal : body.getLiterals()) {
173 if (literal instanceof Atom atom) {
174 discardDerivedAtomState(atom);
175 }
176 if (literal instanceof NegativeLiteral negativeLiteral) {
177 negativeLiteral.getImplicitVariables().clear();
178 discardDerivedAtomState(negativeLiteral.getAtom());
179 }
180 }
181 }
182 }
183
184 protected void discardDerivedAtomState(Atom atom) {
185 if (atom == null) {
186 return;
187 }
188 for (Argument argument : atom.getArguments()) {
189 if (argument instanceof VariableOrNodeArgument variableOrNodeArgument) {
190 variableOrNodeArgument.setSingletonVariable(null);
191 }
192 }
193 }
194}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java b/subprojects/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java
new file mode 100644
index 00000000..99bf9b64
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java
@@ -0,0 +1,88 @@
1package tools.refinery.language.resource;
2
3import java.util.List;
4import java.util.Set;
5
6import org.eclipse.emf.ecore.EObject;
7import org.eclipse.emf.ecore.EStructuralFeature;
8import org.eclipse.xtext.linking.impl.LinkingHelper;
9import org.eclipse.xtext.naming.IQualifiedNameConverter;
10import org.eclipse.xtext.nodemodel.INode;
11import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
12import org.eclipse.xtext.scoping.IScope;
13import org.eclipse.xtext.scoping.IScopeProvider;
14import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
15
16import com.google.common.collect.ImmutableSet;
17import com.google.inject.Inject;
18import com.google.inject.name.Named;
19
20import tools.refinery.language.model.problem.Assertion;
21import tools.refinery.language.model.problem.AssertionArgument;
22import tools.refinery.language.model.problem.NodeAssertionArgument;
23import tools.refinery.language.model.problem.NodeValueAssertion;
24import tools.refinery.language.model.problem.Problem;
25import tools.refinery.language.model.problem.ProblemPackage;
26import tools.refinery.language.model.problem.Statement;
27import tools.refinery.language.naming.NamingUtil;
28
29public class NodeNameCollector {
30 @Inject
31 private LinkingHelper linkingHelper;
32
33 @Inject
34 private IQualifiedNameConverter qualifiedNameConverter;
35
36 @Inject
37 @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE)
38 private IScopeProvider scopeProvider;
39
40 private final ImmutableSet.Builder<String> nodeNames = ImmutableSet.builder();
41
42 private IScope nodeScope;
43
44 public Set<String> getNodeNames() {
45 return nodeNames.build();
46 }
47
48 public void collectNodeNames(Problem problem) {
49 nodeScope = scopeProvider.getScope(problem, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE);
50 for (Statement statement : problem.getStatements()) {
51 collectStatementNodeNames(statement);
52 }
53 }
54
55 protected void collectStatementNodeNames(Statement statement) {
56 if (statement instanceof Assertion assertion) {
57 collectAssertionNodeNames(assertion);
58 } else if (statement instanceof NodeValueAssertion nodeValueAssertion) {
59 collectNodeValueAssertionNodeNames(nodeValueAssertion);
60 }
61 }
62
63 protected void collectAssertionNodeNames(Assertion assertion) {
64 for (AssertionArgument argument : assertion.getArguments()) {
65 if (argument instanceof NodeAssertionArgument) {
66 collectNodeNames(argument, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE);
67 }
68 }
69 }
70
71 protected void collectNodeValueAssertionNodeNames(NodeValueAssertion nodeValueAssertion) {
72 collectNodeNames(nodeValueAssertion, ProblemPackage.Literals.NODE_VALUE_ASSERTION__NODE);
73 }
74
75 private void collectNodeNames(EObject eObject, EStructuralFeature feature) {
76 List<INode> nodes = NodeModelUtils.findNodesForFeature(eObject, feature);
77 for (INode node : nodes) {
78 var nodeName = linkingHelper.getCrossRefNodeAsString(node, true);
79 if (!NamingUtil.isValidId(nodeName)) {
80 continue;
81 }
82 var qualifiedName = qualifiedNameConverter.toQualifiedName(nodeName);
83 if (nodeScope.getSingleElement(qualifiedName) == null) {
84 nodeNames.add(nodeName);
85 }
86 }
87 }
88} \ No newline at end of file
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java
new file mode 100644
index 00000000..275feca3
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java
@@ -0,0 +1,163 @@
1package tools.refinery.language.resource;
2
3import java.util.Collection;
4import java.util.HashMap;
5import java.util.HashSet;
6import java.util.List;
7import java.util.Map;
8import java.util.Set;
9import java.util.function.Function;
10
11import org.eclipse.emf.common.notify.impl.AdapterImpl;
12import org.eclipse.emf.ecore.EObject;
13import org.eclipse.emf.ecore.resource.Resource;
14import org.eclipse.emf.ecore.util.EcoreUtil;
15import org.eclipse.xtext.Constants;
16import org.eclipse.xtext.resource.DerivedStateAwareResource;
17import org.eclipse.xtext.resource.IDerivedStateComputer;
18import org.eclipse.xtext.resource.XtextResource;
19import org.eclipse.xtext.scoping.IScopeProvider;
20import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
21
22import com.google.inject.Inject;
23import com.google.inject.Provider;
24import com.google.inject.Singleton;
25import com.google.inject.name.Named;
26
27import tools.refinery.language.model.problem.ClassDeclaration;
28import tools.refinery.language.model.problem.Node;
29import tools.refinery.language.model.problem.Problem;
30import tools.refinery.language.model.problem.ProblemFactory;
31import tools.refinery.language.model.problem.Statement;
32
33@Singleton
34public class ProblemDerivedStateComputer implements IDerivedStateComputer {
35 public static final String NEW_NODE = "new";
36
37 @Inject
38 @Named(Constants.LANGUAGE_NAME)
39 private String languageName;
40
41 @Inject
42 @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE)
43 private IScopeProvider scopeProvider;
44
45 @Inject
46 private Provider<NodeNameCollector> nodeNameCollectorProvider;
47
48 @Inject
49 private DerivedVariableComputer derivedVariableComputer;
50
51 @Override
52 public void installDerivedState(DerivedStateAwareResource resource, boolean preLinkingPhase) {
53 var problem = getProblem(resource);
54 if (problem != null) {
55 var adapter = getOrInstallAdapter(resource);
56 installDerivedProblemState(problem, adapter, preLinkingPhase);
57 }
58 }
59
60 protected Problem getProblem(Resource resource) {
61 List<EObject> contents = resource.getContents();
62 if (contents.isEmpty()) {
63 return null;
64 }
65 EObject object = contents.get(0);
66 if (object instanceof Problem problem) {
67 return problem;
68 }
69 return null;
70 }
71
72 protected void installDerivedProblemState(Problem problem, Adapter adapter, boolean preLinkingPhase) {
73 installNewNodes(problem, adapter);
74 if (preLinkingPhase) {
75 return;
76 }
77 Set<String> nodeNames = installDerivedNodes(problem);
78 derivedVariableComputer.installDerivedVariables(problem, nodeNames);
79 }
80
81 protected void installNewNodes(Problem problem, Adapter adapter) {
82 for (Statement statement : problem.getStatements()) {
83 if (statement instanceof ClassDeclaration declaration && !declaration.isAbstract()
84 && declaration.getNewNode() == null) {
85 var newNode = adapter.createNodeIfAbsent(declaration, key -> createNode(NEW_NODE));
86 declaration.setNewNode(newNode);
87 }
88 }
89 }
90
91 protected Set<String> installDerivedNodes(Problem problem) {
92 var collector = nodeNameCollectorProvider.get();
93 collector.collectNodeNames(problem);
94 Set<String> nodeNames = collector.getNodeNames();
95 List<Node> grapNodes = problem.getNodes();
96 for (String nodeName : nodeNames) {
97 var graphNode = createNode(nodeName);
98 grapNodes.add(graphNode);
99 }
100 return nodeNames;
101 }
102
103 protected Node createNode(String name) {
104 var node = ProblemFactory.eINSTANCE.createNode();
105 node.setName(name);
106 return node;
107 }
108
109 @Override
110 public void discardDerivedState(DerivedStateAwareResource resource) {
111 var problem = getProblem(resource);
112 if (problem != null) {
113 var adapter = getOrInstallAdapter(resource);
114 discardDerivedProblemState(problem, adapter);
115 }
116 }
117
118 protected void discardDerivedProblemState(Problem problem, Adapter adapter) {
119 Set<ClassDeclaration> classDeclarations = new HashSet<>();
120 problem.getNodes().clear();
121 for (Statement statement : problem.getStatements()) {
122 if (statement instanceof ClassDeclaration classDeclaration) {
123 classDeclaration.setNewNode(null);
124 classDeclarations.add(classDeclaration);
125 }
126 }
127 adapter.retainAll(classDeclarations);
128 derivedVariableComputer.discardDerivedVariables(problem);
129 }
130
131 protected Adapter getOrInstallAdapter(Resource resource) {
132 if (!(resource instanceof XtextResource)) {
133 return new Adapter();
134 }
135 String resourceLanguageName = ((XtextResource) resource).getLanguageName();
136 if (!languageName.equals(resourceLanguageName)) {
137 return new Adapter();
138 }
139 var adapter = (Adapter) EcoreUtil.getAdapter(resource.eAdapters(), Adapter.class);
140 if (adapter == null) {
141 adapter = new Adapter();
142 resource.eAdapters().add(adapter);
143 }
144 return adapter;
145 }
146
147 protected static class Adapter extends AdapterImpl {
148 private Map<ClassDeclaration, Node> newNodes = new HashMap<>();
149
150 public Node createNodeIfAbsent(ClassDeclaration classDeclaration, Function<ClassDeclaration, Node> createNode) {
151 return newNodes.computeIfAbsent(classDeclaration, createNode);
152 }
153
154 public void retainAll(Collection<ClassDeclaration> classDeclarations) {
155 newNodes.keySet().retainAll(classDeclarations);
156 }
157
158 @Override
159 public boolean isAdapterForType(Object type) {
160 return Adapter.class == type;
161 }
162 }
163}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java
new file mode 100644
index 00000000..7aa75833
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java
@@ -0,0 +1,33 @@
1package tools.refinery.language.resource;
2
3import org.eclipse.emf.ecore.EObject;
4import org.eclipse.xtext.resource.DefaultLocationInFileProvider;
5import org.eclipse.xtext.util.ITextRegion;
6
7import tools.refinery.language.model.ProblemUtil;
8import tools.refinery.language.model.problem.ImplicitVariable;
9import tools.refinery.language.model.problem.Node;
10
11public class ProblemLocationInFileProvider extends DefaultLocationInFileProvider {
12 @Override
13 protected ITextRegion doGetTextRegion(EObject obj, RegionDescription query) {
14 if (obj instanceof Node node) {
15 return getNodeTextRegion(node, query);
16 }
17 if (obj instanceof ImplicitVariable) {
18 return ITextRegion.EMPTY_REGION;
19 }
20 return super.doGetTextRegion(obj, query);
21 }
22
23 protected ITextRegion getNodeTextRegion(Node node, RegionDescription query) {
24 if (ProblemUtil.isIndividualNode(node)) {
25 return super.doGetTextRegion(node, query);
26 }
27 if (ProblemUtil.isNewNode(node)) {
28 EObject container = node.eContainer();
29 return doGetTextRegion(container, query);
30 }
31 return ITextRegion.EMPTY_REGION;
32 }
33}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java
new file mode 100644
index 00000000..f86ebd38
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java
@@ -0,0 +1,103 @@
1package tools.refinery.language.resource;
2
3import org.eclipse.emf.ecore.EObject;
4import org.eclipse.xtext.EcoreUtil2;
5import org.eclipse.xtext.naming.IQualifiedNameConverter;
6import org.eclipse.xtext.naming.QualifiedName;
7import org.eclipse.xtext.resource.EObjectDescription;
8import org.eclipse.xtext.resource.IEObjectDescription;
9import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy;
10import org.eclipse.xtext.util.IAcceptor;
11
12import com.google.inject.Inject;
13import com.google.inject.Singleton;
14
15import tools.refinery.language.model.ProblemUtil;
16import tools.refinery.language.model.problem.NamedElement;
17import tools.refinery.language.model.problem.Node;
18import tools.refinery.language.model.problem.Problem;
19import tools.refinery.language.model.problem.Variable;
20import tools.refinery.language.naming.NamingUtil;
21
22@Singleton
23public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy {
24 @Inject
25 private IQualifiedNameConverter qualifiedNameConverter;
26
27 @Override
28 public boolean createEObjectDescriptions(EObject eObject, IAcceptor<IEObjectDescription> acceptor) {
29 if (!shouldExport(eObject)) {
30 return false;
31 }
32 var qualifiedName = getNameAsQualifiedName(eObject);
33 if (qualifiedName == null) {
34 return true;
35 }
36 var problem = EcoreUtil2.getContainerOfType(eObject, Problem.class);
37 var problemQualifiedName = getNameAsQualifiedName(problem);
38 boolean nameExported;
39 if (shouldExportSimpleName(eObject)) {
40 acceptEObjectDescription(eObject, problemQualifiedName, qualifiedName, acceptor);
41 nameExported = true;
42 } else {
43 nameExported = false;
44 }
45 var parent = eObject.eContainer();
46 while (parent != null && parent != problem) {
47 var parentQualifiedName = getNameAsQualifiedName(parent);
48 if (parentQualifiedName == null) {
49 parent = parent.eContainer();
50 continue;
51 }
52 qualifiedName = parentQualifiedName.append(qualifiedName);
53 if (shouldExportSimpleName(parent)) {
54 acceptEObjectDescription(eObject, problemQualifiedName, qualifiedName, acceptor);
55 nameExported = true;
56 } else {
57 nameExported = false;
58 }
59 parent = parent.eContainer();
60 }
61 if (!nameExported) {
62 acceptEObjectDescription(eObject, problemQualifiedName, qualifiedName, acceptor);
63 }
64 return true;
65 }
66
67 protected QualifiedName getNameAsQualifiedName(EObject eObject) {
68 if (!(eObject instanceof NamedElement)) {
69 return null;
70 }
71 var namedElement = (NamedElement) eObject;
72 var name = namedElement.getName();
73 if (NamingUtil.isNullOrEmpty(name)) {
74 return null;
75 }
76 return qualifiedNameConverter.toQualifiedName(name);
77 }
78
79 protected boolean shouldExport(EObject eObject) {
80 if (eObject instanceof Variable) {
81 // Variables are always private to the containing predicate definition.
82 return false;
83 }
84 if (eObject instanceof Node node) {
85 // Only enum literals and new nodes are visible across problem files.
86 return ProblemUtil.isIndividualNode(node) || ProblemUtil.isNewNode(node);
87 }
88 return true;
89 }
90
91 protected boolean shouldExportSimpleName(EObject eObject) {
92 if (eObject instanceof Node node) {
93 return !ProblemUtil.isNewNode(node);
94 }
95 return true;
96 }
97
98 private void acceptEObjectDescription(EObject eObject, QualifiedName prefix, QualifiedName qualifiedName,
99 IAcceptor<IEObjectDescription> acceptor) {
100 var qualifiedNameWithPrefix = prefix == null ? qualifiedName : prefix.append(qualifiedName);
101 acceptor.accept(EObjectDescription.create(qualifiedNameWithPrefix, eObject));
102 }
103}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemXmiResourceFactory.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemXmiResourceFactory.java
new file mode 100644
index 00000000..68aa6016
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemXmiResourceFactory.java
@@ -0,0 +1,16 @@
1package tools.refinery.language.resource;
2
3import org.eclipse.emf.common.util.URI;
4import org.eclipse.emf.ecore.resource.Resource;
5import org.eclipse.xtext.resource.IResourceFactory;
6
7import tools.refinery.language.model.problem.util.ProblemResourceFactoryImpl;
8
9public class ProblemXmiResourceFactory implements IResourceFactory {
10 private Resource.Factory problemResourceFactory = new ProblemResourceFactoryImpl();
11
12 @Override
13 public Resource createResource(URI uri) {
14 return problemResourceFactory.createResource(uri);
15 }
16}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java
new file mode 100644
index 00000000..7525dfc6
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java
@@ -0,0 +1,51 @@
1package tools.refinery.language.resource;
2
3import java.util.HashMap;
4import java.util.Map;
5
6import org.eclipse.emf.ecore.EObject;
7import org.eclipse.xtext.util.IResourceScopeCache;
8
9import com.google.inject.Inject;
10import com.google.inject.Singleton;
11
12import tools.refinery.language.model.problem.Problem;
13
14@Singleton
15public class ReferenceCounter {
16 @Inject
17 private IResourceScopeCache cache;
18
19 public int countReferences(Problem problem, EObject eObject) {
20 var count = getReferenceCounts(problem).get(eObject);
21 if (count == null) {
22 return 0;
23 }
24 return count;
25 }
26
27 protected Map<EObject, Integer> getReferenceCounts(Problem problem) {
28 var resource = problem.eResource();
29 if (resource == null) {
30 return doGetReferenceCounts(problem);
31 }
32 return cache.get(problem, resource, () -> doGetReferenceCounts(problem));
33 }
34
35 protected Map<EObject, Integer> doGetReferenceCounts(Problem problem) {
36 var map = new HashMap<EObject, Integer>();
37 countCrossReferences(problem, map);
38 var iterator = problem.eAllContents();
39 while (iterator.hasNext()) {
40 var eObject = iterator.next();
41 countCrossReferences(eObject, map);
42 }
43 return map;
44 }
45
46 protected void countCrossReferences(EObject eObject, Map<EObject, Integer> map) {
47 for (var referencedObject : eObject.eCrossReferences()) {
48 map.compute(referencedObject, (key, currentValue) -> currentValue == null ? 1 : currentValue + 1);
49 }
50 }
51}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java
new file mode 100644
index 00000000..b582d16b
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java
@@ -0,0 +1,18 @@
1package tools.refinery.language.scoping;
2
3import java.util.LinkedHashSet;
4
5import org.eclipse.emf.common.util.URI;
6import org.eclipse.emf.ecore.resource.Resource;
7import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider;
8
9import tools.refinery.language.model.ProblemUtil;
10
11public class ProblemGlobalScopeProvider extends ImportUriGlobalScopeProvider {
12 @Override
13 protected LinkedHashSet<URI> getImportedUris(Resource resource) {
14 LinkedHashSet<URI> importedUris = new LinkedHashSet<>();
15 importedUris.add(ProblemUtil.BUILTIN_LIBRARY_URI);
16 return importedUris;
17 }
18}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java
new file mode 100644
index 00000000..85797025
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java
@@ -0,0 +1,42 @@
1package tools.refinery.language.scoping;
2
3import java.util.List;
4
5import org.eclipse.emf.ecore.EObject;
6import org.eclipse.emf.ecore.resource.Resource;
7import org.eclipse.xtext.naming.QualifiedName;
8import org.eclipse.xtext.resource.IResourceDescriptions;
9import org.eclipse.xtext.resource.IResourceDescriptionsProvider;
10import org.eclipse.xtext.resource.ISelectable;
11import org.eclipse.xtext.scoping.impl.ImportNormalizer;
12import org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider;
13
14import com.google.inject.Inject;
15
16import tools.refinery.language.model.ProblemUtil;
17
18public class ProblemLocalScopeProvider extends ImportedNamespaceAwareLocalScopeProvider {
19 private static final QualifiedName BUILTIN_LIBRARY_QUALIFIED_NAME = QualifiedName
20 .create(ProblemUtil.BUILTIN_LIBRARY_NAME);
21
22 @Inject
23 private IResourceDescriptionsProvider resourceDescriptionsProvider;
24
25 @Override
26 protected List<ImportNormalizer> getImplicitImports(boolean ignoreCase) {
27 return List.of(doCreateImportNormalizer(BUILTIN_LIBRARY_QUALIFIED_NAME, true, ignoreCase));
28 }
29
30 @Override
31 protected List<ImportNormalizer> getImportedNamespaceResolvers(EObject context, boolean ignoreCase) {
32 return List.of();
33 }
34
35 @Override
36 protected ISelectable internalGetAllDescriptions(Resource resource) {
37 // Force the use of ProblemResourceDescriptionStrategy to include all QualifiedNames of objects.
38 IResourceDescriptions resourceDescriptions = resourceDescriptionsProvider
39 .getResourceDescriptions(resource.getResourceSet());
40 return resourceDescriptions.getResourceDescription(resource.getURI());
41 }
42}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java
new file mode 100644
index 00000000..d31a5308
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java
@@ -0,0 +1,104 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language.scoping;
5
6import java.util.ArrayList;
7import java.util.List;
8
9import org.eclipse.emf.ecore.EObject;
10import org.eclipse.emf.ecore.EReference;
11import org.eclipse.xtext.EcoreUtil2;
12import org.eclipse.xtext.scoping.IScope;
13import org.eclipse.xtext.scoping.Scopes;
14
15import tools.refinery.language.model.ProblemUtil;
16import tools.refinery.language.model.problem.ClassDeclaration;
17import tools.refinery.language.model.problem.ExistentialQuantifier;
18import tools.refinery.language.model.problem.NewActionLiteral;
19import tools.refinery.language.model.problem.ParametricDefinition;
20import tools.refinery.language.model.problem.Action;
21import tools.refinery.language.model.problem.Problem;
22import tools.refinery.language.model.problem.ProblemPackage;
23import tools.refinery.language.model.problem.ReferenceDeclaration;
24import tools.refinery.language.model.problem.Variable;
25import tools.refinery.language.model.problem.VariableOrNodeArgument;
26
27/**
28 * This class contains custom scoping description.
29 *
30 * See
31 * https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping
32 * on how and when to use it.
33 */
34public class ProblemScopeProvider extends AbstractProblemScopeProvider {
35
36 @Override
37 public IScope getScope(EObject context, EReference reference) {
38 var scope = super.getScope(context, reference);
39 if (reference == ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE
40 || reference == ProblemPackage.Literals.NODE_VALUE_ASSERTION__NODE) {
41 return getNodesScope(context, scope);
42 }
43 if (reference == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE
44 || reference == ProblemPackage.Literals.DELETE_ACTION_LITERAL__VARIABLE_OR_NODE) {
45 return getVariableScope(context, scope);
46 }
47 if (reference == ProblemPackage.Literals.REFERENCE_DECLARATION__OPPOSITE) {
48 return getOppositeScope(context, scope);
49 }
50 return scope;
51 }
52
53 protected IScope getNodesScope(EObject context, IScope delegateScope) {
54 var problem = EcoreUtil2.getContainerOfType(context, Problem.class);
55 if (problem == null) {
56 return delegateScope;
57 }
58 return Scopes.scopeFor(problem.getNodes(), delegateScope);
59 }
60
61 protected IScope getVariableScope(EObject context, IScope delegateScope) {
62 List<Variable> variables = new ArrayList<>();
63 EObject currentContext = context;
64 if (context instanceof VariableOrNodeArgument argument) {
65 Variable singletonVariable = argument.getSingletonVariable();
66 if (singletonVariable != null) {
67 variables.add(singletonVariable);
68 }
69 }
70 while (currentContext != null && !(currentContext instanceof ParametricDefinition)) {
71 if (currentContext instanceof ExistentialQuantifier quantifier) {
72 variables.addAll(quantifier.getImplicitVariables());
73 } else
74 if(currentContext instanceof Action action) {
75 for (var literal : action.getActionLiterals()) {
76 if(literal instanceof NewActionLiteral newActionLiteral && newActionLiteral.getVariable() != null) {
77 variables.add(newActionLiteral.getVariable());
78 }
79 }
80 }
81 currentContext = currentContext.eContainer();
82 }
83 IScope parentScope = getNodesScope(context, delegateScope);
84 if (currentContext != null) {
85 ParametricDefinition definition = (ParametricDefinition) currentContext;
86 parentScope = Scopes.scopeFor(definition.getParameters(),parentScope);
87 }
88 return Scopes.scopeFor(variables,parentScope);
89 }
90
91 protected IScope getOppositeScope(EObject context, IScope delegateScope) {
92 var referenceDeclaration = EcoreUtil2.getContainerOfType(context, ReferenceDeclaration.class);
93 if (referenceDeclaration == null) {
94 return delegateScope;
95 }
96 var relation = referenceDeclaration.getReferenceType();
97 if (!(relation instanceof ClassDeclaration)) {
98 return delegateScope;
99 }
100 var classDeclaration = (ClassDeclaration) relation;
101 var referenceDeclarations = ProblemUtil.getAllReferenceDeclarations(classDeclaration);
102 return Scopes.scopeFor(referenceDeclarations, delegateScope);
103 }
104}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java
new file mode 100644
index 00000000..975fdca2
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java
@@ -0,0 +1,62 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language.validation;
5
6import org.eclipse.xtext.EcoreUtil2;
7import org.eclipse.xtext.validation.Check;
8
9import com.google.inject.Inject;
10
11import tools.refinery.language.model.ProblemUtil;
12import tools.refinery.language.model.problem.Node;
13import tools.refinery.language.model.problem.Problem;
14import tools.refinery.language.model.problem.ProblemPackage;
15import tools.refinery.language.model.problem.Variable;
16import tools.refinery.language.model.problem.VariableOrNodeArgument;
17import tools.refinery.language.resource.ReferenceCounter;
18
19/**
20 * This class contains custom validation rules.
21 *
22 * See
23 * https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation
24 */
25public class ProblemValidator extends AbstractProblemValidator {
26 private static final String ISSUE_PREFIX = "tools.refinery.language.validation.ProblemValidator.";
27
28 public static final String SINGLETON_VARIABLE_ISSUE = ISSUE_PREFIX + "SINGLETON_VARIABLE";
29
30 public static final String NON_INDIVIDUAL_NODE_ISSUE = ISSUE_PREFIX + "NON_INDIVIDUAL_NODE";
31
32 @Inject
33 private ReferenceCounter referenceCounter;
34
35 @Check
36 public void checkUniqueVariable(VariableOrNodeArgument argument) {
37 var variableOrNode = argument.getVariableOrNode();
38 if (variableOrNode instanceof Variable variable && ProblemUtil.isImplicitVariable(variable)
39 && !ProblemUtil.isSingletonVariable(variable)) {
40 var problem = EcoreUtil2.getContainerOfType(variable, Problem.class);
41 if (problem != null && referenceCounter.countReferences(problem, variable) <= 1) {
42 var name = variable.getName();
43 var message = "Variable '%s' has only a single reference. Add another reference or mark is as a singleton variable: '_%s'"
44 .formatted(name, name);
45 warning(message, argument, ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE,
46 INSIGNIFICANT_INDEX, SINGLETON_VARIABLE_ISSUE);
47 }
48 }
49 }
50
51 @Check
52 public void checkNonUniqueNode(VariableOrNodeArgument argument) {
53 var variableOrNode = argument.getVariableOrNode();
54 if (variableOrNode instanceof Node node && !ProblemUtil.isIndividualNode(node)) {
55 var name = node.getName();
56 var message = "Only individual nodes can be referenced in predicates. Mark '%s' as individual with the declaration 'indiv %s.'"
57 .formatted(name, name);
58 error(message, argument, ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE,
59 INSIGNIFICANT_INDEX, NON_INDIVIDUAL_NODE_ISSUE);
60 }
61 }
62}
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.xtend b/subprojects/language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.xtend
new file mode 100644
index 00000000..53d31a6c
--- /dev/null
+++ b/subprojects/language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.xtend
@@ -0,0 +1,64 @@
1/*
2 * generated by Xtext 2.26.0.M1
3 */
4package tools.refinery.language.tests
5
6import com.google.inject.Inject
7import org.eclipse.xtext.testing.InjectWith
8import org.eclipse.xtext.testing.extensions.InjectionExtension
9import org.eclipse.xtext.testing.util.ParseHelper
10import org.junit.jupiter.api.Test
11import org.junit.jupiter.api.^extension.ExtendWith
12import tools.refinery.language.model.problem.Problem
13import tools.refinery.language.model.tests.ProblemTestUtil
14
15import static org.hamcrest.MatcherAssert.assertThat
16import static org.hamcrest.Matchers.*
17
18@ExtendWith(InjectionExtension)
19@InjectWith(ProblemInjectorProvider)
20class ProblemParsingTest {
21 @Inject
22 ParseHelper<Problem> parseHelper
23
24 @Inject
25 extension ProblemTestUtil
26
27 @Test
28 def void exampleTest() {
29 val it = parseHelper.parse('''
30 class Family {
31 contains Person[] members
32 }
33
34 class Person {
35 Person[0..*] children opposite parent
36 Person[0..1] parent opposite children
37 int age
38 TaxStatus taxStatus
39 }
40
41 enum TaxStatus {
42 child, student, adult, retired
43 }
44
45 % A child cannot have any dependents.
46 error invalidTaxStatus(Person p) <->
47 taxStatus(p, child), children(p, _q).
48
49 indiv family.
50 Family(family).
51 members(family, anne): true.
52 members(family, bob).
53 members(family, ciri).
54 children(anne, ciri).
55 ?children(bob, ciri).
56 taxStatus(anne, adult).
57 age(anne, 35).
58 bobAge: 27.
59 age(bob, bobAge).
60 !age(ciri, bobAge).
61 ''')
62 assertThat(errors, empty)
63 }
64}
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/formatting2/ProblemFormatterTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/formatting2/ProblemFormatterTest.java
new file mode 100644
index 00000000..41ad2d31
--- /dev/null
+++ b/subprojects/language/src/test/java/tools/refinery/language/tests/formatting2/ProblemFormatterTest.java
@@ -0,0 +1,235 @@
1package tools.refinery.language.tests.formatting2;
2
3import static org.hamcrest.MatcherAssert.assertThat;
4import static org.hamcrest.Matchers.equalTo;
5
6import java.util.List;
7
8import org.eclipse.xtext.formatting2.FormatterRequest;
9import org.eclipse.xtext.formatting2.IFormatter2;
10import org.eclipse.xtext.formatting2.regionaccess.ITextRegionAccess;
11import org.eclipse.xtext.formatting2.regionaccess.ITextReplacement;
12import org.eclipse.xtext.formatting2.regionaccess.TextRegionAccessBuilder;
13import org.eclipse.xtext.resource.XtextResource;
14import org.eclipse.xtext.testing.InjectWith;
15import org.eclipse.xtext.testing.extensions.InjectionExtension;
16import org.eclipse.xtext.testing.util.ParseHelper;
17import org.junit.jupiter.api.Test;
18import org.junit.jupiter.api.extension.ExtendWith;
19
20import com.google.inject.Inject;
21import com.google.inject.Provider;
22
23import tools.refinery.language.model.problem.Problem;
24import tools.refinery.language.tests.ProblemInjectorProvider;
25
26@ExtendWith(InjectionExtension.class)
27@InjectWith(ProblemInjectorProvider.class)
28class ProblemFormatterTest {
29 @Inject
30 private ParseHelper<Problem> parseHelper;
31
32 @Inject
33 private Provider<FormatterRequest> formatterRequestProvider;
34
35 @Inject
36 private TextRegionAccessBuilder regionBuilder;
37
38 @Inject
39 private IFormatter2 formatter2;
40
41 @Test
42 void problemNameTest() {
43 testFormatter(" problem problem . ", "problem problem.\n");
44 }
45
46 @Test
47 void assertionTest() {
48 testFormatter(" equals ( a , b , * ) : true . ", "equals(a, b, *): true.\n");
49 }
50
51 @Test
52 void defaultAssertionTest() {
53 testFormatter(" default equals ( a , b , * ) : true . ", "default equals(a, b, *): true.\n");
54 }
55
56 @Test
57 void assertionShortTrueTest() {
58 testFormatter(" equals ( a , b , * ) . ", "equals(a, b, *).\n");
59 }
60
61 @Test
62 void defaultAssertionShortTrueTest() {
63 testFormatter(" default equals ( a , b , * ) . ", "default equals(a, b, *).\n");
64 }
65
66 @Test
67 void assertionShortFalseTest() {
68 testFormatter(" ! equals ( a , b , * ) . ", "!equals(a, b, *).\n");
69 }
70
71 @Test
72 void defaultAssertionShortFalseTest() {
73 testFormatter(" default ! equals ( a , b , * ) . ", "default !equals(a, b, *).\n");
74 }
75
76 @Test
77 void assertionShortUnknownTest() {
78 testFormatter(" ? equals ( a , b , * ) . ", "?equals(a, b, *).\n");
79 }
80
81 @Test
82 void defaultAssertionShortUnknownTest() {
83 testFormatter(" default ? equals ( a , b , * ) . ", "default ?equals(a, b, *).\n");
84 }
85
86 @Test
87 void multipleAssertionsTest() {
88 testFormatter(" exists ( a ) . ? equals ( a , a ).", """
89 exists(a).
90 ?equals(a, a).
91 """);
92 }
93
94 @Test
95 void multipleAssertionsNamedProblemTest() {
96 testFormatter(" problem foo . exists ( a ) . ? equals ( a , a ).", """
97 problem foo.
98
99 exists(a).
100 ?equals(a, a).
101 """);
102 }
103
104 @Test
105 void classWithoutBodyTest() {
106 testFormatter(" class Foo . ", "class Foo.\n");
107 }
108
109 @Test
110 void abstractClassWithoutBodyTest() {
111 testFormatter(" abstract class Foo . ", "abstract class Foo.\n");
112 }
113
114 @Test
115 void classExtendsWithoutBodyTest() {
116 testFormatter(" class Foo. class Bar . class Quux extends Foo , Bar . ", """
117 class Foo.
118
119 class Bar.
120
121 class Quux extends Foo, Bar.
122 """);
123 }
124
125 @Test
126 void classWithEmptyBodyTest() {
127 testFormatter(" class Foo { } ", """
128 class Foo {
129 }
130 """);
131 }
132
133 @Test
134 void classExtendsWithBodyTest() {
135 testFormatter(" class Foo. class Bar . class Quux extends Foo , Bar { } ", """
136 class Foo.
137
138 class Bar.
139
140 class Quux extends Foo, Bar {
141 }
142 """);
143 }
144
145 @Test
146 void predicateWithoutBodyTest() {
147 testFormatter(" pred foo ( node a , b ) . ", "pred foo(node a, b).\n");
148 }
149
150 @Test
151 void predicateWithBodyTest() {
152 testFormatter(
153 " pred foo ( node a , b ) <-> equal (a , _c ) , ! equal ( a , b ) ; equal+( a , b ) . ",
154 "pred foo(node a, b) <-> equal(a, _c), !equal(a, b); equal+(a, b).\n");
155 }
156
157 @Test
158 void predicatesWithoutBodyTest() {
159 testFormatter(" pred foo ( node a , b ) . pred bar ( node c ) . ", """
160 pred foo(node a, b).
161
162 pred bar(node c).
163 """);
164 }
165
166 @Test
167 void predicateCommentsTest() {
168 testFormatter("""
169 % Some foo
170 pred foo ( node a , b ) .
171 % Some bar
172 pred bar ( node c ) .
173 """, """
174 % Some foo
175 pred foo(node a, b).
176
177 % Some bar
178 pred bar(node c).
179 """);
180 }
181
182 @Test
183 void individualDeclarationTest() {
184 testFormatter(" indiv a , b . ", "indiv a, b.\n");
185 }
186
187 @Test
188 void mixedDeclarationsTest() {
189 testFormatter("""
190 problem test.
191 pred foo(node a).
192 class Foo.
193 foo(n1, n2).
194 indiv i1.
195 !foo(i1, n1).
196 pred bar(node a, node b).
197 pred quux().
198 default !bar(*, *).
199 """, """
200 problem test.
201
202 pred foo(node a).
203
204 class Foo.
205
206 foo(n1, n2).
207 indiv i1.
208 !foo(i1, n1).
209
210 pred bar(node a, node b).
211
212 pred quux().
213
214 default !bar(*, *).
215 """);
216 }
217
218 private void testFormatter(String toFormat, String expected) {
219 Problem problem;
220 try {
221 problem = parseHelper.parse(toFormat);
222 } catch (Exception e) {
223 throw new RuntimeException("Failed to parse document", e);
224 }
225 var resource = (XtextResource) problem.eResource();
226 FormatterRequest request = formatterRequestProvider.get();
227 request.setAllowIdentityEdits(false);
228 request.setFormatUndefinedHiddenRegionsOnly(false);
229 ITextRegionAccess regionAccess = regionBuilder.forNodeModel(resource).create();
230 request.setTextRegionAccess(regionAccess);
231 List<ITextReplacement> replacements = formatter2.format(request);
232 var formattedString = regionAccess.getRewriter().renderToString(replacements);
233 assertThat(formattedString, equalTo(expected));
234 }
235}
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/rules/DirectRuleParsingTest.xtend b/subprojects/language/src/test/java/tools/refinery/language/tests/rules/DirectRuleParsingTest.xtend
new file mode 100644
index 00000000..d60651a0
--- /dev/null
+++ b/subprojects/language/src/test/java/tools/refinery/language/tests/rules/DirectRuleParsingTest.xtend
@@ -0,0 +1,96 @@
1package tools.refinery.language.tests.rules
2
3import com.google.inject.Inject
4import org.eclipse.xtext.testing.InjectWith
5import org.eclipse.xtext.testing.extensions.InjectionExtension
6import org.eclipse.xtext.testing.util.ParseHelper
7import org.junit.jupiter.api.Test
8import org.junit.jupiter.api.^extension.ExtendWith
9import tools.refinery.language.model.problem.Problem
10import tools.refinery.language.tests.ProblemInjectorProvider
11import tools.refinery.language.model.tests.ProblemTestUtil
12
13import static org.hamcrest.MatcherAssert.assertThat
14import static org.hamcrest.Matchers.*
15
16@ExtendWith(InjectionExtension)
17@InjectWith(ProblemInjectorProvider)
18class DirectRuleParsingTest {
19 @Inject
20 ParseHelper<Problem> parseHelper
21
22 @Inject
23 extension ProblemTestUtil
24
25 @Test
26 def void relationValueRewriteTest() {
27 val it = parseHelper.parse('''
28 pred Person(p).
29 direct rule r(p1): Person(p1) = true ~> Person(p1) = false.
30 ''')
31 assertThat(errors, empty)
32 }
33
34 @Test
35 def void relationValueMergeTest() {
36 val it = parseHelper.parse('''
37 pred Person(p).
38 direct rule r(p1): Person(p1): true ~> Person(p1): false.
39 ''')
40 assertThat(errors, empty)
41 }
42
43 @Test
44 def void newNodeTest() {
45 val it = parseHelper.parse('''
46 pred Person(p).
47 direct rule r(p1): Person(p1) = true ~> new p2, Person(p2) = unknown.
48 ''')
49 assertThat(errors, empty)
50 assertThat(rule("r").param(0), equalTo(rule("r").conj(0).lit(0).valueAtom.arg(0).variable))
51 assertThat(rule("r").actionLit(0).newVar,
52 equalTo(rule("r").actionLit(1).valueAtom.arg(0).variable)
53 )
54 }
55
56 @Test
57 def void differentScopeTest() {
58 val it = parseHelper.parse('''
59 pred Friend(a, b).
60 direct rule r(p1): Friend(p1, p2) = false ~> new p2, Friend(p1, p2) = true.
61 ''')
62 assertThat(errors, empty)
63 assertThat(rule("r").conj(0).lit(0).valueAtom.arg(1).variable,
64 not(equalTo(rule("r").actionLit(1).valueAtom.arg(1).variable)))
65 }
66
67 @Test
68 def void parameterShadowingTest() {
69 val it = parseHelper.parse('''
70 pred Friend(a, b).
71 direct rule r(p1, p2): Friend(p1, p2) = false ~> new p2, Friend(p1, p2) = true.
72 ''')
73 assertThat(errors, empty)
74 assertThat(rule("r").param(1),
75 not(equalTo(rule("r").actionLit(1).valueAtom.arg(1).variable)))
76 }
77
78 @Test
79 def void deleteParameterNodeTest() {
80 val it = parseHelper.parse('''
81 pred Person(p).
82 direct rule r(p1): Person(p1): false ~> delete p1.
83 ''')
84 assertThat(errors, empty)
85 }
86
87 @Test
88 def void deleteDifferentScopeNodeTest() {
89 val it = parseHelper.parse('''
90 pred Friend(p).
91 direct rule r(p1): Friend(p1, p2) = true ~> delete p2.
92 ''')
93 assertThat(errors, not(empty))
94 }
95
96}
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.xtend b/subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.xtend
new file mode 100644
index 00000000..3a046341
--- /dev/null
+++ b/subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.xtend
@@ -0,0 +1,322 @@
1package tools.refinery.language.tests.scoping
2
3import com.google.inject.Inject
4import java.util.stream.Stream
5import org.eclipse.xtext.testing.InjectWith
6import org.eclipse.xtext.testing.extensions.InjectionExtension
7import org.eclipse.xtext.testing.util.ParseHelper
8import org.junit.jupiter.api.Test
9import org.junit.jupiter.api.^extension.ExtendWith
10import org.junit.jupiter.params.ParameterizedTest
11import org.junit.jupiter.params.provider.Arguments
12import org.junit.jupiter.params.provider.MethodSource
13import org.junit.jupiter.params.provider.ValueSource
14import tools.refinery.language.model.problem.Problem
15import tools.refinery.language.model.tests.ProblemTestUtil
16import tools.refinery.language.tests.ProblemInjectorProvider
17
18import static org.hamcrest.MatcherAssert.assertThat
19import static org.hamcrest.Matchers.*
20
21@ExtendWith(InjectionExtension)
22@InjectWith(ProblemInjectorProvider)
23class NodeScopingTest {
24 @Inject
25 ParseHelper<Problem> parseHelper
26
27 @Inject
28 extension ProblemTestUtil
29
30 @ParameterizedTest
31 @ValueSource(strings=#["", "builtin::"])
32 def void builtInArgumentTypeTest(String prefix) {
33 val it = parseHelper.parse('''
34 pred predicate(«prefix»node a, «prefix»data b, «prefix»int c).
35 ''')
36 assertThat(errors, empty)
37 assertThat(pred('predicate').param(0).parameterType, equalTo(builtin.findClass('node')))
38 assertThat(pred('predicate').param(1).parameterType, equalTo(builtin.findClass('data')))
39 assertThat(pred('predicate').param(2).parameterType, equalTo(builtin.findClass('int')))
40 }
41
42 @Test
43 def void implicitNodeInAssertionTest() {
44 val it = parseHelper.parse('''
45 pred predicate(node x, node y) <-> node(x).
46 predicate(a, a).
47 ?predicate(a, b).
48 ''')
49 assertThat(errors, empty)
50 assertThat(nodeNames, hasItems('a', 'b'))
51 assertThat(assertion(0).arg(0).node, equalTo(node('a')))
52 assertThat(assertion(0).arg(1).node, equalTo(node('a')))
53 assertThat(assertion(1).arg(0).node, equalTo(node('a')))
54 assertThat(assertion(1).arg(1).node, equalTo(node('b')))
55 }
56
57 @Test
58 def void implicitNodeInNodeValueAssertionTest() {
59 val it = parseHelper.parse('''
60 a: 16.
61 ''')
62 assertThat(errors, empty)
63 assertThat(nodeNames, hasItems('a'))
64 assertThat(nodeValueAssertion(0).node, equalTo(node('a')))
65 }
66
67 @Test
68 def void implicitNodeInPredicateTest() {
69 val it = parseHelper.parse('''
70 pred predicate(node a) <-> node(b).
71 predicate(b).
72 ''')
73 assertThat(errors, empty)
74 assertThat(nodeNames, hasItem("b"))
75 assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(node("b")))
76 assertThat(assertion(0).arg(0).node, equalTo(node("b")))
77 }
78
79 @ParameterizedTest
80 @MethodSource("individualNodeReferenceSource")
81 def void individualNodeInAssertionTest(String qualifiedNamePrefix, boolean namedProblem) {
82 val it = parseHelper.parse('''
83 «IF namedProblem»problem test.«ENDIF»
84 indiv a, b.
85 pred predicate(node x, node y) <-> node(x).
86 predicate(«qualifiedNamePrefix»a, «qualifiedNamePrefix»a).
87 ?predicate(«qualifiedNamePrefix»a, «qualifiedNamePrefix»b).
88 ''')
89 assertThat(errors, empty)
90 assertThat(nodeNames, empty)
91 assertThat(assertion(0).arg(0).node, equalTo(individualNode('a')))
92 assertThat(assertion(0).arg(1).node, equalTo(individualNode('a')))
93 assertThat(assertion(1).arg(0).node, equalTo(individualNode('a')))
94 assertThat(assertion(1).arg(1).node, equalTo(individualNode('b')))
95 }
96
97 @ParameterizedTest
98 @MethodSource("individualNodeReferenceSource")
99 def void individualNodeInNodeValueAssertionTest(String qualifiedNamePrefix, boolean namedProblem) {
100 val it = parseHelper.parse('''
101 «IF namedProblem»problem test.«ENDIF»
102 indiv a.
103 «qualifiedNamePrefix»a: 16.
104 ''')
105 assertThat(errors, empty)
106 assertThat(nodeNames, empty)
107 assertThat(nodeValueAssertion(0).node, equalTo(individualNode('a')))
108 }
109
110 @ParameterizedTest
111 @MethodSource("individualNodeReferenceSource")
112 def void individualNodeInPredicateTest(String qualifiedNamePrefix, boolean namedProblem) {
113 val it = parseHelper.parse('''
114 «IF namedProblem»problem test.«ENDIF»
115 indiv b.
116 pred predicate(node a) <-> node(«qualifiedNamePrefix»b).
117 ''')
118 assertThat(errors, empty)
119 assertThat(nodeNames, empty)
120 assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(individualNode("b")))
121 }
122
123 static def individualNodeReferenceSource() {
124 Stream.of(
125 Arguments.of("", false),
126 Arguments.of("", true),
127 Arguments.of("test::", true)
128 )
129 }
130
131 @ParameterizedTest
132 @MethodSource("builtInNodeReferencesSource")
133 def void builtInNodeTest(String qualifiedName) {
134 val it = parseHelper.parse('''
135 pred predicate(node x) <-> node(x).
136 predicate(«qualifiedName»).
137 ''')
138 assertThat(errors, empty)
139 assertThat(nodes, empty)
140 assertThat(assertion(0).arg(0).node, equalTo(builtin.findClass('int').newNode))
141 }
142
143 @ParameterizedTest
144 @MethodSource("builtInNodeReferencesSource")
145 def void builtInNodeInNodeValueAssertionTest(String qualifiedName) {
146 val it = parseHelper.parse('''
147 «qualifiedName»: 16.
148 ''')
149 assertThat(errors, empty)
150 assertThat(nodes, empty)
151 assertThat(nodeValueAssertion(0).node, equalTo(builtin.findClass('int').newNode))
152 }
153
154 @ParameterizedTest
155 @MethodSource("builtInNodeReferencesSource")
156 def void builtInNodeInPredicateTest(String qualifiedName) {
157 val it = parseHelper.parse('''
158 pred predicate(node x) <-> node(«qualifiedName»).
159 ''')
160 assertThat(errors, empty)
161 assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(builtin.findClass('int').newNode))
162 }
163
164 static def builtInNodeReferencesSource() {
165 Stream.of(
166 Arguments.of("int::new"),
167 Arguments.of("builtin::int::new")
168 )
169 }
170
171 @ParameterizedTest(name="{0}, namedProblem={1}")
172 @MethodSource("classNewNodeReferencesSource")
173 def void classNewNodeTest(String qualifiedName, boolean namedProblem) {
174 val it = parseHelper.parse('''
175 «IF namedProblem»problem test.«ENDIF»
176 class Foo.
177 pred predicate(node x) <-> node(x).
178 predicate(«qualifiedName»).
179 ''')
180 assertThat(errors, empty)
181 assertThat(nodes, empty)
182 assertThat(assertion(0).arg(0).node, equalTo(findClass('Foo').newNode))
183 }
184
185 @ParameterizedTest(name="{0}, namedProblem={1}")
186 @MethodSource("classNewNodeReferencesSource")
187 def void classNewNodeInNodeValueAssertionTest(String qualifiedName, boolean namedProblem) {
188 val it = parseHelper.parse('''
189 «IF namedProblem»problem test.«ENDIF»
190 class Foo.
191 «qualifiedName»: 16.
192 ''')
193 assertThat(errors, empty)
194 assertThat(nodes, empty)
195 assertThat(nodeValueAssertion(0).node, equalTo(findClass('Foo').newNode))
196 }
197
198 @ParameterizedTest(name="{0}, namedProblem={1}")
199 @MethodSource("classNewNodeReferencesSource")
200 def void classNewNodeInPredicateTest(String qualifiedName, boolean namedProblem) {
201 val it = parseHelper.parse('''
202 «IF namedProblem»problem test.«ENDIF»
203 class Foo.
204 pred predicate(node x) <-> node(«qualifiedName»).
205 ''')
206 assertThat(errors, empty)
207 assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(findClass('Foo').newNode))
208 }
209
210 static def classNewNodeReferencesSource() {
211 Stream.of(
212 Arguments.of("Foo::new", false),
213 Arguments.of("Foo::new", true),
214 Arguments.of("test::Foo::new", true)
215 )
216 }
217
218 @Test
219 def void newNodeIsNotSpecial() {
220 val it = parseHelper.parse('''
221 class Foo.
222 pred predicate(node x) <-> node(x).
223 predicate(new).
224 ''')
225 assertThat(errors, empty)
226 assertThat(nodeNames, hasItem('new'))
227 assertThat(assertion(0).arg(0).node, not(equalTo(findClass('Foo').newNode)))
228 }
229
230 @ParameterizedTest(name="{0}, namedProblem={1}")
231 @MethodSource("enumLiteralReferencesSource")
232 def void enumLiteralTest(String qualifiedName, boolean namedProblem) {
233 val it = parseHelper.parse('''
234 «IF namedProblem»problem test.«ENDIF»
235 enum Foo { alpha, beta }
236 pred predicate(Foo a) <-> node(a).
237 predicate(«qualifiedName»).
238 ''')
239 assertThat(errors, empty)
240 assertThat(nodes, empty)
241 assertThat(assertion(0).arg(0).node, equalTo(findEnum("Foo").literal("alpha")))
242 }
243
244 @ParameterizedTest(name="{0}, namedProblem={1}")
245 @MethodSource("enumLiteralReferencesSource")
246 def void enumLiteralInNodeValueAssertionTest(String qualifiedName, boolean namedProblem) {
247 val it = parseHelper.parse('''
248 «IF namedProblem»problem test.«ENDIF»
249 enum Foo { alpha, beta }
250 «qualifiedName»: 16.
251 ''')
252 assertThat(errors, empty)
253 assertThat(nodes, empty)
254 assertThat(nodeValueAssertion(0).node, equalTo(findEnum("Foo").literal("alpha")))
255 }
256
257 @ParameterizedTest(name="{0}, namedProblem={1}")
258 @MethodSource("enumLiteralReferencesSource")
259 def void enumLiteralInPredicateTest(String qualifiedName, boolean namedProblem) {
260 val it = parseHelper.parse('''
261 «IF namedProblem»problem test.«ENDIF»
262 enum Foo { alpha, beta }
263 pred predicate(Foo a) <-> node(«qualifiedName»).
264 ''')
265 assertThat(errors, empty)
266 assertThat(nodes, empty)
267 assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(findEnum("Foo").literal("alpha")))
268 }
269
270 static def enumLiteralReferencesSource() {
271 Stream.of(
272 Arguments.of("alpha", false),
273 Arguments.of("alpha", true),
274 Arguments.of("Foo::alpha", false),
275 Arguments.of("Foo::alpha", true),
276 Arguments.of("test::alpha", true),
277 Arguments.of("test::Foo::alpha", true)
278 )
279 }
280
281 @ParameterizedTest
282 @MethodSource("builtInEnumLiteralReferencesSource")
283 def void builtInEnumLiteralTest(String qualifiedName) {
284 val it = parseHelper.parse('''
285 pred predicate(node a) <-> node(a).
286 predicate(«qualifiedName»).
287 ''')
288 assertThat(errors, empty)
289 assertThat(nodes, empty)
290 assertThat(assertion(0).arg(0).node, equalTo(builtin.findEnum("bool").literal("true")))
291 }
292
293 @ParameterizedTest
294 @MethodSource("builtInEnumLiteralReferencesSource")
295 def void builtInEnumLiteralInNodeValueAssertionTest(String qualifiedName) {
296 val it = parseHelper.parse('''
297 «qualifiedName»: 16.
298 ''')
299 assertThat(errors, empty)
300 assertThat(nodes, empty)
301 assertThat(nodeValueAssertion(0).node, equalTo(builtin.findEnum("bool").literal("true")))
302 }
303
304 @ParameterizedTest
305 @MethodSource("builtInEnumLiteralReferencesSource")
306 def void bultInEnumLiteralInPredicateTest(String qualifiedName) {
307 val it = parseHelper.parse('''
308 pred predicate() <-> node(«qualifiedName»).
309 ''')
310 assertThat(errors, empty)
311 assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(builtin.findEnum("bool").literal("true")))
312 }
313
314 static def builtInEnumLiteralReferencesSource() {
315 Stream.of(
316 Arguments.of("true"),
317 Arguments.of("bool::true"),
318 Arguments.of("builtin::true"),
319 Arguments.of("builtin::bool::true")
320 )
321 }
322}
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/serializer/ProblemSerializerTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/serializer/ProblemSerializerTest.java
new file mode 100644
index 00000000..ba3aaeb7
--- /dev/null
+++ b/subprojects/language/src/test/java/tools/refinery/language/tests/serializer/ProblemSerializerTest.java
@@ -0,0 +1,229 @@
1package tools.refinery.language.tests.serializer;
2
3import static org.hamcrest.MatcherAssert.assertThat;
4import static org.hamcrest.Matchers.equalTo;
5
6import java.io.ByteArrayOutputStream;
7import java.io.IOException;
8import java.util.Map;
9import java.util.stream.Stream;
10
11import org.eclipse.emf.common.util.URI;
12import org.eclipse.emf.ecore.resource.Resource;
13import org.eclipse.emf.ecore.resource.ResourceSet;
14import org.eclipse.xtext.testing.InjectWith;
15import org.eclipse.xtext.testing.extensions.InjectionExtension;
16import org.junit.jupiter.api.BeforeEach;
17import org.junit.jupiter.api.Test;
18import org.junit.jupiter.api.extension.ExtendWith;
19import org.junit.jupiter.params.ParameterizedTest;
20import org.junit.jupiter.params.provider.Arguments;
21import org.junit.jupiter.params.provider.MethodSource;
22
23import com.google.inject.Inject;
24
25import tools.refinery.language.model.ProblemUtil;
26import tools.refinery.language.model.problem.Atom;
27import tools.refinery.language.model.problem.LogicValue;
28import tools.refinery.language.model.problem.Node;
29import tools.refinery.language.model.problem.PredicateDefinition;
30import tools.refinery.language.model.problem.Problem;
31import tools.refinery.language.model.problem.ProblemFactory;
32import tools.refinery.language.model.problem.Relation;
33import tools.refinery.language.model.problem.VariableOrNode;
34import tools.refinery.language.model.tests.ProblemTestUtil;
35import tools.refinery.language.tests.ProblemInjectorProvider;
36
37@ExtendWith(InjectionExtension.class)
38@InjectWith(ProblemInjectorProvider.class)
39class ProblemSerializerTest {
40 @Inject
41 private ResourceSet resourceSet;
42
43 @Inject
44 private ProblemTestUtil testUtil;
45
46 private Resource resource;
47
48 private Problem problem;
49
50 private Problem builtin;
51
52 @BeforeEach
53 void beforeEach() {
54 problem = ProblemFactory.eINSTANCE.createProblem();
55 resource = resourceSet.createResource(URI.createFileURI("test.problem"));
56 resource.getContents().add(problem);
57 builtin = ProblemUtil.getBuiltInLibrary(problem).get();
58 }
59
60 @ParameterizedTest
61 @MethodSource
62 void assertionTest(LogicValue value, String serializedAssertion) {
63 var pred = createPred();
64 var node = ProblemFactory.eINSTANCE.createNode();
65 node.setName("a");
66 var individualDeclaration = ProblemFactory.eINSTANCE.createIndividualDeclaration();
67 individualDeclaration.getNodes().add(node);
68 problem.getStatements().add(individualDeclaration);
69 createAssertion(pred, node, value);
70
71 assertSerializedResult("""
72 pred foo(node p).
73
74 indiv a.
75 """ + serializedAssertion + "\n");
76 }
77
78 static Stream<Arguments> assertionTest() {
79 return Stream.of(Arguments.of(LogicValue.TRUE, "foo(a)."), Arguments.of(LogicValue.FALSE, "!foo(a)."),
80 Arguments.of(LogicValue.UNKNOWN, "?foo(a)."), Arguments.of(LogicValue.ERROR, "foo(a): error."));
81 }
82
83 @Test
84 void implicitNodeTest() {
85 var pred = createPred();
86 var node = ProblemFactory.eINSTANCE.createNode();
87 node.setName("a");
88 problem.getNodes().add(node);
89 createAssertion(pred, node);
90
91 assertSerializedResult("""
92 pred foo(node p).
93
94 foo(a).
95 """);
96 }
97
98 private PredicateDefinition createPred() {
99 var pred = ProblemFactory.eINSTANCE.createPredicateDefinition();
100 pred.setName("foo");
101 var parameter = ProblemFactory.eINSTANCE.createParameter();
102 var nodeType = testUtil.findClass(builtin, "node");
103 parameter.setParameterType(nodeType);
104 parameter.setName("p");
105 pred.getParameters().add(parameter);
106 problem.getStatements().add(pred);
107 return pred;
108 }
109
110 @Test
111 void newNodeTest() {
112 var classDeclaration = ProblemFactory.eINSTANCE.createClassDeclaration();
113 classDeclaration.setName("Foo");
114 var newNode = ProblemFactory.eINSTANCE.createNode();
115 newNode.setName("new");
116 classDeclaration.setNewNode(newNode);
117 problem.getStatements().add(classDeclaration);
118 createAssertion(classDeclaration, newNode);
119
120 assertSerializedResult("""
121 class Foo.
122
123 Foo(Foo::new).
124 """);
125 }
126
127 private void createAssertion(Relation relation, Node node) {
128 createAssertion(relation, node, LogicValue.TRUE);
129 }
130
131 private void createAssertion(Relation relation, Node node, LogicValue value) {
132 var assertion = ProblemFactory.eINSTANCE.createAssertion();
133 assertion.setRelation(relation);
134 var argument = ProblemFactory.eINSTANCE.createNodeAssertionArgument();
135 argument.setNode(node);
136 assertion.getArguments().add(argument);
137 assertion.setValue(value);
138 problem.getStatements().add(assertion);
139 }
140
141 @Test
142 void implicitVariableTest() {
143 var pred = ProblemFactory.eINSTANCE.createPredicateDefinition();
144 pred.setName("foo");
145 var nodeType = testUtil.findClass(builtin, "node");
146 var parameter1 = ProblemFactory.eINSTANCE.createParameter();
147 parameter1.setParameterType(nodeType);
148 parameter1.setName("p1");
149 pred.getParameters().add(parameter1);
150 var parameter2 = ProblemFactory.eINSTANCE.createParameter();
151 parameter2.setParameterType(nodeType);
152 parameter2.setName("p2");
153 pred.getParameters().add(parameter2);
154 var conjunction = ProblemFactory.eINSTANCE.createConjunction();
155 var variable = ProblemFactory.eINSTANCE.createImplicitVariable();
156 variable.setName("q");
157 conjunction.getImplicitVariables().add(variable);
158 var equals = testUtil.reference(nodeType, "equals");
159 conjunction.getLiterals().add(createAtom(equals, parameter1, variable));
160 conjunction.getLiterals().add(createAtom(equals, variable, parameter2));
161 pred.getBodies().add(conjunction);
162 problem.getStatements().add(pred);
163
164 assertSerializedResult("""
165 pred foo(node p1, node p2) <-> equals(p1, q), equals(q, p2).
166 """);
167 }
168
169 private Atom createAtom(Relation relation, VariableOrNode variable1, VariableOrNode variable2) {
170 var atom = ProblemFactory.eINSTANCE.createAtom();
171 atom.setRelation(relation);
172 var arg1 = ProblemFactory.eINSTANCE.createVariableOrNodeArgument();
173 arg1.setVariableOrNode(variable1);
174 atom.getArguments().add(arg1);
175 var arg2 = ProblemFactory.eINSTANCE.createVariableOrNodeArgument();
176 arg2.setVariableOrNode(variable2);
177 atom.getArguments().add(arg2);
178 return atom;
179 }
180
181 @Test
182 void singletonVariableTest() {
183 var pred = ProblemFactory.eINSTANCE.createPredicateDefinition();
184 pred.setName("foo");
185 var nodeType = testUtil.findClass(builtin, "node");
186 var parameter = ProblemFactory.eINSTANCE.createParameter();
187 parameter.setParameterType(nodeType);
188 parameter.setName("p");
189 pred.getParameters().add(parameter);
190 var conjunction = ProblemFactory.eINSTANCE.createConjunction();
191 var atom = ProblemFactory.eINSTANCE.createAtom();
192 var equals = testUtil.reference(nodeType, "equals");
193 atom.setRelation(equals);
194 var arg1 = ProblemFactory.eINSTANCE.createVariableOrNodeArgument();
195 arg1.setVariableOrNode(parameter);
196 atom.getArguments().add(arg1);
197 var arg2 = ProblemFactory.eINSTANCE.createVariableOrNodeArgument();
198 var variable = ProblemFactory.eINSTANCE.createImplicitVariable();
199 variable.setName("_q");
200 arg2.setSingletonVariable(variable);
201 arg2.setVariableOrNode(variable);
202 atom.getArguments().add(arg2);
203 conjunction.getLiterals().add(atom);
204 pred.getBodies().add(conjunction);
205 problem.getStatements().add(pred);
206
207 assertSerializedResult("""
208 pred foo(node p) <-> equals(p, _q).
209 """);
210 }
211
212 private void assertSerializedResult(String expected) {
213 var outputStream = new ByteArrayOutputStream();
214 try {
215 resource.save(outputStream, Map.of());
216 } catch (IOException e) {
217 throw new AssertionError("Failed to serialize problem", e);
218 } finally {
219 try {
220 outputStream.close();
221 } catch (IOException e) {
222 // Nothing to handle in a test.
223 }
224 }
225 var problemString = outputStream.toString();
226
227 assertThat(problemString, equalTo(expected));
228 }
229}
diff --git a/subprojects/store/build.gradle b/subprojects/store/build.gradle
new file mode 100644
index 00000000..8d091a81
--- /dev/null
+++ b/subprojects/store/build.gradle
@@ -0,0 +1,9 @@
1plugins {
2 id 'refinery-java-library'
3 id 'refinery-jmh'
4}
5
6dependencies {
7 implementation libs.ecore
8 implementation libs.viatra
9}
diff --git a/subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutBenchmark.java b/subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutBenchmark.java
new file mode 100644
index 00000000..cdf3d3c8
--- /dev/null
+++ b/subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutBenchmark.java
@@ -0,0 +1,77 @@
1package tools.refinery.store.map.benchmarks;
2
3import java.util.ArrayList;
4import java.util.HashMap;
5import java.util.concurrent.TimeUnit;
6
7import org.openjdk.jmh.annotations.Benchmark;
8import org.openjdk.jmh.annotations.BenchmarkMode;
9import org.openjdk.jmh.annotations.Fork;
10import org.openjdk.jmh.annotations.Measurement;
11import org.openjdk.jmh.annotations.Mode;
12import org.openjdk.jmh.annotations.OutputTimeUnit;
13import org.openjdk.jmh.annotations.Warmup;
14import org.openjdk.jmh.infra.Blackhole;
15
16@Fork(1)
17@BenchmarkMode(Mode.AverageTime)
18@OutputTimeUnit(TimeUnit.MILLISECONDS)
19@Measurement(time = 1, timeUnit = TimeUnit.SECONDS)
20@Warmup(time = 1, timeUnit = TimeUnit.SECONDS)
21public class ImmutablePutBenchmark {
22 @Benchmark
23 public void immutablePutBenchmark(ImmutablePutExecutionPlan executionPlan, Blackhole blackhole) {
24 var sut = executionPlan.createSut();
25 for (int i = 0; i < executionPlan.nPut; i++) {
26 sut.put(executionPlan.nextKey(), executionPlan.nextValue());
27 }
28 blackhole.consume(sut);
29 }
30
31 @Benchmark
32 public void immutablePutAndCommitBenchmark(ImmutablePutExecutionPlan executionPlan, Blackhole blackhole) {
33 var sut = executionPlan.createSut();
34 for (int i = 0; i < executionPlan.nPut; i++) {
35 sut.put(executionPlan.nextKey(), executionPlan.nextValue());
36 if (i % 10 == 0) {
37 blackhole.consume(sut.commit());
38 }
39 }
40 blackhole.consume(sut);
41 }
42
43 @Benchmark
44 public void baselinePutBenchmark(ImmutablePutExecutionPlan executionPlan, Blackhole blackhole) {
45 var sut = new HashMap<Integer, String>();
46 for (int i = 0; i < executionPlan.nPut; i++) {
47 var key = executionPlan.nextKey();
48 var value = executionPlan.nextValue();
49 if (executionPlan.isDefault(value)) {
50 sut.remove(key);
51 } else {
52 sut.put(key, value);
53 }
54 }
55 blackhole.consume(sut);
56 }
57
58 @Benchmark
59 public void baselinePutAndCommitBenchmark(ImmutablePutExecutionPlan executionPlan, Blackhole blackhole) {
60 var sut = new HashMap<Integer, String>();
61 var store = new ArrayList<HashMap<Integer, String>>();
62 for (int i = 0; i < executionPlan.nPut; i++) {
63 var key = executionPlan.nextKey();
64 var value = executionPlan.nextValue();
65 if (executionPlan.isDefault(value)) {
66 sut.remove(key);
67 } else {
68 sut.put(key, value);
69 }
70 if (i % 10 == 0) {
71 store.add(new HashMap<>(sut));
72 }
73 }
74 blackhole.consume(sut);
75 blackhole.consume(store);
76 }
77}
diff --git a/subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutExecutionPlan.java b/subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutExecutionPlan.java
new file mode 100644
index 00000000..756d504e
--- /dev/null
+++ b/subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutExecutionPlan.java
@@ -0,0 +1,57 @@
1package tools.refinery.store.map.benchmarks;
2
3import java.util.Random;
4
5import tools.refinery.store.map.ContinousHashProvider;
6import tools.refinery.store.map.VersionedMapStore;
7import tools.refinery.store.map.VersionedMapStoreImpl;
8import tools.refinery.store.map.internal.VersionedMapImpl;
9import tools.refinery.store.map.tests.utils.MapTestEnvironment;
10
11import org.openjdk.jmh.annotations.Level;
12import org.openjdk.jmh.annotations.Param;
13import org.openjdk.jmh.annotations.Scope;
14import org.openjdk.jmh.annotations.Setup;
15import org.openjdk.jmh.annotations.State;
16
17@State(Scope.Benchmark)
18public class ImmutablePutExecutionPlan {
19
20 @Param({ "100", "10000" })
21 public int nPut;
22
23 @Param({ "32", "1000", "100000" })
24 public int nKeys;
25
26 @Param({ "2", "3" })
27 public int nValues;
28
29 private Random random;
30
31 private String[] values;
32
33 private ContinousHashProvider<Integer> hashProvider = MapTestEnvironment.prepareHashProvider(false);
34
35 @Setup(Level.Trial)
36 public void setUpTrial() {
37 random = new Random();
38 values = MapTestEnvironment.prepareValues(nValues);
39 }
40
41 public VersionedMapImpl<Integer, String> createSut() {
42 VersionedMapStore<Integer, String> store = new VersionedMapStoreImpl<Integer, String>(hashProvider, values[0]);
43 return (VersionedMapImpl<Integer, String>) store.createMap();
44 }
45
46 public Integer nextKey() {
47 return random.nextInt(nKeys);
48 }
49
50 public boolean isDefault(String value) {
51 return value == values[0];
52 }
53
54 public String nextValue() {
55 return values[random.nextInt(nValues)];
56 }
57}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/ContinousHashProvider.java b/subprojects/store/src/main/java/tools/refinery/store/map/ContinousHashProvider.java
new file mode 100644
index 00000000..75f1e2ab
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/ContinousHashProvider.java
@@ -0,0 +1,69 @@
1package tools.refinery.store.map;
2
3import tools.refinery.store.map.internal.Node;
4
5/**
6 * A class representing an equivalence relation for a type {@code K} with a
7 * continuous hash function.
8 *
9 * @author Oszkar Semerath
10 *
11 * @param <K> Target java type.
12 */
13public interface ContinousHashProvider<K> {
14 public static final int EFFECTIVE_BITS = Node.EFFECTIVE_BITS;
15 public static final int EFFECTIVE_BIT_MASK = (1 << (EFFECTIVE_BITS)) - 1;
16
17 /**
18 * Maximal practical depth for differentiating keys. If two keys have the same
19 * hash code until that depth, the algorithm can stop.
20 */
21 public static final int MAX_PRACTICAL_DEPTH = 500;
22
23 /**
24 * Provides a hash code for a object {@code key} with a given {@code index}. It
25 * has the following contracts:
26 * <ul>
27 * <li>If {@link #equals}{@code (key1,key2)}, then
28 * {@code getHash(key1, index) == getHash(key2, index)} for all values of
29 * {@code index}.</li>
30 * <li>If {@code getHash(key1,index) == getHash(key2, index)} for all values of
31 * {@code index}, then {@link #equals}{@code (key1, key2)}</li>
32 * <li>In current implementation, we use only the least significant
33 * {@link #EFFECTIVE_BITS}
34 * </ul>
35 * Check {@link #equals} for further details.
36 *
37 * @param key The target data object.
38 * @param index The depth of the the hash code. Needs to be non-negative.
39 * @return A hash code.
40 */
41 public int getHash(K key, int index);
42
43 public default int getEffectiveHash(K key, int index) {
44 return getHash(key, index) & EFFECTIVE_BIT_MASK;
45 }
46
47 public default int compare(K key1, K key2) {
48 if (key1.equals(key2)) {
49 return 0;
50 } else {
51 for (int i = 0; i < ContinousHashProvider.MAX_PRACTICAL_DEPTH; i++) {
52 int hash1 = getEffectiveHash(key1, i);
53 int hash2 = getEffectiveHash(key2, i);
54 for(int j = 0; j<Integer.SIZE/Node.BRANCHING_FACTOR_BITS; j++) {
55 final int factorMask = (1<<Node.BRANCHING_FACTOR_BITS)-1;
56 int hashFragment1 = (hash1>>>j*Node.BRANCHING_FACTOR_BITS) & factorMask;
57 int hashFragment2 = (hash2>>>j*Node.BRANCHING_FACTOR_BITS) & factorMask;
58 var result = Integer.compare(hashFragment1, hashFragment2);
59 if (result != 0) {
60 return result;
61 }
62 }
63 }
64 throw new IllegalArgumentException("Two different keys (" + key1 + " and " + key2
65 + ") have the same hashcode over the practical depth limitation ("
66 + ContinousHashProvider.MAX_PRACTICAL_DEPTH + ")!");
67 }
68 }
69}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/Cursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/Cursor.java
new file mode 100644
index 00000000..9c465ddc
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/Cursor.java
@@ -0,0 +1,14 @@
1package tools.refinery.store.map;
2
3import java.util.List;
4
5public interface Cursor<K,V> {
6 public K getKey();
7 public V getValue();
8 public boolean isTerminated();
9 public boolean move();
10 public boolean isDirty();
11
12 @SuppressWarnings("squid:S1452")
13 public List<VersionedMap<?,?>> getDependingMaps();
14}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/CursorAsIterator.java b/subprojects/store/src/main/java/tools/refinery/store/map/CursorAsIterator.java
new file mode 100644
index 00000000..65ae6648
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/CursorAsIterator.java
@@ -0,0 +1,57 @@
1package tools.refinery.store.map;
2
3import java.util.Iterator;
4import java.util.NoSuchElementException;
5import java.util.function.BiFunction;
6import java.util.function.BiPredicate;
7
8public class CursorAsIterator<K,V,D> implements Iterator<D> {
9 private final Cursor<K, V> internal;
10 private final BiFunction<K, V, D> entryTransformation;
11 private final BiPredicate<K,V> filtering;
12
13 D lastValidElement;
14
15 public CursorAsIterator(Cursor<K, V> internal, BiFunction<K, V, D> entryTransformation, BiPredicate<K,V> filtering) {
16 this.internal = internal;
17 this.entryTransformation = entryTransformation;
18 this.filtering = filtering;
19
20 moveToNext();
21 }
22 public CursorAsIterator(Cursor<K, V> internal, BiFunction<K, V, D> entryTransformation) {
23 this.internal = internal;
24 this.entryTransformation = entryTransformation;
25 this.filtering = ((k,v)->true);
26
27 moveToNext();
28 }
29
30 private void moveToNext() {
31 internal.move();
32 while(!internal.isTerminated() && !filtering.test(internal.getKey(), internal.getValue())) {
33 internal.move();
34 }
35 if(!internal.isTerminated()) {
36 lastValidElement = entryTransformation.apply(internal.getKey(), internal.getValue());
37 }
38 }
39
40
41 @Override
42 public boolean hasNext() {
43 return !internal.isTerminated();
44 }
45 @Override
46 public D next() {
47 if(hasNext()) {
48 D last = lastValidElement;
49 moveToNext();
50 return last;
51 } else {
52 throw new NoSuchElementException();
53 }
54
55 }
56
57}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/DiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/DiffCursor.java
new file mode 100644
index 00000000..701f3ec8
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/DiffCursor.java
@@ -0,0 +1,6 @@
1package tools.refinery.store.map;
2
3public interface DiffCursor<K, V> extends Cursor<K,V> {
4 public V getFromValue();
5 public V getToValue();
6} \ No newline at end of file
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/MapAsIterable.java b/subprojects/store/src/main/java/tools/refinery/store/map/MapAsIterable.java
new file mode 100644
index 00000000..6b986732
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/MapAsIterable.java
@@ -0,0 +1,26 @@
1package tools.refinery.store.map;
2
3import java.util.Iterator;
4import java.util.function.BiFunction;
5import java.util.function.BiPredicate;
6
7public class MapAsIterable<K,V,D> implements Iterable<D> {
8 private final VersionedMap<K, V> internal;
9 private final BiFunction<K, V, D> entryTransformation;
10 private final BiPredicate<K,V> filtering;
11
12 public MapAsIterable(VersionedMap<K, V> internal, BiFunction<K, V, D> entryTransformation, BiPredicate<K,V> filtering) {
13 this.internal = internal;
14 this.entryTransformation = entryTransformation;
15 this.filtering = filtering;
16 }
17 public MapAsIterable(VersionedMap<K, V> internal, BiFunction<K, V, D> entryTransformation) {
18 this.internal = internal;
19 this.entryTransformation = entryTransformation;
20 this.filtering = ((k,v)->true);
21 }
22 @Override
23 public Iterator<D> iterator() {
24 return new CursorAsIterator<>(internal.getAll(), entryTransformation, filtering);
25 }
26}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/Versioned.java b/subprojects/store/src/main/java/tools/refinery/store/map/Versioned.java
new file mode 100644
index 00000000..6a23e9d5
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/Versioned.java
@@ -0,0 +1,7 @@
1package tools.refinery.store.map;
2
3public interface Versioned {
4 public long commit();
5 //maybe revert()?
6 public void restore(long state);
7}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java
new file mode 100644
index 00000000..a8a64d08
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java
@@ -0,0 +1,13 @@
1package tools.refinery.store.map;
2
3public interface VersionedMap<K,V> extends Versioned{
4 public V get(K key);
5 public Cursor<K,V> getAll();
6
7 public V put(K key, V value);
8 public void putAll(Cursor<K,V> cursor);
9
10 public long getSize();
11
12 public DiffCursor<K,V> getDiffCursor(long state);
13}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java
new file mode 100644
index 00000000..a8d7fb1a
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java
@@ -0,0 +1,14 @@
1package tools.refinery.store.map;
2
3import java.util.Set;
4
5public interface VersionedMapStore<K, V> {
6
7 public VersionedMap<K, V> createMap();
8
9 public VersionedMap<K, V> createMap(long state);
10
11 public Set<Long> getStates();
12
13 public DiffCursor<K,V> getDiffCursor(long fromState, long toState);
14} \ No newline at end of file
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreConfiguration.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreConfiguration.java
new file mode 100644
index 00000000..723e5ec4
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreConfiguration.java
@@ -0,0 +1,48 @@
1package tools.refinery.store.map;
2
3public class VersionedMapStoreConfiguration {
4
5 public VersionedMapStoreConfiguration() {
6
7 }
8 public VersionedMapStoreConfiguration(boolean immutableWhenCommiting, boolean sharedNodeCacheInStore,
9 boolean sharedNodeCacheInStoreGroups) {
10 super();
11 this.immutableWhenCommiting = immutableWhenCommiting;
12 this.sharedNodeCacheInStore = sharedNodeCacheInStore;
13 this.sharedNodeCacheInStoreGroups = sharedNodeCacheInStoreGroups;
14 }
15
16 /**
17 * If true root is replaced with immutable node when committed. Frees up memory
18 * by releasing immutable nodes, but it may decrease performance by recreating
19 * immutable nodes upon changes (some evidence).
20 */
21 private boolean immutableWhenCommiting = true;
22 public boolean isImmutableWhenCommiting() {
23 return immutableWhenCommiting;
24 }
25
26 /**
27 * If true, all subnodes are cached within a {@link VersionedMapStore}. It
28 * decreases the memory requirements. It may increase performance by discovering
29 * existing immutable copy of a node (some evidence). Additional overhead may
30 * decrease performance (no example found). The option permits the efficient
31 * implementation of version deletion.
32 */
33 private boolean sharedNodeCacheInStore = true;
34 public boolean isSharedNodeCacheInStore() {
35 return sharedNodeCacheInStore;
36 }
37
38 /**
39 * If true, all subnodes are cached within a group of
40 * {@link VersionedMapStoreImpl#createSharedVersionedMapStores(int, ContinousHashProvider, Object, VersionedMapStoreConfiguration)}.
41 * If {@link VersionedMapStoreConfiguration#sharedNodeCacheInStore} is
42 * <code>false</code>, then it has currently no impact.
43 */
44 private boolean sharedNodeCacheInStoreGroups = true;
45 public boolean isSharedNodeCacheInStoreGroups() {
46 return sharedNodeCacheInStoreGroups;
47 }
48}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreImpl.java
new file mode 100644
index 00000000..a626a5e8
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreImpl.java
@@ -0,0 +1,135 @@
1package tools.refinery.store.map;
2
3import java.util.ArrayList;
4import java.util.Arrays;
5import java.util.Collections;
6import java.util.HashMap;
7import java.util.HashSet;
8import java.util.List;
9import java.util.Map;
10import java.util.Set;
11
12import tools.refinery.store.map.internal.ImmutableNode;
13import tools.refinery.store.map.internal.MapDiffCursor;
14import tools.refinery.store.map.internal.Node;
15import tools.refinery.store.map.internal.VersionedMapImpl;
16
17public class VersionedMapStoreImpl<K, V> implements VersionedMapStore<K, V> {
18 // Configuration
19 private final boolean immutableWhenCommiting;
20
21 // Static data
22 protected final ContinousHashProvider<K> hashProvider;
23 protected final V defaultValue;
24
25 // Dynamic data
26 protected final Map<Long, ImmutableNode<K, V>> states = new HashMap<>();
27 protected final Map<Node<K, V>, ImmutableNode<K, V>> nodeCache;
28 protected long nextID = 0;
29
30 public VersionedMapStoreImpl(ContinousHashProvider<K> hashProvider, V defaultValue,
31 VersionedMapStoreConfiguration config) {
32 this.immutableWhenCommiting = config.isImmutableWhenCommiting();
33 this.hashProvider = hashProvider;
34 this.defaultValue = defaultValue;
35 if (config.isSharedNodeCacheInStore()) {
36 nodeCache = new HashMap<>();
37 } else {
38 nodeCache = null;
39 }
40 }
41
42 private VersionedMapStoreImpl(ContinousHashProvider<K> hashProvider, V defaultValue,
43 Map<Node<K, V>, ImmutableNode<K, V>> nodeCache, VersionedMapStoreConfiguration config) {
44 this.immutableWhenCommiting = config.isImmutableWhenCommiting();
45 this.hashProvider = hashProvider;
46 this.defaultValue = defaultValue;
47 this.nodeCache = nodeCache;
48 }
49
50 public VersionedMapStoreImpl(ContinousHashProvider<K> hashProvider, V defaultValue) {
51 this(hashProvider, defaultValue, new VersionedMapStoreConfiguration());
52 }
53
54 public static <K, V> List<VersionedMapStore<K, V>> createSharedVersionedMapStores(int amount,
55 ContinousHashProvider<K> hashProvider, V defaultValue,
56 VersionedMapStoreConfiguration config) {
57 List<VersionedMapStore<K, V>> result = new ArrayList<>(amount);
58 if (config.isSharedNodeCacheInStoreGroups()) {
59 Map<Node<K, V>, ImmutableNode<K, V>> nodeCache;
60 if (config.isSharedNodeCacheInStore()) {
61 nodeCache = new HashMap<>();
62 } else {
63 nodeCache = null;
64 }
65 for (int i = 0; i < amount; i++) {
66 result.add(new VersionedMapStoreImpl<>(hashProvider, defaultValue, nodeCache, config));
67 }
68 } else {
69 for (int i = 0; i < amount; i++) {
70 result.add(new VersionedMapStoreImpl<>(hashProvider, defaultValue, config));
71 }
72 }
73 return result;
74 }
75
76 public static <K, V> List<VersionedMapStore<K, V>> createSharedVersionedMapStores(int amount,
77 ContinousHashProvider<K> hashProvider, V defaultValue) {
78 return createSharedVersionedMapStores(amount, hashProvider, defaultValue, new VersionedMapStoreConfiguration());
79 }
80
81 @Override
82 public synchronized Set<Long> getStates() {
83 return new HashSet<>(states.keySet());
84 }
85
86 @Override
87 public VersionedMap<K, V> createMap() {
88 return new VersionedMapImpl<>(this, hashProvider, defaultValue);
89 }
90
91 @Override
92 public VersionedMap<K, V> createMap(long state) {
93 ImmutableNode<K, V> data = revert(state);
94 return new VersionedMapImpl<>(this, hashProvider, defaultValue, data);
95 }
96
97
98 public synchronized ImmutableNode<K, V> revert(long state) {
99 if (states.containsKey(state)) {
100 return states.get(state);
101 } else {
102 ArrayList<Long> existingKeys = new ArrayList<>(states.keySet());
103 Collections.sort(existingKeys);
104 throw new IllegalArgumentException("Store does not contain state " + state + "! Avaliable states: "
105 + Arrays.toString(existingKeys.toArray()));
106 }
107 }
108
109 public synchronized long commit(Node<K, V> data, VersionedMapImpl<K, V> mapToUpdateRoot) {
110 ImmutableNode<K, V> immutable;
111 if (data != null) {
112 immutable = data.toImmutable(this.nodeCache);
113 } else {
114 immutable = null;
115 }
116
117 if (nextID == Long.MAX_VALUE)
118 throw new IllegalStateException("Map store run out of Id-s");
119 long id = nextID++;
120 this.states.put(id, immutable);
121 if (this.immutableWhenCommiting) {
122 mapToUpdateRoot.setRoot(immutable);
123 }
124 return id;
125 }
126
127 @Override
128 public DiffCursor<K, V> getDiffCursor(long fromState, long toState) {
129 VersionedMap<K, V> map1 = createMap(fromState);
130 VersionedMap<K, V> map2 = createMap(toState);
131 Cursor<K, V> cursor1 = map1.getAll();
132 Cursor<K, V> cursor2 = map2.getAll();
133 return new MapDiffCursor<>(this.hashProvider, this.defaultValue, cursor1, cursor2);
134 }
135}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/HashClash.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/HashClash.java
new file mode 100644
index 00000000..5402ed4a
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/HashClash.java
@@ -0,0 +1,18 @@
1package tools.refinery.store.map.internal;
2
3enum HashClash {
4 /**
5 * Not stuck.
6 */
7 NONE,
8
9 /**
10 * Clashed, next we should return the key of cursor 1.
11 */
12 STUCK_CURSOR_1,
13
14 /**
15 * Clashed, next we should return the key of cursor 2.
16 */
17 STUCK_CURSOR_2
18}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java
new file mode 100644
index 00000000..f68734ab
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java
@@ -0,0 +1,378 @@
1package tools.refinery.store.map.internal;
2
3import java.util.Arrays;
4import java.util.Map;
5
6import tools.refinery.store.map.ContinousHashProvider;
7
8public class ImmutableNode<K, V> extends Node<K, V> {
9 /**
10 * Bitmap defining the stored key and values.
11 */
12 final int dataMap;
13 /**
14 * Bitmap defining the positions of further nodes.
15 */
16 final int nodeMap;
17 /**
18 * Stores Keys, Values, and subnodes. Structure: (K,V)*,NODE; NODES are stored
19 * backwards.
20 */
21 final Object[] content;
22
23 /**
24 * Hash code derived from immutable hash code
25 */
26 final int precalculatedHash;
27
28 private ImmutableNode(int dataMap, int nodeMap, Object[] content, int precalculatedHash) {
29 super();
30 this.dataMap = dataMap;
31 this.nodeMap = nodeMap;
32 this.content = content;
33 this.precalculatedHash = precalculatedHash;
34 }
35
36 /**
37 * Constructor that copies a mutable node to an immutable.
38 *
39 * @param node A mutable node.
40 * @param cache A cache of existing immutable nodes. It can be used to search
41 * and place reference immutable nodes. It can be null, if no cache
42 * available.
43 * @return an immutable version of the input node.
44 */
45 static <K, V> ImmutableNode<K, V> constructImmutable(MutableNode<K, V> node,
46 Map<Node<K, V>, ImmutableNode<K, V>> cache) {
47 // 1. try to return from cache
48 if (cache != null) {
49 ImmutableNode<K, V> cachedResult = cache.get(node);
50 if (cachedResult != null) {
51 // 1.1 Already cached, return from cache.
52 return cachedResult;
53 }
54 }
55
56 // 2. otherwise construct a new ImmutableNode
57 int size = 0;
58 for (int i = 0; i < node.content.length; i++) {
59 if (node.content[i] != null) {
60 size++;
61 }
62 }
63
64 int datas = 0;
65 int nodes = 0;
66 int resultDataMap = 0;
67 int resultNodeMap = 0;
68 final Object[] resultContent = new Object[size];
69 int bitposition = 1;
70 for (int i = 0; i < FACTOR; i++) {
71 Object key = node.content[i * 2];
72 if (key != null) {
73 resultDataMap |= bitposition;
74 resultContent[datas * 2] = key;
75 resultContent[datas * 2 + 1] = node.content[i * 2 + 1];
76 datas++;
77 } else {
78 @SuppressWarnings("unchecked")
79 var subnode = (Node<K, V>) node.content[i * 2 + 1];
80 if (subnode != null) {
81 ImmutableNode<K, V> immutableSubnode = subnode.toImmutable(cache);
82 resultNodeMap |= bitposition;
83 resultContent[size - 1 - nodes] = immutableSubnode;
84 nodes++;
85 }
86 }
87 bitposition <<= 1;
88 }
89 final int resultHash = node.hashCode();
90 var newImmutable = new ImmutableNode<K, V>(resultDataMap, resultNodeMap, resultContent, resultHash);
91
92 // 3. save new immutable.
93 if (cache != null) {
94 cache.put(newImmutable, newImmutable);
95 }
96 return newImmutable;
97 }
98
99 private int index(int bitmap, int bitpos) {
100 return Integer.bitCount(bitmap & (bitpos - 1));
101 }
102
103 @Override
104 public V getValue(K key, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth) {
105 int selectedHashFragment = hashFragment(hash, shiftDepth(depth));
106 int bitposition = 1 << selectedHashFragment;
107 // If the key is stored as a data
108 if ((dataMap & bitposition) != 0) {
109 int keyIndex = 2 * index(dataMap, bitposition);
110 @SuppressWarnings("unchecked")
111 K keyCandidate = (K) content[keyIndex];
112 if (keyCandidate.equals(key)) {
113 @SuppressWarnings("unchecked")
114 V value = (V) content[keyIndex + 1];
115 return value;
116 } else {
117 return defaultValue;
118 }
119 }
120 // the key is stored as a node
121 else if ((nodeMap & bitposition) != 0) {
122 int keyIndex = content.length - 1 - index(nodeMap, bitposition);
123 @SuppressWarnings("unchecked")
124 var subNode = (ImmutableNode<K, V>) content[keyIndex];
125 int newDepth = depth + 1;
126 int newHash = newHash(hashProvider, key, hash, newDepth);
127 return subNode.getValue(key, hashProvider, defaultValue, newHash, newDepth);
128 }
129 // the key is not stored at all
130 else {
131 return defaultValue;
132 }
133 }
134
135 @Override
136 public Node<K, V> putValue(K key, V value, OldValueBox<V> oldValue, ContinousHashProvider<? super K> hashProvider,
137 V defaultValue, int hash, int depth) {
138 int selectedHashFragment = hashFragment(hash, shiftDepth(depth));
139 int bitposition = 1 << selectedHashFragment;
140 if ((dataMap & bitposition) != 0) {
141 int keyIndex = 2 * index(dataMap, bitposition);
142 @SuppressWarnings("unchecked")
143 K keyCandidate = (K) content[keyIndex];
144 if (keyCandidate.equals(key)) {
145 if (value == defaultValue) {
146 // delete
147 MutableNode<K, V> mutable = this.toMutable();
148 return mutable.removeEntry(selectedHashFragment, oldValue);
149 } else if (value == content[keyIndex + 1]) {
150 // dont change
151 oldValue.setOldValue(value);
152 return this;
153 } else {
154 // update existing value
155 MutableNode<K, V> mutable = this.toMutable();
156 return mutable.updateValue(value, oldValue, selectedHashFragment);
157 }
158 } else {
159 if (value == defaultValue) {
160 // dont change
161 oldValue.setOldValue(defaultValue);
162 return this;
163 } else {
164 // add new key + value
165 MutableNode<K, V> mutable = this.toMutable();
166 return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth);
167 }
168 }
169 } else if ((nodeMap & bitposition) != 0) {
170 int keyIndex = content.length - 1 - index(nodeMap, bitposition);
171 @SuppressWarnings("unchecked")
172 var subNode = (ImmutableNode<K, V>) content[keyIndex];
173 int newDepth = depth + 1;
174 int newHash = newHash(hashProvider, key, hash, newDepth);
175 var newsubNode = subNode.putValue(key, value, oldValue, hashProvider, defaultValue, newHash, newDepth);
176
177 if (subNode == newsubNode) {
178 // nothing changed
179 return this;
180 } else {
181 MutableNode<K, V> mutable = toMutable();
182 return mutable.updateWithSubNode(selectedHashFragment, newsubNode, value.equals(defaultValue));
183 }
184 } else {
185 // add new key + value
186 MutableNode<K, V> mutable = this.toMutable();
187 return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth);
188 }
189 }
190
191 @Override
192 public long getSize() {
193 int result = Integer.bitCount(this.dataMap);
194 for (int subnodeIndex = 0; subnodeIndex < Integer.bitCount(this.nodeMap); subnodeIndex++) {
195 @SuppressWarnings("unchecked")
196 var subnode = (ImmutableNode<K, V>) this.content[this.content.length - 1 - subnodeIndex];
197 result += subnode.getSize();
198 }
199 return result;
200 }
201
202 @Override
203 protected MutableNode<K, V> toMutable() {
204 return new MutableNode<>(this);
205 }
206
207 @Override
208 public ImmutableNode<K, V> toImmutable(Map<Node<K, V>, ImmutableNode<K, V>> cache) {
209 return this;
210 }
211
212 @Override
213 protected MutableNode<K, V> isMutable() {
214 return null;
215 }
216
217 @SuppressWarnings("unchecked")
218 @Override
219 boolean moveToNext(MapCursor<K, V> cursor) {
220 // 1. try to move to data
221 int datas = Integer.bitCount(this.dataMap);
222 if (cursor.dataIndex != MapCursor.INDEX_FINISH) {
223 int newDataIndex = cursor.dataIndex + 1;
224 if (newDataIndex < datas) {
225 cursor.dataIndex = newDataIndex;
226 cursor.key = (K) this.content[newDataIndex * 2];
227 cursor.value = (V) this.content[newDataIndex * 2 + 1];
228 return true;
229 } else {
230 cursor.dataIndex = MapCursor.INDEX_FINISH;
231 }
232 }
233
234 // 2. look inside the subnodes
235 int nodes = Integer.bitCount(this.nodeMap);
236 int newNodeIndex = cursor.nodeIndexStack.peek() + 1;
237 if (newNodeIndex < nodes) {
238 // 2.1 found next subnode, move down to the subnode
239 Node<K, V> subnode = (Node<K, V>) this.content[this.content.length - 1 - newNodeIndex];
240 cursor.dataIndex = MapCursor.INDEX_START;
241 cursor.nodeIndexStack.pop();
242 cursor.nodeIndexStack.push(newNodeIndex);
243 cursor.nodeIndexStack.push(MapCursor.INDEX_START);
244 cursor.nodeStack.push(subnode);
245 return subnode.moveToNext(cursor);
246 } else {
247 // 3. no subnode found, move up
248 cursor.nodeStack.pop();
249 cursor.nodeIndexStack.pop();
250 if (!cursor.nodeStack.isEmpty()) {
251 Node<K, V> supernode = cursor.nodeStack.peek();
252 return supernode.moveToNext(cursor);
253 } else {
254 cursor.key = null;
255 cursor.value = null;
256 return false;
257 }
258 }
259 }
260
261 @Override
262 public void prettyPrint(StringBuilder builder, int depth, int code) {
263 for (int i = 0; i < depth; i++) {
264 builder.append("\t");
265 }
266 if (code >= 0) {
267 builder.append(code);
268 builder.append(":");
269 }
270 builder.append("Immutable(");
271 boolean hadContent = false;
272 int dataMask = 1;
273 for (int i = 0; i < FACTOR; i++) {
274 if ((dataMask & dataMap) != 0) {
275 if (hadContent) {
276 builder.append(",");
277 }
278 builder.append(i);
279 builder.append(":[");
280 builder.append(content[2 * index(dataMap, dataMask)].toString());
281 builder.append("]->[");
282 builder.append(content[2 * index(dataMap, dataMask) + 1].toString());
283 builder.append("]");
284 hadContent = true;
285 }
286 dataMask <<= 1;
287 }
288 builder.append(")");
289 int nodeMask = 1;
290 for (int i = 0; i < FACTOR; i++) {
291 if ((nodeMask & nodeMap) != 0) {
292 @SuppressWarnings("unchecked")
293 Node<K, V> subNode = (Node<K, V>) content[content.length - 1 - index(nodeMap, nodeMask)];
294 builder.append("\n");
295 subNode.prettyPrint(builder, depth + 1, i);
296 }
297 nodeMask <<= 1;
298 }
299 }
300
301 @Override
302 public void checkIntegrity(ContinousHashProvider<? super K> hashProvider, V defaultValue, int depth) {
303 if (depth > 0) {
304 boolean orphaned = Integer.bitCount(dataMap) == 1 && nodeMap == 0;
305 if (orphaned) {
306 throw new IllegalStateException("Orphaned node! " + dataMap + ": " + content[0]);
307 }
308 }
309 // check the place of data
310
311 // check subnodes
312 for (int i = 0; i < Integer.bitCount(nodeMap); i++) {
313 @SuppressWarnings("unchecked")
314 var subnode = (Node<K, V>) this.content[this.content.length - 1 - i];
315 if (!(subnode instanceof ImmutableNode<?, ?>)) {
316 throw new IllegalStateException("Immutable node contains mutable subnodes!");
317 } else {
318 subnode.checkIntegrity(hashProvider, defaultValue, depth + 1);
319 }
320 }
321 }
322
323 @Override
324 public int hashCode() {
325 return this.precalculatedHash;
326 }
327
328 @Override
329 public boolean equals(Object obj) {
330 if (this == obj)
331 return true;
332 if (obj == null)
333 return false;
334 if (obj instanceof ImmutableNode<?, ?> other) {
335 return precalculatedHash == other.precalculatedHash && dataMap == other.dataMap && nodeMap == other.nodeMap
336 && Arrays.deepEquals(content, other.content);
337 } else if (obj instanceof MutableNode<?, ?> mutableObj) {
338 return ImmutableNode.compareImmutableMutable(this, mutableObj);
339 } else {
340 return false;
341 }
342 }
343
344 public static boolean compareImmutableMutable(ImmutableNode<?, ?> immutable, MutableNode<?, ?> mutable) {
345 int datas = 0;
346 int nodes = 0;
347 final int immutableLength = immutable.content.length;
348 for (int i = 0; i < FACTOR; i++) {
349 Object key = mutable.content[i * 2];
350 // For each key candidate
351 if (key != null) {
352 // Check whether a new Key-Value pair can fit into the immutable container
353 if (datas * 2 + nodes + 2 <= immutableLength) {
354 if (!immutable.content[datas * 2].equals(key)
355 || !immutable.content[datas * 2 + 1].equals(mutable.content[i * 2 + 1])) {
356 return false;
357 }
358 } else
359 return false;
360 datas++;
361 } else {
362 var mutableSubnode = (Node<?, ?>) mutable.content[i * 2 + 1];
363 if (mutableSubnode != null) {
364 if (datas * 2 + nodes + 1 <= immutableLength) {
365 Object immutableSubnode = immutable.content[immutableLength - 1 - nodes];
366 if (!mutableSubnode.equals(immutableSubnode)) {
367 return false;
368 }
369 nodes++;
370 } else {
371 return false;
372 }
373 }
374 }
375 }
376 return true;
377 }
378}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java
new file mode 100644
index 00000000..b90f5b71
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java
@@ -0,0 +1,131 @@
1package tools.refinery.store.map.internal;
2
3import java.util.ArrayDeque;
4import java.util.ConcurrentModificationException;
5import java.util.Iterator;
6import java.util.List;
7
8import tools.refinery.store.map.Cursor;
9import tools.refinery.store.map.VersionedMap;
10
11public class MapCursor<K,V> implements Cursor<K,V> {
12 // Constants
13 static final int INDEX_START = -1;
14 static final int INDEX_FINISH = -2;
15
16 // Tree stack
17 ArrayDeque<Node<K,V>> nodeStack;
18 ArrayDeque<Integer> nodeIndexStack;
19 int dataIndex;
20
21 // Values
22 K key;
23 V value;
24
25 // Hash code for checking concurrent modifications
26 final VersionedMap<K,V> map;
27 final int creationHash;
28
29 public MapCursor(Node<K, V> root, VersionedMap<K,V> map) {
30 // Initializing tree stack
31 super();
32 this.nodeStack = new ArrayDeque<>();
33 this.nodeIndexStack = new ArrayDeque<>();
34 if(root != null) {
35 this.nodeStack.add(root);
36 this.nodeIndexStack.push(INDEX_START);
37 }
38
39 this.dataIndex = INDEX_START;
40
41 // Initializing cache
42 this.key = null;
43 this.value = null;
44
45 // Initializing state
46 this.map=map;
47 this.creationHash = map.hashCode();
48 }
49
50 public K getKey() {
51 return key;
52 }
53
54 public V getValue() {
55 return value;
56 }
57
58 public boolean isTerminated() {
59 return this.nodeStack.isEmpty();
60 }
61
62 public boolean move() {
63 if(isDirty()) {
64 throw new ConcurrentModificationException();
65 }
66 if(!isTerminated()) {
67 boolean result = this.nodeStack.peek().moveToNext(this);
68 if(this.nodeIndexStack.size() != this.nodeStack.size()) {
69 throw new IllegalArgumentException("Node stack is corrupted by illegal moves!");
70 }
71 return result;
72 }
73 return false;
74 }
75 public boolean skipCurrentNode() {
76 nodeStack.pop();
77 nodeIndexStack.pop();
78 dataIndex = INDEX_FINISH;
79 return move();
80 }
81 @Override
82 public boolean isDirty() {
83 return this.map.hashCode() != this.creationHash;
84 }
85 @Override
86 public List<VersionedMap<?, ?>> getDependingMaps() {
87 return List.of(this.map);
88 }
89
90 public static <K,V> boolean sameSubnode(MapCursor<K,V> cursor1, MapCursor<K,V> cursor2) {
91 Node<K, V> nodeOfCursor1 = cursor1.nodeStack.peek();
92 Node<K, V> nodeOfCursor2 = cursor2.nodeStack.peek();
93 if(nodeOfCursor1 != null && nodeOfCursor2 != null) {
94 return nodeOfCursor1.equals(nodeOfCursor2);
95 } else {
96 return false;
97 }
98 }
99
100 /**
101 *
102 * @param <K>
103 * @param <V>
104 * @param cursor1
105 * @param cursor2
106 * @return Positive number if cursor 1 is behind, negative number if cursor 2 is behind, and 0 if they are at the same position.
107 */
108 public static <K,V> int compare(MapCursor<K,V> cursor1, MapCursor<K,V> cursor2) {
109 // two cursors are equally deep
110 Iterator<Integer> stack1 = cursor1.nodeIndexStack.descendingIterator();
111 Iterator<Integer> stack2 = cursor2.nodeIndexStack.descendingIterator();
112 if(stack1.hasNext()) {
113 if(!stack2.hasNext()) {
114 // stack 2 has no more element, thus stack 1 is deeper
115 return 1;
116 }
117 int val1 = stack1.next();
118 int val2 = stack2.next();
119 if(val1 < val2) {
120 return -1;
121 } else if(val2 < val1) {
122 return 1;
123 }
124 }
125 if(stack2.hasNext()) {
126 // stack 2 has more element, thus stack 2 is deeper
127 return 1;
128 }
129 return Integer.compare(cursor1.dataIndex, cursor2.dataIndex);
130 }
131}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java
new file mode 100644
index 00000000..42333635
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java
@@ -0,0 +1,221 @@
1package tools.refinery.store.map.internal;
2
3import java.util.List;
4import java.util.stream.Stream;
5
6import tools.refinery.store.map.ContinousHashProvider;
7import tools.refinery.store.map.Cursor;
8import tools.refinery.store.map.DiffCursor;
9import tools.refinery.store.map.VersionedMap;
10
11/**
12 * A cursor representing the difference between two states of a map.
13 *
14 * @author Oszkar Semerath
15 *
16 */
17public class MapDiffCursor<K, V> implements DiffCursor<K, V>, Cursor<K, V> {
18 /**
19 * Default value representing missing elements.
20 */
21 private V defaultValue;
22 private MapCursor<K, V> cursor1;
23 private MapCursor<K, V> cursor2;
24 private ContinousHashProvider<? super K> hashProvider;
25
26 // Values
27 private K key;
28 private V fromValue;
29 private V toValue;
30
31 // State
32 /**
33 * Positive number if cursor 1 is behind, negative number if cursor 2 is behind,
34 * and 0 if they are at the same position.
35 */
36 private int cursorRelation;
37 private HashClash hashClash = HashClash.NONE;
38
39 public MapDiffCursor(ContinousHashProvider<? super K> hashProvider, V defaultValue, Cursor<K, V> cursor1,
40 Cursor<K, V> cursor2) {
41 super();
42 this.hashProvider = hashProvider;
43 this.defaultValue = defaultValue;
44 this.cursor1 = (MapCursor<K, V>) cursor1;
45 this.cursor2 = (MapCursor<K, V>) cursor2;
46 }
47
48 @Override
49 public K getKey() {
50 return key;
51 }
52
53 @Override
54 public V getFromValue() {
55 return fromValue;
56 }
57
58 @Override
59 public V getToValue() {
60 return toValue;
61 }
62
63 @Override
64 public V getValue() {
65 return getToValue();
66 }
67
68 public boolean isTerminated() {
69 return cursor1.isTerminated() && cursor2.isTerminated();
70 }
71
72 @Override
73 public boolean isDirty() {
74 return this.cursor1.isDirty() || this.cursor2.isDirty();
75 }
76
77 @Override
78 public List<VersionedMap<?, ?>> getDependingMaps() {
79 return Stream.concat(cursor1.getDependingMaps().stream(), cursor2.getDependingMaps().stream()).toList();
80 }
81
82 protected void updateState() {
83 if (!isTerminated()) {
84 this.cursorRelation = MapCursor.compare(cursor1, cursor2);
85 if (cursorRelation > 0 || cursor2.isTerminated()) {
86 this.key = cursor1.getKey();
87 this.fromValue = cursor1.getValue();
88 this.toValue = defaultValue;
89 } else if (cursorRelation < 0 || cursor1.isTerminated()) {
90 this.key = cursor2.getKey();
91 this.fromValue = defaultValue;
92 this.toValue = cursor1.getValue();
93 } else {
94 // cursor1 = cursor2
95 if (cursor1.getKey().equals(cursor2.getKey())) {
96 this.key = cursor1.getKey();
97 this.fromValue = cursor1.getValue();
98 this.toValue = defaultValue;
99 } else {
100 resolveHashClashWithFirstEntry();
101 }
102 }
103 }
104 }
105
106 protected void resolveHashClashWithFirstEntry() {
107 int compareResult = this.hashProvider.compare(cursor1.key, cursor2.key);
108 if (compareResult < 0) {
109 this.hashClash = HashClash.STUCK_CURSOR_2;
110 this.cursorRelation = 0;
111 this.key = cursor1.key;
112 this.fromValue = cursor1.value;
113 this.toValue = defaultValue;
114 } else if (compareResult > 0) {
115 this.hashClash = HashClash.STUCK_CURSOR_1;
116 this.cursorRelation = 0;
117 this.key = cursor2.key;
118 this.fromValue = defaultValue;
119 this.toValue = cursor2.value;
120 } else {
121 throw new IllegalArgumentException("Inconsistent compare result for diffcursor");
122 }
123 }
124
125 protected boolean isInHashClash() {
126 return this.hashClash != HashClash.NONE;
127 }
128
129 protected void resolveHashClashWithSecondEntry() {
130 switch (this.hashClash) {
131 case STUCK_CURSOR_1:
132 this.hashClash = HashClash.NONE;
133 this.cursorRelation = 0;
134 this.key = cursor1.key;
135 this.fromValue = cursor1.value;
136 this.toValue = defaultValue;
137 break;
138 case STUCK_CURSOR_2:
139 this.hashClash = HashClash.NONE;
140 this.cursorRelation = 0;
141 this.key = cursor2.key;
142 this.fromValue = defaultValue;
143 this.toValue = cursor2.value;
144 break;
145 default:
146 throw new IllegalArgumentException("Inconsistent compare result for diffcursor");
147 }
148 }
149
150 protected boolean sameValues() {
151 if (this.fromValue == null) {
152 return this.toValue == null;
153 } else {
154 return this.fromValue.equals(this.toValue);
155 }
156 }
157
158 protected boolean moveOne() {
159 if (isTerminated()) {
160 return false;
161 }
162 if (this.cursorRelation > 0 || cursor2.isTerminated()) {
163 return cursor1.move();
164 } else if (this.cursorRelation < 0 || cursor1.isTerminated()) {
165 return cursor2.move();
166 } else {
167 boolean moved1 = cursor1.move();
168 boolean moved2 = cursor2.move();
169 return moved1 && moved2;
170 }
171 }
172
173 private boolean skipNode() {
174 if (isTerminated()) {
175 throw new IllegalStateException("DiffCursor tries to skip when terminated!");
176 }
177 boolean update1 = cursor1.skipCurrentNode();
178 boolean update2 = cursor2.skipCurrentNode();
179 updateState();
180 return update1 && update2;
181 }
182
183 protected boolean moveToConsistentState() {
184 if (!isTerminated()) {
185 boolean changed;
186 boolean lastResult = true;
187 do {
188 changed = false;
189 if (MapCursor.sameSubnode(cursor1, cursor2)) {
190 lastResult = skipNode();
191 changed = true;
192 }
193 if (sameValues()) {
194 lastResult = moveOne();
195 changed = true;
196 }
197 updateState();
198 } while (changed && !isTerminated());
199 return lastResult;
200 } else {
201 return false;
202 }
203 }
204
205 public boolean move() {
206 if (!isTerminated()) {
207 if (isInHashClash()) {
208 this.resolveHashClashWithSecondEntry();
209 return true;
210 } else {
211 if (moveOne()) {
212 return moveToConsistentState();
213 } else {
214 return false;
215 }
216 }
217
218 } else
219 return false;
220 }
221}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java
new file mode 100644
index 00000000..54853010
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java
@@ -0,0 +1,454 @@
1package tools.refinery.store.map.internal;
2
3import java.util.Arrays;
4import java.util.Map;
5
6import tools.refinery.store.map.ContinousHashProvider;
7
8public class MutableNode<K, V> extends Node<K, V> {
9 int cachedHash;
10 protected Object[] content;
11
12 protected MutableNode() {
13 this.content = new Object[2 * FACTOR];
14 updateHash();
15 }
16
17 public static <K, V> MutableNode<K, V> initialize(K key, V value, ContinousHashProvider<? super K> hashProvider,
18 V defaultValue) {
19 if (value == defaultValue) {
20 return null;
21 } else {
22 int hash = hashProvider.getHash(key, 0);
23 int fragment = hashFragment(hash, 0);
24 MutableNode<K, V> res = new MutableNode<>();
25 res.content[2 * fragment] = key;
26 res.content[2 * fragment + 1] = value;
27 res.updateHash();
28 return res;
29 }
30 }
31
32 /**
33 * Constructs a {@link MutableNode} as a copy of an {@link ImmutableNode}
34 *
35 * @param node
36 */
37 protected MutableNode(ImmutableNode<K, V> node) {
38 this.content = new Object[2 * FACTOR];
39 int dataUsed = 0;
40 int nodeUsed = 0;
41 for (int i = 0; i < FACTOR; i++) {
42 int bitposition = 1 << i;
43 if ((node.dataMap & bitposition) != 0) {
44 content[2 * i] = node.content[dataUsed * 2];
45 content[2 * i + 1] = node.content[dataUsed * 2 + 1];
46 dataUsed++;
47 } else if ((node.nodeMap & bitposition) != 0) {
48 content[2 * i + 1] = node.content[node.content.length - 1 - nodeUsed];
49 nodeUsed++;
50 }
51 }
52 this.cachedHash = node.hashCode();
53 }
54
55 @Override
56 public V getValue(K key, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth) {
57 int selectedHashFragment = hashFragment(hash, shiftDepth(depth));
58 @SuppressWarnings("unchecked")
59 K keyCandidate = (K) this.content[2 * selectedHashFragment];
60 if (keyCandidate != null) {
61 if (keyCandidate.equals(key)) {
62 @SuppressWarnings("unchecked")
63 V value = (V) this.content[2 * selectedHashFragment + 1];
64 return value;
65 } else {
66 return defaultValue;
67 }
68 } else {
69 @SuppressWarnings("unchecked")
70 var nodeCandidate = (Node<K, V>) content[2 * selectedHashFragment + 1];
71 if (nodeCandidate != null) {
72 int newDepth = depth + 1;
73 int newHash = newHash(hashProvider, key, hash, newDepth);
74 return nodeCandidate.getValue(key, hashProvider, defaultValue, newHash, newDepth);
75 } else {
76 return defaultValue;
77 }
78 }
79 }
80
81 @Override
82 public Node<K, V> putValue(K key, V value, OldValueBox<V> oldValueBox, ContinousHashProvider<? super K> hashProvider,
83 V defaultValue, int hash, int depth) {
84 int selectedHashFragment = hashFragment(hash, shiftDepth(depth));
85 @SuppressWarnings("unchecked")
86 K keyCandidate = (K) content[2 * selectedHashFragment];
87 if (keyCandidate != null) {
88 // If has key
89 if (keyCandidate.equals(key)) {
90 // The key is equals to an existing key -> update entry
91 if (value == defaultValue) {
92 return removeEntry(selectedHashFragment, oldValueBox);
93 } else {
94 return updateValue(value, oldValueBox, selectedHashFragment);
95 }
96 } else {
97 // The key is not equivalent to an existing key on the same hash bin
98 // -> split entry if it is necessary
99 if (value == defaultValue) {
100 // Value is default -> do not need to add new node
101 oldValueBox.setOldValue(defaultValue);
102 return this;
103 } else {
104 // Value is not default -> Split entry data to a new node
105 oldValueBox.setOldValue(defaultValue);
106 return moveDownAndSplit(hashProvider, key, value, keyCandidate, hash, depth, selectedHashFragment);
107 }
108 }
109 } else {
110 // If it does not have key, check for value
111 @SuppressWarnings("unchecked")
112 var nodeCandidate = (Node<K, V>) content[2 * selectedHashFragment + 1];
113 if (nodeCandidate != null) {
114 // If it has value, it is a subnode -> upate that
115 var newNode = nodeCandidate.putValue(key, value, oldValueBox, hashProvider, defaultValue,
116 newHash(hashProvider, key, hash, depth + 1), depth + 1);
117 return updateWithSubNode(selectedHashFragment, newNode, value.equals(defaultValue));
118 } else {
119 // If it does not have value, put it in the empty place
120 if (value == defaultValue) {
121 // dont need to add new key-value pair
122 oldValueBox.setOldValue(defaultValue);
123 return this;
124 } else {
125 return addEntry(key, value, oldValueBox, selectedHashFragment, defaultValue);
126 }
127
128 }
129 }
130 }
131
132 private Node<K, V> addEntry(K key, V value, OldValueBox<V> oldValueBox, int selectedHashFragment, V defaultValue) {
133 content[2 * selectedHashFragment] = key;
134 oldValueBox.setOldValue(defaultValue);
135 content[2 * selectedHashFragment + 1] = value;
136 updateHash();
137 return this;
138 }
139
140 /**
141 * Updates an entry in a selected hash-fragment to a non-default value.
142 *
143 * @param value
144 * @param selectedHashFragment
145 * @return
146 */
147 @SuppressWarnings("unchecked")
148 Node<K, V> updateValue(V value, OldValueBox<V> oldValue, int selectedHashFragment) {
149 oldValue.setOldValue((V) content[2 * selectedHashFragment + 1]);
150 content[2 * selectedHashFragment + 1] = value;
151 updateHash();
152 return this;
153 }
154
155 /**
156 *
157 * @param selectedHashFragment
158 * @param newNode
159 * @return
160 */
161 Node<K, V> updateWithSubNode(int selectedHashFragment, Node<K, V> newNode, boolean deletionHappened) {
162 if (deletionHappened) {
163 if (newNode == null) {
164 // Check whether this node become empty
165 content[2 * selectedHashFragment + 1] = null; // i.e. the new node
166 if (hasContent()) {
167 updateHash();
168 return this;
169 } else {
170 return null;
171 }
172 } else {
173 // check whether newNode is orphan
174 MutableNode<K, V> immutableNewNode = newNode.isMutable();
175 if (immutableNewNode != null) {
176 int orphaned = immutableNewNode.isOrphaned();
177 if (orphaned >= 0) {
178 // orphan subnode data is replaced with data
179 content[2 * selectedHashFragment] = immutableNewNode.content[orphaned * 2];
180 content[2 * selectedHashFragment + 1] = immutableNewNode.content[orphaned * 2 + 1];
181 updateHash();
182 return this;
183 }
184 }
185 }
186 }
187 // normal behaviour
188 content[2 * selectedHashFragment + 1] = newNode;
189 updateHash();
190 return this;
191
192 }
193
194 private boolean hasContent() {
195 for (Object element : this.content) {
196 if (element != null)
197 return true;
198 }
199 return false;
200 }
201
202 @Override
203 protected MutableNode<K, V> isMutable() {
204 return this;
205 }
206
207 protected int isOrphaned() {
208 int dataFound = -2;
209 for (int i = 0; i < FACTOR; i++) {
210 if (content[i * 2] != null) {
211 if (dataFound >= 0) {
212 return -1;
213 } else {
214 dataFound = i;
215 }
216 } else if (content[i * 2 + 1] != null) {
217 return -3;
218 }
219 }
220 return dataFound;
221 }
222
223 @SuppressWarnings("unchecked")
224 private Node<K, V> moveDownAndSplit(ContinousHashProvider<? super K> hashProvider, K newKey, V newValue,
225 K previousKey, int hashOfNewKey, int depth, int selectedHashFragmentOfCurrentDepth) {
226 V previousValue = (V) content[2 * selectedHashFragmentOfCurrentDepth + 1];
227
228 MutableNode<K, V> newSubNode = newNodeWithTwoEntries(hashProvider, previousKey, previousValue,
229 hashProvider.getHash(previousKey, hashDepth(depth)), newKey, newValue, hashOfNewKey, depth + 1);
230
231 content[2 * selectedHashFragmentOfCurrentDepth] = null;
232 content[2 * selectedHashFragmentOfCurrentDepth + 1] = newSubNode;
233 updateHash();
234 return this;
235 }
236
237 // Pass everything as parameters for performance.
238 @SuppressWarnings("squid:S107")
239 private MutableNode<K, V> newNodeWithTwoEntries(ContinousHashProvider<? super K> hashProvider, K key1, V value1,
240 int oldHash1, K key2, V value2, int oldHash2, int newdepth) {
241 int newHash1 = newHash(hashProvider, key1, oldHash1, newdepth);
242 int newHash2 = newHash(hashProvider, key2, oldHash2, newdepth);
243 int newFragment1 = hashFragment(newHash1, shiftDepth(newdepth));
244 int newFragment2 = hashFragment(newHash2, shiftDepth(newdepth));
245
246 MutableNode<K, V> subNode = new MutableNode<>();
247 if (newFragment1 != newFragment2) {
248 subNode.content[newFragment1 * 2] = key1;
249 subNode.content[newFragment1 * 2 + 1] = value1;
250
251 subNode.content[newFragment2 * 2] = key2;
252 subNode.content[newFragment2 * 2 + 1] = value2;
253 } else {
254 MutableNode<K, V> subSubNode = newNodeWithTwoEntries(hashProvider, key1, value1, newHash1, key2, value2,
255 newHash2, newdepth + 1);
256 subNode.content[newFragment1 * 2 + 1] = subSubNode;
257 }
258 subNode.updateHash();
259 return subNode;
260 }
261
262 @SuppressWarnings("unchecked")
263 Node<K, V> removeEntry(int selectedHashFragment, OldValueBox<V> oldValue) {
264 content[2 * selectedHashFragment] = null;
265 oldValue.setOldValue((V) content[2 * selectedHashFragment + 1]);
266 content[2 * selectedHashFragment + 1] = null;
267 if (hasContent()) {
268 updateHash();
269 return this;
270 } else {
271 return null;
272 }
273 }
274
275 @SuppressWarnings("unchecked")
276 @Override
277 public long getSize() {
278 int size = 0;
279 for (int i = 0; i < FACTOR; i++) {
280 if (content[i * 2] != null) {
281 size++;
282 } else {
283 Node<K, V> nodeCandidate = (Node<K, V>) content[i * 2 + 1];
284 if (nodeCandidate != null) {
285 size += nodeCandidate.getSize();
286 }
287 }
288 }
289 return size;
290 }
291
292 @Override
293 protected MutableNode<K, V> toMutable() {
294 return this;
295 }
296
297 @Override
298 public ImmutableNode<K, V> toImmutable(Map<Node<K, V>, ImmutableNode<K, V>> cache) {
299 return ImmutableNode.constructImmutable(this, cache);
300 }
301
302 @SuppressWarnings("unchecked")
303 @Override
304 boolean moveToNext(MapCursor<K, V> cursor) {
305 // 1. try to move to data
306 if (cursor.dataIndex != MapCursor.INDEX_FINISH) {
307 for (int index = cursor.dataIndex + 1; index < FACTOR; index++) {
308 if (this.content[index * 2] != null) {
309 // 1.1 found next data
310 cursor.dataIndex = index;
311 cursor.key = (K) this.content[index * 2];
312 cursor.value = (V) this.content[index * 2 + 1];
313 return true;
314 }
315 }
316 cursor.dataIndex = MapCursor.INDEX_FINISH;
317 }
318
319 // 2. look inside the subnodes
320 for (int index = cursor.nodeIndexStack.peek() + 1; index < FACTOR; index++) {
321 if (this.content[index * 2] == null && this.content[index * 2 + 1] != null) {
322 // 2.1 found next subnode, move down to the subnode
323 Node<K, V> subnode = (Node<K, V>) this.content[index * 2 + 1];
324
325 cursor.dataIndex = MapCursor.INDEX_START;
326 cursor.nodeIndexStack.pop();
327 cursor.nodeIndexStack.push(index);
328 cursor.nodeIndexStack.push(MapCursor.INDEX_START);
329 cursor.nodeStack.push(subnode);
330
331 return subnode.moveToNext(cursor);
332 }
333 }
334 // 3. no subnode found, move up
335 cursor.nodeStack.pop();
336 cursor.nodeIndexStack.pop();
337 if (!cursor.nodeStack.isEmpty()) {
338 Node<K, V> supernode = cursor.nodeStack.peek();
339 return supernode.moveToNext(cursor);
340 } else {
341 cursor.key = null;
342 cursor.value = null;
343 return false;
344 }
345 }
346
347 @Override
348 public void prettyPrint(StringBuilder builder, int depth, int code) {
349 for (int i = 0; i < depth; i++) {
350 builder.append("\t");
351 }
352 if (code >= 0) {
353 builder.append(code);
354 builder.append(":");
355 }
356 builder.append("Mutable(");
357 // print content
358 boolean hadContent = false;
359 for (int i = 0; i < FACTOR; i++) {
360 if (content[2 * i] != null) {
361 if (hadContent) {
362 builder.append(",");
363 }
364 builder.append(i);
365 builder.append(":[");
366 builder.append(content[2 * i].toString());
367 builder.append("]->[");
368 builder.append(content[2 * i + 1].toString());
369 builder.append("]");
370 hadContent = true;
371 }
372 }
373 builder.append(")");
374 // print subnodes
375 for (int i = 0; i < FACTOR; i++) {
376 if (content[2 * i] == null && content[2 * i + 1] != null) {
377 @SuppressWarnings("unchecked")
378 Node<K, V> subNode = (Node<K, V>) content[2 * i + 1];
379 builder.append("\n");
380 subNode.prettyPrint(builder, depth + 1, i);
381 }
382 }
383 }
384
385 @Override
386 public void checkIntegrity(ContinousHashProvider<? super K> hashProvider, V defaultValue, int depth) {
387 // check for orphan nodes
388 if (depth > 0) {
389 int orphaned = isOrphaned();
390 if (orphaned >= 0) {
391 throw new IllegalStateException("Orphaned node! " + orphaned + ": " + content[2 * orphaned]);
392 }
393 }
394 // check the place of data
395 for (int i = 0; i < FACTOR; i++) {
396 if (this.content[2 * i] != null) {
397 @SuppressWarnings("unchecked")
398 K key = (K) this.content[2 * i];
399 @SuppressWarnings("unchecked")
400 V value = (V) this.content[2 * i + 1];
401
402 if (value == defaultValue) {
403 throw new IllegalStateException("Node contains default value!");
404 }
405 int hashCode = hashProvider.getHash(key, hashDepth(depth));
406 int shiftDepth = shiftDepth(depth);
407 int selectedHashFragment = hashFragment(hashCode, shiftDepth);
408 if (i != selectedHashFragment) {
409 throw new IllegalStateException("Key " + key + " with hash code " + hashCode
410 + " is in bad place! Fragment=" + selectedHashFragment + ", Place=" + i);
411 }
412 }
413 }
414 // check subnodes
415 for (int i = 0; i < FACTOR; i++) {
416 if (this.content[2 * i + 1] != null && this.content[2 * i] == null) {
417 @SuppressWarnings("unchecked")
418 var subNode = (Node<K, V>) this.content[2 * i + 1];
419 subNode.checkIntegrity(hashProvider, defaultValue, depth + 1);
420 }
421 }
422 // check the hash
423 int oldHash = this.cachedHash;
424 updateHash();
425 int newHash = this.cachedHash;
426 if (oldHash != newHash) {
427 throw new IllegalStateException("Hash code was not up to date! (old=" + oldHash + ",new=" + newHash + ")");
428 }
429 }
430
431 protected void updateHash() {
432 this.cachedHash = Arrays.hashCode(content);
433 }
434
435 @Override
436 public int hashCode() {
437 return this.cachedHash;
438 }
439
440 @Override
441 public boolean equals(Object obj) {
442 if (this == obj)
443 return true;
444 if (obj == null)
445 return false;
446 if (obj instanceof MutableNode<?, ?> mutableObj) {
447 return Arrays.deepEquals(this.content, mutableObj.content);
448 } else if (obj instanceof ImmutableNode<?, ?> immutableObj) {
449 return ImmutableNode.compareImmutableMutable(immutableObj, this);
450 } else {
451 return false;
452 }
453 }
454}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java
new file mode 100644
index 00000000..234a4ff3
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java
@@ -0,0 +1,85 @@
1package tools.refinery.store.map.internal;
2
3import java.util.Map;
4
5import tools.refinery.store.map.ContinousHashProvider;
6
7public abstract class Node<K,V>{
8 public static final int BRANCHING_FACTOR_BITS = 5;
9 public static final int FACTOR = 1<<BRANCHING_FACTOR_BITS;
10 protected static final int NUMBER_OF_FACTORS = Integer.SIZE / BRANCHING_FACTOR_BITS;
11 protected static final int FACTOR_MASK = FACTOR-1;
12 public static final int EFFECTIVE_BITS = BRANCHING_FACTOR_BITS * NUMBER_OF_FACTORS;
13
14 /**
15 * Calculates the index for the continuous hash.
16 * @param depth The depth of the node in the tree.
17 * @return The index of the continuous hash.
18 */
19 protected static int hashDepth(int depth) {
20 return depth/NUMBER_OF_FACTORS;
21 }
22
23 /**
24 * Calculates the which segment of a single hash should be used.
25 * @param depth The depth of the node in the tree.
26 * @return The segment of a hash code.
27 */
28 protected static int shiftDepth(int depth) {
29 return depth%NUMBER_OF_FACTORS;
30 }
31 /**
32 * Selects a segments from a complete hash for a given depth.
33 * @param hash A complete hash.
34 * @param shiftDepth The index of the segment.
35 * @return The segment as an integer.
36 */
37 protected static int hashFragment(int hash, int shiftDepth) {
38 if(shiftDepth<0 || Node.NUMBER_OF_FACTORS<shiftDepth) throw new IllegalArgumentException("Invalid shift depth! valid intervall=[0;5], input="+shiftDepth);
39 return (hash >>> shiftDepth*BRANCHING_FACTOR_BITS) & FACTOR_MASK;
40 }
41
42 /**
43 * Returns the hash code for a given depth. It may calculate new hash code, or reuse a hash code calculated for depth-1.
44 * @param key The key.
45 * @param hash Hash code for depth-1
46 * @param depth The depth.
47 * @return The new hash code.
48 */
49 protected int newHash(final ContinousHashProvider<? super K> hashProvider, K key, int hash, int depth) {
50 final int hashDepth = hashDepth(depth);
51 if(hashDepth>=ContinousHashProvider.MAX_PRACTICAL_DEPTH) {
52 throw new IllegalArgumentException("Key "+key+" have the clashing hashcode over the practical depth limitation ("+ContinousHashProvider.MAX_PRACTICAL_DEPTH+")!");
53 }
54 return depth%NUMBER_OF_FACTORS == 0?
55 hashProvider.getHash(key, hashDepth) :
56 hash;
57 }
58
59
60 public abstract V getValue(K key, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth);
61 public abstract Node<K,V> putValue(K key, V value, OldValueBox<V> old, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth);
62 public abstract long getSize();
63
64 abstract MutableNode<K, V> toMutable();
65 public abstract ImmutableNode<K, V> toImmutable(
66 Map<Node<K, V>,ImmutableNode<K, V>> cache);
67 protected abstract MutableNode<K, V> isMutable();
68 /**
69 * Moves a {@link MapCursor} to its next position.
70 * @param cursor the cursor
71 * @return Whether there was a next value to move on.
72 */
73 abstract boolean moveToNext(MapCursor<K,V> cursor);
74
75 ///////// FOR printing
76 public abstract void prettyPrint(StringBuilder builder, int depth, int code);
77 @Override
78 public String toString() {
79 StringBuilder stringBuilder = new StringBuilder();
80 prettyPrint(stringBuilder, 0, -1);
81 return stringBuilder.toString();
82 }
83 public void checkIntegrity(ContinousHashProvider<? super K> hashProvider, V defaultValue, int depth) {}
84
85}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/OldValueBox.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/OldValueBox.java
new file mode 100644
index 00000000..5534c703
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/OldValueBox.java
@@ -0,0 +1,19 @@
1package tools.refinery.store.map.internal;
2
3public class OldValueBox<V>{
4 V oldValue;
5 boolean isSet = false;
6
7 public V getOldValue() {
8 if(!isSet) throw new IllegalStateException();
9 isSet = false;
10 return oldValue;
11 }
12
13 public void setOldValue(V ouldValue) {
14 if(isSet) throw new IllegalStateException();
15 this.oldValue = ouldValue;
16 isSet = true;
17 }
18
19}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java
new file mode 100644
index 00000000..346fe596
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java
@@ -0,0 +1,171 @@
1package tools.refinery.store.map.internal;
2
3import java.util.Iterator;
4import java.util.LinkedList;
5import java.util.List;
6
7import tools.refinery.store.map.ContinousHashProvider;
8import tools.refinery.store.map.Cursor;
9import tools.refinery.store.map.DiffCursor;
10import tools.refinery.store.map.VersionedMap;
11import tools.refinery.store.map.VersionedMapStoreImpl;
12
13/**
14 * Not threadSafe in itself
15 * @author Oszkar Semerath
16 *
17 * @param <K>
18 * @param <V>
19 */
20public class VersionedMapImpl<K,V> implements VersionedMap<K,V>{
21 protected final VersionedMapStoreImpl<K,V> store;
22
23 protected final ContinousHashProvider<K> hashProvider;
24 protected final V defaultValue;
25 protected Node<K,V> root;
26
27 private OldValueBox<V> oldValueBox = new OldValueBox<>();
28
29 public VersionedMapImpl(
30 VersionedMapStoreImpl<K,V> store,
31 ContinousHashProvider<K> hashProvider,
32 V defaultValue)
33 {
34 this.store = store;
35 this.hashProvider = hashProvider;
36 this.defaultValue = defaultValue;
37 this.root = null;
38 }
39 public VersionedMapImpl(
40 VersionedMapStoreImpl<K,V> store,
41 ContinousHashProvider<K> hashProvider,
42 V defaultValue, Node<K,V> data)
43 {
44 this.store = store;
45 this.hashProvider = hashProvider;
46 this.defaultValue = defaultValue;
47 this.root = data;
48 }
49
50 public V getDefaultValue() {
51 return defaultValue;
52 }
53 public ContinousHashProvider<K> getHashProvider() {
54 return hashProvider;
55 }
56 @Override
57 public V put(K key, V value) {
58 if(root!=null) {
59 root = root.putValue(key, value, oldValueBox, hashProvider, defaultValue, hashProvider.getHash(key, 0), 0);
60 return oldValueBox.getOldValue();
61 } else {
62 root = MutableNode.initialize(key, value, hashProvider, defaultValue);
63 return defaultValue;
64 }
65 }
66
67 @Override
68 public void putAll(Cursor<K, V> cursor) {
69 if(cursor.getDependingMaps().contains(this)) {
70 List<K> keys = new LinkedList<>();
71 List<V> values = new LinkedList<>();
72 while(cursor.move()) {
73 keys.add(cursor.getKey());
74 values.add(cursor.getValue());
75 }
76 Iterator<K> keyIterator = keys.iterator();
77 Iterator<V> valueIterator = values.iterator();
78 while(keyIterator.hasNext()) {
79 this.put(keyIterator.next(), valueIterator.next());
80 }
81 } else {
82 while(cursor.move()) {
83 this.put(cursor.getKey(), cursor.getValue());
84 }
85 }
86 }
87
88 @Override
89 public V get(K key) {
90 if(root!=null) {
91 return root.getValue(key, hashProvider, defaultValue, hashProvider.getHash(key, 0), 0);
92 } else {
93 return defaultValue;
94 }
95 }
96 @Override
97 public long getSize() {
98 if(root == null) {
99 return 0;
100 } else {
101 return root.getSize();
102 }
103 }
104
105 @Override
106 public Cursor<K, V> getAll() {
107 return new MapCursor<>(this.root,this);
108 }
109 @Override
110 public DiffCursor<K, V> getDiffCursor(long toVersion) {
111 Cursor<K, V> fromCursor = this.getAll();
112 VersionedMap<K, V> toMap = this.store.createMap(toVersion);
113 Cursor<K, V> toCursor = toMap.getAll();
114 return new MapDiffCursor<>(this.hashProvider,this.defaultValue, fromCursor, toCursor);
115
116 }
117
118
119 @Override
120 public long commit() {
121 return this.store.commit(root,this);
122 }
123 public void setRoot(Node<K, V> root) {
124 this.root = root;
125 }
126
127 @Override
128 public void restore(long state) {
129 root = this.store.revert(state);
130 }
131
132 @Override
133 public int hashCode() {
134 final int prime = 31;
135 int result = 1;
136 result = prime * result + ((root == null) ? 0 : root.hashCode());
137 return result;
138 }
139
140 @Override
141 public boolean equals(Object obj) {
142 if (this == obj)
143 return true;
144 if (obj == null)
145 return false;
146 if (getClass() != obj.getClass())
147 return false;
148 VersionedMapImpl<?,?> other = (VersionedMapImpl<?,?>) obj;
149 if (root == null) {
150 if (other.root != null)
151 return false;
152 } else if (!root.equals(other.root))
153 return false;
154 return true;
155 }
156 public void prettyPrint() {
157 StringBuilder s = new StringBuilder();
158 if(this.root != null) {
159 this.root.prettyPrint(s, 0, -1);
160 System.out.println(s.toString());
161 } else {
162 System.out.println("empty tree");
163 }
164 }
165 public void checkIntegrity() {
166 if(this.root != null) {
167 this.root.checkIntegrity(hashProvider, defaultValue, 0);
168 }
169 }
170
171}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/Model.java b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java
new file mode 100644
index 00000000..a42d711a
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java
@@ -0,0 +1,20 @@
1package tools.refinery.store.model;
2
3import java.util.Set;
4
5import tools.refinery.store.map.Cursor;
6import tools.refinery.store.map.Versioned;
7import tools.refinery.store.model.representation.DataRepresentation;
8
9public interface Model extends Versioned{
10 @SuppressWarnings("squid:S1452")
11 Set<DataRepresentation<?, ?>> getDataRepresentations();
12
13 <K,V> V get(DataRepresentation<K,V> representation, K key);
14 <K,V> Cursor<K,V> getAll(DataRepresentation<K,V> representation);
15 <K,V> V put(DataRepresentation<K,V> representation, K key, V value);
16 <K,V> void putAll(DataRepresentation<K,V> representation, Cursor<K,V> cursor);
17 <K,V> long getSize(DataRepresentation<K,V> representation);
18
19 ModelDiffCursor getDiffCursor(long to);
20}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelCursor.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelCursor.java
new file mode 100644
index 00000000..a835cf69
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelCursor.java
@@ -0,0 +1,25 @@
1package tools.refinery.store.model;
2
3import java.util.Map;
4
5import tools.refinery.store.map.Cursor;
6import tools.refinery.store.model.representation.DataRepresentation;
7
8public class ModelCursor {
9 final Map<DataRepresentation<?, ?>,Cursor<?,?>> cursors;
10
11 public ModelCursor(Map<DataRepresentation<?, ?>, Cursor<?, ?>> cursors) {
12 super();
13 this.cursors = cursors;
14 }
15
16 @SuppressWarnings("unchecked")
17 public <K,V> Cursor<K,V> getCursor(DataRepresentation<K, V> representation) {
18 Cursor<?, ?> cursor = cursors.get(representation);
19 if(cursor != null) {
20 return (Cursor<K, V>) cursor;
21 } else {
22 throw new IllegalArgumentException("ModelCursor does not contain cursor for representation "+representation);
23 }
24 }
25}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelDiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelDiffCursor.java
new file mode 100644
index 00000000..91990fa6
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelDiffCursor.java
@@ -0,0 +1,26 @@
1package tools.refinery.store.model;
2
3import java.util.Map;
4
5import tools.refinery.store.map.Cursor;
6import tools.refinery.store.map.DiffCursor;
7import tools.refinery.store.model.representation.DataRepresentation;
8
9public class ModelDiffCursor {
10 final Map<DataRepresentation<?, ?>,DiffCursor<?,?>> diffcursors;
11
12 public ModelDiffCursor(Map<DataRepresentation<?, ?>, DiffCursor<?, ?>> diffcursors) {
13 super();
14 this.diffcursors = diffcursors;
15 }
16
17 @SuppressWarnings("unchecked")
18 public <K,V> DiffCursor<K,V> getCursor(DataRepresentation<K, V> representation) {
19 Cursor<?, ?> cursor = diffcursors.get(representation);
20 if(cursor != null) {
21 return (DiffCursor<K, V>) cursor;
22 } else {
23 throw new IllegalArgumentException("ModelCursor does not contain cursor for representation "+representation);
24 }
25 }
26}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java
new file mode 100644
index 00000000..682a0e78
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.model;
2
3import java.util.Set;
4
5import tools.refinery.store.model.representation.DataRepresentation;
6
7public interface ModelStore {
8 @SuppressWarnings("squid:S1452")
9 Set<DataRepresentation<?, ?>> getDataRepresentations();
10
11 Model createModel();
12 Model createModel(long state);
13
14 Set<Long> getStates();
15 ModelDiffCursor getDiffCursor(long from, long to);
16} \ No newline at end of file
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreImpl.java
new file mode 100644
index 00000000..97406cbb
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreImpl.java
@@ -0,0 +1,122 @@
1package tools.refinery.store.model;
2
3import java.util.HashMap;
4import java.util.LinkedList;
5import java.util.List;
6import java.util.Map;
7import java.util.Map.Entry;
8
9import tools.refinery.store.map.ContinousHashProvider;
10import tools.refinery.store.map.DiffCursor;
11import tools.refinery.store.map.VersionedMap;
12import tools.refinery.store.map.VersionedMapStore;
13import tools.refinery.store.map.VersionedMapStoreImpl;
14import tools.refinery.store.model.internal.ModelImpl;
15import tools.refinery.store.model.internal.SimilarRelationEquivalenceClass;
16import tools.refinery.store.model.representation.AuxilaryData;
17import tools.refinery.store.model.representation.DataRepresentation;
18import tools.refinery.store.model.representation.Relation;
19
20import java.util.Set;
21
22public class ModelStoreImpl implements ModelStore {
23
24 private final Map<DataRepresentation<?, ?>, VersionedMapStore<?, ?>> stores;
25
26 public ModelStoreImpl(Set<DataRepresentation<?, ?>> dataRepresentations) {
27 stores = initStores(dataRepresentations);
28 }
29
30 private Map<DataRepresentation<?, ?>, VersionedMapStore<?, ?>> initStores(
31 Set<DataRepresentation<?, ?>> dataRepresentations) {
32 Map<DataRepresentation<?, ?>, VersionedMapStore<?, ?>> result = new HashMap<>();
33
34 Map<SimilarRelationEquivalenceClass, List<Relation<?>>> symbolRepresentationsPerHashPerArity = new HashMap<>();
35
36 for (DataRepresentation<?, ?> dataRepresentation : dataRepresentations) {
37 if (dataRepresentation instanceof Relation<?> symbolRepresentation) {
38 addOrCreate(symbolRepresentationsPerHashPerArity,
39 new SimilarRelationEquivalenceClass(symbolRepresentation), symbolRepresentation);
40 } else if (dataRepresentation instanceof AuxilaryData<?, ?>) {
41 VersionedMapStoreImpl<?, ?> store = new VersionedMapStoreImpl<>(dataRepresentation.getHashProvider(),
42 dataRepresentation.getDefaultValue());
43 result.put(dataRepresentation, store);
44 } else {
45 throw new UnsupportedOperationException(
46 "Model store does not have strategy to use " + dataRepresentation.getClass() + "!");
47 }
48 }
49 for (List<Relation<?>> symbolGroup : symbolRepresentationsPerHashPerArity.values()) {
50 initRepresentationGroup(result, symbolGroup);
51 }
52
53 return result;
54 }
55
56 private void initRepresentationGroup(Map<DataRepresentation<?, ?>, VersionedMapStore<?, ?>> result,
57 List<Relation<?>> symbolGroup) {
58 final ContinousHashProvider<Tuple> hashProvider = symbolGroup.get(0).getHashProvider();
59 final Object defaultValue = symbolGroup.get(0).getDefaultValue();
60
61 List<VersionedMapStore<Tuple, Object>> maps = VersionedMapStoreImpl
62 .createSharedVersionedMapStores(symbolGroup.size(), hashProvider, defaultValue);
63
64 for (int i = 0; i < symbolGroup.size(); i++) {
65 result.put(symbolGroup.get(i), maps.get(i));
66 }
67 }
68
69 private static <K, V> void addOrCreate(Map<K, List<V>> map, K key, V value) {
70 List<V> list;
71 if (map.containsKey(key)) {
72 list = map.get(key);
73 } else {
74 list = new LinkedList<>();
75 map.put(key, list);
76 }
77 list.add(value);
78 }
79
80 @Override
81 public Set<DataRepresentation<?, ?>> getDataRepresentations() {
82 return this.stores.keySet();
83 }
84
85 @Override
86 public ModelImpl createModel() {
87 Map<DataRepresentation<?, ?>, VersionedMap<?, ?>> maps = new HashMap<>();
88 for (Entry<DataRepresentation<?, ?>, VersionedMapStore<?, ?>> entry : this.stores.entrySet()) {
89 maps.put(entry.getKey(), entry.getValue().createMap());
90 }
91 return new ModelImpl(this, maps);
92 }
93
94 @Override
95 public synchronized ModelImpl createModel(long state) {
96 Map<DataRepresentation<?, ?>, VersionedMap<?, ?>> maps = new HashMap<>();
97 for (Entry<DataRepresentation<?, ?>, VersionedMapStore<?, ?>> entry : this.stores.entrySet()) {
98 maps.put(entry.getKey(), entry.getValue().createMap(state));
99 }
100 return new ModelImpl(this, maps);
101 }
102
103 @Override
104 public synchronized Set<Long> getStates() {
105 var iterator = stores.values().iterator();
106 if (iterator.hasNext()) {
107 return Set.copyOf(iterator.next().getStates());
108 }
109 return Set.of(0l);
110 }
111
112 @Override
113 public synchronized ModelDiffCursor getDiffCursor(long from, long to) {
114 Map<DataRepresentation<?, ?>, DiffCursor<?, ?>> diffcursors = new HashMap<>();
115 for (Entry<DataRepresentation<?, ?>, VersionedMapStore<?, ?>> entry : stores.entrySet()) {
116 DataRepresentation<?, ?> representation = entry.getKey();
117 DiffCursor<?, ?> diffCursor = entry.getValue().getDiffCursor(from, to);
118 diffcursors.put(representation, diffCursor);
119 }
120 return new ModelDiffCursor(diffcursors);
121 }
122}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/Tuple.java b/subprojects/store/src/main/java/tools/refinery/store/model/Tuple.java
new file mode 100644
index 00000000..0aae3727
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/Tuple.java
@@ -0,0 +1,148 @@
1package tools.refinery.store.model;
2
3import java.util.ArrayList;
4import java.util.Arrays;
5import java.util.List;
6
7public abstract class Tuple {
8 private static final int CUSTOMTUPLESIZE = 2;
9 protected static final List<Tuple1> tuple1Cash = new ArrayList<>(1024);
10
11 public abstract int getSize();
12 public abstract int get(int element);
13 public abstract int[] toArray();
14
15 @Override
16 public String toString() {
17 StringBuilder b = new StringBuilder();
18 b.append("[");
19 for(int i = 0; i<getSize(); i++) {
20 if(i!=0) {
21 b.append(",");
22 }
23 b.append(get(i));
24 }
25 b.append("]");
26 return b.toString();
27 }
28
29 public static Tuple1 of1(int value) {
30 if(value < tuple1Cash.size()) {
31 return tuple1Cash.get(value);
32 } else {
33 Tuple1 newlyCreated = null;
34 while(value >= tuple1Cash.size()) {
35 newlyCreated = new Tuple1(tuple1Cash.size());
36 tuple1Cash.add(newlyCreated);
37 }
38 return newlyCreated;
39 }
40 }
41
42 public static Tuple of(int... values) {
43 if(values.length == 0) {
44 return new Tuple0();
45 } else if(values.length == 1) {
46 return of1(values[0]);
47 } else if(values.length == 2) {
48 return new Tuple2(values[0],values[1]);
49 } else return new TupleN(values);
50 }
51
52 protected IllegalArgumentException doesNotContain(int element) {
53 return new IllegalArgumentException("Tuple does not contain element "+element);
54 }
55
56 public static class Tuple0 extends Tuple{
57 protected Tuple0() { }
58 @Override public int getSize() { return 0; }
59 @Override public int get(int element) {
60 throw doesNotContain(element);
61 }
62 @Override public int[] toArray() {return new int[]{};}
63 @Override public int hashCode() { return TupleHashProvider.singleton().getHash(this, 0); }
64 @Override
65 public boolean equals(Object obj) {
66 if (this == obj)
67 return true;
68 if (obj == null)
69 return false;
70 if (getClass() != obj.getClass())
71 return false;
72 return true;
73 }
74 }
75 public static class Tuple1 extends Tuple{
76 final int value0;
77 protected Tuple1(int value0) { this.value0 = value0; }
78 @Override public int getSize() { return 1; }
79 @Override public int get(int element) {
80 if(element == 0) return value0;
81 throw doesNotContain(element);
82 }
83 @Override public int[] toArray() {return new int[]{ value0 };}
84 @Override public int hashCode() { return TupleHashProvider.singleton().getHash(this, 0); }
85 @Override
86 public boolean equals(Object obj) {
87 if (this == obj)
88 return true;
89 if (obj == null)
90 return false;
91 if (getClass() != obj.getClass())
92 return false;
93 Tuple1 other = (Tuple1) obj;
94 return value0 == other.value0;
95 }
96 }
97 public static class Tuple2 extends Tuple{
98 final int value0;
99 final int value1;
100 protected Tuple2(int value0, int value1) { this.value0 = value0; this.value1 = value1; }
101 @Override public int getSize() { return 2; }
102 @Override public int get(int element) {
103 if(element == 0) return value0;
104 else if(element == 1) return value1;
105 throw doesNotContain(element);
106 }
107 @Override public int[] toArray() {return new int[]{ value0,value1 };}
108 @Override public int hashCode() { return TupleHashProvider.singleton().getHash(this, 0); }
109 @Override
110 public boolean equals(Object obj) {
111 if (this == obj)
112 return true;
113 if (obj == null)
114 return false;
115 if (getClass() != obj.getClass())
116 return false;
117 Tuple2 other = (Tuple2) obj;
118 return value0 == other.value0 && value1 == other.value1;
119 }
120 }
121 public static class TupleN extends Tuple{
122 final int[] values;
123 protected TupleN(int[] values) {
124 if(values.length<CUSTOMTUPLESIZE)
125 throw new IllegalArgumentException();
126 this.values = Arrays.copyOf(values, values.length);
127 }
128 @Override public int getSize() { return values.length; }
129 @Override public int get(int element) {
130 if(0<=element && element < values.length) {
131 return values[element];
132 } else throw doesNotContain(element);
133 }
134 @Override public int[] toArray() { return values; }
135 @Override public int hashCode() { return TupleHashProvider.singleton().getHash(this, 0); }
136 @Override
137 public boolean equals(Object obj) {
138 if (this == obj)
139 return true;
140 if (obj == null)
141 return false;
142 if (getClass() != obj.getClass())
143 return false;
144 TupleN other = (TupleN) obj;
145 return Arrays.equals(values, other.values);
146 }
147 }
148}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProvider.java b/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProvider.java
new file mode 100644
index 00000000..7a01311a
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProvider.java
@@ -0,0 +1,65 @@
1package tools.refinery.store.model;
2
3import tools.refinery.store.map.ContinousHashProvider;
4
5public class TupleHashProvider implements ContinousHashProvider<Tuple> {
6 protected static TupleHashProvider instance;
7
8 public static TupleHashProvider singleton() {
9 if (instance == null) {
10 instance = new TupleHashProvider();
11 }
12 return instance;
13 }
14
15 protected static final int[] primes = new int[] { 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
16 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211,
17 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337,
18 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461,
19 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601,
20 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739,
21 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881,
22 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021,
23 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151,
24 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283,
25 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429,
26 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549,
27 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667,
28 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811,
29 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973,
30 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089,
31 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243,
32 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377,
33 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531,
34 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677,
35 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791,
36 2797, 2801, 2803, 2819, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217,
37 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359,
38 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517,
39 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637,
40 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793,
41 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911 };
42
43 protected static final long LARGESTPRIME30BITS = 1073741789;
44
45 public TupleHashProvider() {
46 if (primes.length < MAX_PRACTICAL_DEPTH) {
47 throw new UnsupportedOperationException(
48 "Not enough prime numbers to support the practical depth of continuous hash!");
49 }
50 }
51
52 @Override
53 public int getHash(Tuple key, int index) {
54 if (index >= primes.length) {
55 throw new IllegalArgumentException("Not enough prime numbers to support index");
56 }
57 long accumulator = 0;
58 final int prime = primes[index];
59 for (int i = 0; i < key.getSize(); i++) {
60 accumulator = (prime * accumulator + key.get(i)) % LARGESTPRIME30BITS;
61 }
62
63 return (int) accumulator;
64 }
65}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProviderBitMagic.java b/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProviderBitMagic.java
new file mode 100644
index 00000000..5b053229
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProviderBitMagic.java
@@ -0,0 +1,28 @@
1package tools.refinery.store.model;
2
3import tools.refinery.store.map.ContinousHashProvider;
4
5public class TupleHashProviderBitMagic implements ContinousHashProvider<Tuple> {
6
7 @Override
8 public int getHash(Tuple key, int index) {
9 if(key.getSize() == 1) {
10 return key.get(0);
11 }
12
13 int result = 0;
14 final int startBitIndex = index*30;
15 final int finalBitIndex = startBitIndex+30;
16 final int arity = key.getSize();
17
18 for(int i = startBitIndex; i<=finalBitIndex; i++) {
19 final int selectedKey = key.get(i%arity);
20 final int selectedPosition = 1<<(i/arity);
21 if((selectedKey&selectedPosition) != 0) {
22 result |= 1<<(i%30);
23 }
24 }
25
26 return result;
27 }
28}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java
new file mode 100644
index 00000000..2a5f2925
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java
@@ -0,0 +1,124 @@
1package tools.refinery.store.model.internal;
2
3import java.util.HashMap;
4import java.util.Map;
5import java.util.Set;
6
7import tools.refinery.store.map.ContinousHashProvider;
8import tools.refinery.store.map.Cursor;
9import tools.refinery.store.map.DiffCursor;
10import tools.refinery.store.map.VersionedMap;
11import tools.refinery.store.map.internal.MapDiffCursor;
12import tools.refinery.store.model.Model;
13import tools.refinery.store.model.ModelDiffCursor;
14import tools.refinery.store.model.ModelStore;
15import tools.refinery.store.model.representation.DataRepresentation;
16
17public class ModelImpl implements Model {
18 private final ModelStore store;
19 private final Map<DataRepresentation<?, ?>, VersionedMap<?, ?>> maps;
20
21 public ModelImpl(ModelStore store, Map<DataRepresentation<?, ?>, VersionedMap<?, ?>> maps) {
22 this.store = store;
23 this.maps = maps;
24 }
25
26 @Override
27 public Set<DataRepresentation<?, ?>> getDataRepresentations() {
28 return maps.keySet();
29 }
30
31 @SuppressWarnings("unchecked")
32 private <K, V> VersionedMap<K, V> getMap(DataRepresentation<K, V> representation) {
33 if (maps.containsKey(representation)) {
34 return (VersionedMap<K, V>) maps.get(representation);
35 } else {
36 throw new IllegalArgumentException("Model does have representation " + representation);
37 }
38 }
39
40 private <K, V> VersionedMap<K, V> getMapValidateKey(DataRepresentation<K, V> representation, K key) {
41 if (representation.isValidKey(key)) {
42 return getMap(representation);
43 } else {
44 throw new IllegalArgumentException(
45 "Key is not valid for representation! (representation=" + representation + ", key=" + key + ");");
46 }
47 }
48
49 @Override
50 public <K, V> V get(DataRepresentation<K, V> representation, K key) {
51 return getMapValidateKey(representation, key).get(key);
52 }
53
54 @Override
55 public <K, V> Cursor<K, V> getAll(DataRepresentation<K, V> representation) {
56 return getMap(representation).getAll();
57 }
58
59 @Override
60 public <K, V> V put(DataRepresentation<K, V> representation, K key, V value) {
61 return getMapValidateKey(representation, key).put(key, value);
62 }
63
64 @Override
65 public <K, V> void putAll(DataRepresentation<K, V> representation, Cursor<K, V> cursor) {
66 getMap(representation).putAll(cursor);
67 }
68
69 @Override
70 public <K, V> long getSize(DataRepresentation<K, V> representation) {
71 return getMap(representation).getSize();
72 }
73
74 @Override
75 public ModelDiffCursor getDiffCursor(long to) {
76 Model toModel = store.createModel(to);
77 Map<DataRepresentation<?, ?>, DiffCursor<?, ?>> diffCursors = new HashMap<>();
78 for (DataRepresentation<?, ?> representation : this.maps.keySet()) {
79 MapDiffCursor<?, ?> diffCursor = constructDiffCursor(toModel, representation);
80 diffCursors.put(representation, diffCursor);
81 }
82 return new ModelDiffCursor(diffCursors);
83 }
84
85 private <K, V> MapDiffCursor<K, V> constructDiffCursor(Model toModel, DataRepresentation<K, V> representation) {
86 @SuppressWarnings("unchecked")
87 Cursor<K, V> fromCursor = (Cursor<K, V>) this.maps.get(representation).getAll();
88 Cursor<K, V> toCursor = toModel.getAll(representation);
89
90 ContinousHashProvider<K> hashProvider = representation.getHashProvider();
91 V defaultValue = representation.getDefaultValue();
92 return new MapDiffCursor<>(hashProvider, defaultValue, fromCursor, toCursor);
93 }
94
95 @Override
96 public long commit() {
97 long version = 0;
98 boolean versionSet = false;
99 for (VersionedMap<?, ?> map : maps.values()) {
100 long newVersion = map.commit();
101 if (versionSet) {
102 if (version != newVersion) {
103 throw new IllegalStateException(
104 "Maps in model have different versions! (" + version + " and" + newVersion + ")");
105 }
106 } else {
107 version = newVersion;
108 versionSet = true;
109 }
110 }
111 return version;
112 }
113
114 @Override
115 public void restore(long state) {
116 if(store.getStates().contains(state)) {
117 for (VersionedMap<?, ?> map : maps.values()) {
118 map.restore(state);
119 }
120 } else {
121 throw new IllegalArgumentException("Map does not contain state "+state+"!");
122 }
123 }
124}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/SimilarRelationEquivalenceClass.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/SimilarRelationEquivalenceClass.java
new file mode 100644
index 00000000..9d1b1dd0
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/SimilarRelationEquivalenceClass.java
@@ -0,0 +1,33 @@
1package tools.refinery.store.model.internal;
2
3import java.util.Objects;
4
5import tools.refinery.store.map.ContinousHashProvider;
6import tools.refinery.store.model.Tuple;
7import tools.refinery.store.model.representation.Relation;
8
9public class SimilarRelationEquivalenceClass {
10 final ContinousHashProvider<Tuple> hashProvider;
11 final Object defaultValue;
12 final int arity;
13 public SimilarRelationEquivalenceClass(Relation<?> representation) {
14 this.hashProvider = representation.getHashProvider();
15 this.defaultValue = representation.getDefaultValue();
16 this.arity = representation.getArity();
17 }
18 @Override
19 public int hashCode() {
20 return Objects.hash(arity, defaultValue, hashProvider);
21 }
22 @Override
23 public boolean equals(Object obj) {
24 if (this == obj)
25 return true;
26 if (!(obj instanceof SimilarRelationEquivalenceClass))
27 return false;
28 SimilarRelationEquivalenceClass other = (SimilarRelationEquivalenceClass) obj;
29 return arity == other.arity && Objects.equals(defaultValue, other.defaultValue)
30 && Objects.equals(hashProvider, other.hashProvider);
31 }
32
33}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/representation/AuxilaryData.java b/subprojects/store/src/main/java/tools/refinery/store/model/representation/AuxilaryData.java
new file mode 100644
index 00000000..ddd8a5f2
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/representation/AuxilaryData.java
@@ -0,0 +1,22 @@
1package tools.refinery.store.model.representation;
2
3import tools.refinery.store.map.ContinousHashProvider;
4
5public class AuxilaryData<K,V> extends DataRepresentation<K, V> {
6 private final String name;
7
8 public AuxilaryData(String name, ContinousHashProvider<K> hashProvider, V defaultValue) {
9 super(hashProvider, defaultValue);
10 this.name = name;
11 }
12
13 @Override
14 public String getName() {
15 return name;
16 }
17
18 @Override
19 public boolean isValidKey(K key) {
20 return true;
21 }
22}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/representation/DataRepresentation.java b/subprojects/store/src/main/java/tools/refinery/store/model/representation/DataRepresentation.java
new file mode 100644
index 00000000..585e7b88
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/representation/DataRepresentation.java
@@ -0,0 +1,24 @@
1package tools.refinery.store.model.representation;
2
3import tools.refinery.store.map.ContinousHashProvider;
4
5public abstract class DataRepresentation<K, V> {
6 protected final ContinousHashProvider<K> hashProvider;
7 protected final V defaultValue;
8
9 protected DataRepresentation(ContinousHashProvider<K> hashProvider, V defaultValue) {
10 this.hashProvider = hashProvider;
11 this.defaultValue = defaultValue;
12 }
13
14 public abstract String getName();
15
16 public ContinousHashProvider<K> getHashProvider() {
17 return hashProvider;
18 }
19 public abstract boolean isValidKey(K key);
20
21 public V getDefaultValue() {
22 return defaultValue;
23 }
24}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/representation/Relation.java b/subprojects/store/src/main/java/tools/refinery/store/model/representation/Relation.java
new file mode 100644
index 00000000..fc2a3185
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/representation/Relation.java
@@ -0,0 +1,31 @@
1package tools.refinery.store.model.representation;
2
3import tools.refinery.store.model.Tuple;
4import tools.refinery.store.model.TupleHashProvider;
5
6public class Relation<D> extends DataRepresentation<Tuple, D> {
7 private final String name;
8 private final int arity;
9
10 public Relation(String name, int arity, D defaultValue) {
11 super(TupleHashProvider.singleton(), defaultValue);
12 this.name = name;
13 this.arity = arity;
14 }
15
16 @Override
17 public String getName() {
18 return name;
19 }
20
21 public int getArity() {
22 return arity;
23 }
24
25 @Override
26 public boolean isValidKey(Tuple key) {
27 if(key == null) {
28 return false;
29 } else return key.getSize() == getArity();
30 }
31}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/representation/TruthValue.java b/subprojects/store/src/main/java/tools/refinery/store/model/representation/TruthValue.java
new file mode 100644
index 00000000..610713f3
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/representation/TruthValue.java
@@ -0,0 +1,51 @@
1package tools.refinery.store.model.representation;
2
3public enum TruthValue {
4 TRUE("true"),
5
6 FALSE("false"),
7
8 UNKNOWN("unknown"),
9
10 ERROR("error");
11
12 private final String name;
13
14 private TruthValue(String name) {
15 this.name = name;
16 }
17
18 public String getName() {
19 return name;
20 }
21
22 public static TruthValue toTruthValue(boolean value) {
23 return value ? TRUE : FALSE;
24 }
25
26 public boolean isConsistent() {
27 return this != ERROR;
28 }
29
30 public boolean isComplete() {
31 return this != UNKNOWN;
32 }
33
34 public boolean must() {
35 return this == TRUE || this == ERROR;
36 }
37
38 public boolean may() {
39 return this == TRUE || this == UNKNOWN;
40 }
41
42 public TruthValue not() {
43 if (this == TRUE) {
44 return FALSE;
45 } else if (this == FALSE) {
46 return TRUE;
47 } else {
48 return this;
49 }
50 }
51}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/QueriableModel.java b/subprojects/store/src/main/java/tools/refinery/store/query/QueriableModel.java
new file mode 100644
index 00000000..f669b3ed
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/QueriableModel.java
@@ -0,0 +1,30 @@
1package tools.refinery.store.query;
2
3import java.util.Optional;
4import java.util.Set;
5import java.util.stream.Stream;
6
7import tools.refinery.store.model.Model;
8import tools.refinery.store.query.building.DNFPredicate;
9
10public interface QueriableModel extends Model{
11 Set<DNFPredicate> getPredicates();
12
13 void flushChanges();
14
15 boolean hasResult(DNFPredicate predicate);
16
17 boolean hasResult(DNFPredicate predicate, Object[] parameters);
18
19 Optional<Object[]> oneResult(DNFPredicate predicate);
20
21 Optional<Object[]> oneResult(DNFPredicate predicate, Object[] parameters);
22
23 Stream<Object[]> allResults(DNFPredicate predicate);
24
25 Stream<Object[]> allResults(DNFPredicate predicate, Object[] parameters);
26
27 int countResults(DNFPredicate predicate);
28
29 int countResults(DNFPredicate predicate, Object[] parameters);
30}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/QueriableModelStore.java b/subprojects/store/src/main/java/tools/refinery/store/query/QueriableModelStore.java
new file mode 100644
index 00000000..3a5b51ff
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/QueriableModelStore.java
@@ -0,0 +1,23 @@
1package tools.refinery.store.query;
2
3import java.util.Set;
4
5import tools.refinery.store.model.ModelDiffCursor;
6import tools.refinery.store.model.ModelStore;
7import tools.refinery.store.model.representation.DataRepresentation;
8import tools.refinery.store.query.building.DNFPredicate;
9import tools.refinery.store.query.view.RelationView;
10
11public interface QueriableModelStore extends ModelStore{
12 @SuppressWarnings("squid:S1452")
13 Set<DataRepresentation<?, ?>> getDataRepresentations();
14 @SuppressWarnings("squid:S1452")
15 Set<RelationView<?>> getViews();
16 Set<DNFPredicate> getPredicates();
17
18 QueriableModel createModel();
19 QueriableModel createModel(long state);
20
21 Set<Long> getStates();
22 ModelDiffCursor getDiffCursor(long from, long to);
23}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/QueriableModelStoreImpl.java b/subprojects/store/src/main/java/tools/refinery/store/query/QueriableModelStoreImpl.java
new file mode 100644
index 00000000..653783dd
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/QueriableModelStoreImpl.java
@@ -0,0 +1,127 @@
1package tools.refinery.store.query;
2
3import java.util.Collections;
4import java.util.HashMap;
5import java.util.Map;
6import java.util.Set;
7
8import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
9
10import tools.refinery.store.model.ModelDiffCursor;
11import tools.refinery.store.model.ModelStore;
12import tools.refinery.store.model.ModelStoreImpl;
13import tools.refinery.store.model.representation.DataRepresentation;
14import tools.refinery.store.query.building.DNFAnd;
15import tools.refinery.store.query.building.DNFAtom;
16import tools.refinery.store.query.building.DNFPredicate;
17import tools.refinery.store.query.building.PredicateAtom;
18import tools.refinery.store.query.building.RelationAtom;
19import tools.refinery.store.query.internal.DNF2PQuery;
20import tools.refinery.store.query.internal.QueriableModelImpl;
21import tools.refinery.store.query.internal.RawPatternMatcher;
22import tools.refinery.store.query.internal.DNF2PQuery.SimplePQuery;
23import tools.refinery.store.query.view.RelationView;
24
25public class QueriableModelStoreImpl implements QueriableModelStore {
26 protected final ModelStore store;
27 protected final Set<RelationView<?>> relationViews;
28 protected final Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> predicates;
29
30 public QueriableModelStoreImpl(Set<DataRepresentation<?, ?>> dataRepresentations,
31 Set<RelationView<?>> relationViews, Set<DNFPredicate> predicates) {
32 this.store = new ModelStoreImpl(dataRepresentations);
33 validateViews(dataRepresentations, relationViews);
34 this.relationViews = Collections.unmodifiableSet(relationViews);
35 validatePredicates(relationViews, predicates);
36 this.predicates = initPredicates(predicates);
37 }
38
39 private void validateViews(Set<DataRepresentation<?, ?>> dataRepresentations, Set<RelationView<?>> relationViews) {
40 for (RelationView<?> relationView : relationViews) {
41 // TODO: make it work for non-relation representation?
42 if (!dataRepresentations.contains(relationView.getRepresentation())) {
43 throw new IllegalArgumentException(
44 DataRepresentation.class.getSimpleName() + " " + relationView.getStringID() + " added to "
45 + QueriableModelStore.class.getSimpleName() + " without a referred representation.");
46 }
47 }
48 }
49
50 private void validatePredicates(Set<RelationView<?>> relationViews, Set<DNFPredicate> predicates) {
51 for (DNFPredicate dnfPredicate : predicates) {
52 for (DNFAnd clause : dnfPredicate.getClauses()) {
53 for (DNFAtom atom : clause.getConstraints()) {
54 if (atom instanceof RelationAtom relationAtom) {
55 validateRelationAtom(relationViews, dnfPredicate, relationAtom);
56 } else if (atom instanceof PredicateAtom predicateAtom) {
57 validatePredicateAtom(predicates, dnfPredicate, predicateAtom);
58 }
59 }
60 }
61 }
62 }
63
64 private void validateRelationAtom(Set<RelationView<?>> relationViews, DNFPredicate dnfPredicate,
65 RelationAtom relationAtom) {
66 if (!relationViews.contains(relationAtom.getView())) {
67 throw new IllegalArgumentException(DNFPredicate.class.getSimpleName() + " "
68 + dnfPredicate.getUniqueName() + " contains reference to a view of "
69 + relationAtom.getView().getRepresentation().getName()
70 + " that is not in the model.");
71 }
72 }
73 private void validatePredicateAtom(Set<DNFPredicate> predicates, DNFPredicate dnfPredicate,
74 PredicateAtom predicateAtom) {
75 if (!predicates.contains(predicateAtom.getReferred())) {
76 throw new IllegalArgumentException(
77 DNFPredicate.class.getSimpleName() + " " + dnfPredicate.getUniqueName()
78 + " contains reference to a predicate "
79 + predicateAtom.getReferred().getName()
80 + "that is not in the model.");
81 }
82 }
83
84 private Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> initPredicates(Set<DNFPredicate> predicates) {
85 Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> result = new HashMap<>();
86 Map<DNFPredicate, SimplePQuery> dnf2PQueryMap = new HashMap<>();
87 for (DNFPredicate dnfPredicate : predicates) {
88 GenericQuerySpecification<RawPatternMatcher> query = DNF2PQuery.translate(dnfPredicate,dnf2PQueryMap).build();
89 result.put(dnfPredicate, query);
90 }
91
92 return result;
93 }
94
95 @Override
96 public Set<DataRepresentation<?, ?>> getDataRepresentations() {
97 return store.getDataRepresentations();
98 }
99 @Override
100 public Set<RelationView<?>> getViews() {
101 return this.relationViews;
102 }
103 @Override
104 public Set<DNFPredicate> getPredicates() {
105 return predicates.keySet();
106 }
107
108 @Override
109 public QueriableModel createModel() {
110 return new QueriableModelImpl(this, this.store.createModel(), predicates);
111 }
112
113 @Override
114 public QueriableModel createModel(long state) {
115 return new QueriableModelImpl(this, this.store.createModel(state), predicates);
116 }
117
118 @Override
119 public synchronized Set<Long> getStates() {
120 return this.store.getStates();
121 }
122
123 @Override
124 public synchronized ModelDiffCursor getDiffCursor(long from, long to) {
125 return this.store.getDiffCursor(from, to);
126 }
127}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/building/DNFAnd.java b/subprojects/store/src/main/java/tools/refinery/store/query/building/DNFAnd.java
new file mode 100644
index 00000000..48dabce2
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/building/DNFAnd.java
@@ -0,0 +1,37 @@
1package tools.refinery.store.query.building;
2
3import java.util.HashMap;
4import java.util.HashSet;
5import java.util.List;
6import java.util.Map;
7import java.util.Set;
8
9public class DNFAnd {
10 private Set<Variable> existentiallyQuantified;
11 private List<DNFAtom> constraints;
12 public DNFAnd(Set<Variable> quantifiedVariables, List<DNFAtom> constraints) {
13 super();
14 this.existentiallyQuantified = quantifiedVariables;
15 this.constraints = constraints;
16 }
17 public Set<Variable> getExistentiallyQuantified() {
18 return existentiallyQuantified;
19 }
20 public List<DNFAtom> getConstraints() {
21 return constraints;
22 }
23 void unifyVariables(Map<String,Variable> uniqueVariableMap) {
24 Map<String,Variable> uniqueVariableMapForClause = new HashMap<>(uniqueVariableMap);
25 for(DNFAtom atom : constraints) {
26 atom.unifyVariables(uniqueVariableMapForClause);
27 }
28 }
29 void collectQuantifiedVariables(Set<Variable> parameters) {
30 Set<Variable> result = new HashSet<>();
31 for(DNFAtom constraint : constraints) {
32 constraint.collectAllVariables(result);
33 }
34 result.removeAll(parameters);
35 existentiallyQuantified = result;
36 }
37}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/building/DNFAtom.java b/subprojects/store/src/main/java/tools/refinery/store/query/building/DNFAtom.java
new file mode 100644
index 00000000..b047d7c8
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/building/DNFAtom.java
@@ -0,0 +1,33 @@
1package tools.refinery.store.query.building;
2
3import java.util.Collection;
4import java.util.Iterator;
5import java.util.Map;
6import java.util.Set;
7
8public interface DNFAtom {
9 void unifyVariables(Map<String,Variable> variables);
10 static Variable unifyVariables(Map<String,Variable> unifiedVariables, Variable variable) {
11 if(variable != null) {
12 if(variable.isNamed() && unifiedVariables.containsKey(variable.getName())) {
13 return unifiedVariables.get(variable.getName());
14 }
15 return variable;
16 } else {
17 return null;
18 }
19 }
20 void collectAllVariables(Set<Variable> variables);
21 static void addToCollection(Set<Variable> variables, Variable variable) {
22 if(variable != null) {
23 variables.add(variable);
24 }
25 }
26 static void addToCollection(Set<Variable> variables, Collection<Variable> variableCollection) {
27 Iterator<Variable> iterator = variableCollection.iterator();
28 while(iterator.hasNext()) {
29 Variable variable = iterator.next();
30 addToCollection(variables, variable);
31 }
32 }
33}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/building/DNFPredicate.java b/subprojects/store/src/main/java/tools/refinery/store/query/building/DNFPredicate.java
new file mode 100644
index 00000000..f0c9ac42
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/building/DNFPredicate.java
@@ -0,0 +1,72 @@
1package tools.refinery.store.query.building;
2
3import java.util.HashMap;
4import java.util.HashSet;
5import java.util.List;
6import java.util.Map;
7import java.util.UUID;
8
9public class DNFPredicate {
10 private final String name;
11 private final String uniqueName;
12 private final List<Variable> parameters;
13 private final List<DNFAnd> clauses;
14
15 public DNFPredicate(String name, List<Variable> parameters, List<DNFAnd> clauses) {
16 this.name = name;
17 this.uniqueName = generateUniqueName(name,"predicate");
18 this.parameters = parameters;
19 this.clauses = clauses;
20
21 postProcess();
22 }
23
24 public static String generateUniqueName(String originalName, String defaultPrefix) {
25 UUID uuid = UUID.randomUUID();
26 String uniqueString = uuid.toString().replace('-', '_');
27 if(originalName == null) {
28 return defaultPrefix+uniqueString;
29 } else {
30 return originalName+uniqueString;
31 }
32 }
33
34 public String getName() {
35 return name;
36 }
37 public String getUniqueName() {
38 return uniqueName;
39 }
40 public List<Variable> getVariables() {
41 return parameters;
42 }
43 public List<DNFAnd> getClauses() {
44 return clauses;
45 }
46
47 public void unifyVariables() {
48 Map<String,Variable> uniqueVariableMap = new HashMap<>();
49 for(Variable parameter : this.parameters) {
50 if(parameter.isNamed()) {
51 String parameterName = parameter.getName();
52 if(uniqueVariableMap.containsKey(parameterName)) {
53 throw new IllegalArgumentException("Multiple parameters has the name "+parameterName);
54 } else {
55 uniqueVariableMap.put(parameterName, parameter);
56 }
57 }
58 }
59 for(DNFAnd clause : this.clauses) {
60 clause.unifyVariables(uniqueVariableMap);
61 }
62 }
63 public void collectQuantifiedVariables() {
64 for(DNFAnd clause : this.clauses) {
65 clause.collectQuantifiedVariables(new HashSet<>(parameters));
66 }
67 }
68 public void postProcess() {
69 unifyVariables();
70 collectQuantifiedVariables();
71 }
72}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/building/EquivalenceAtom.java b/subprojects/store/src/main/java/tools/refinery/store/query/building/EquivalenceAtom.java
new file mode 100644
index 00000000..fede2518
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/building/EquivalenceAtom.java
@@ -0,0 +1,44 @@
1package tools.refinery.store.query.building;
2
3import java.util.Map;
4import java.util.Set;
5
6public class EquivalenceAtom implements DNFAtom{
7 private boolean positive;
8 private Variable left;
9 private Variable right;
10 public EquivalenceAtom(boolean positive, Variable left, Variable right) {
11 this.positive = positive;
12 this.left = left;
13 this.right = right;
14 }
15 public boolean isPositive() {
16 return positive;
17 }
18 public void setPositive(boolean positive) {
19 this.positive = positive;
20 }
21 public Variable getLeft() {
22 return left;
23 }
24 public void setLeft(Variable left) {
25 this.left = left;
26 }
27 public Variable getRight() {
28 return right;
29 }
30 public void setRight(Variable right) {
31 this.right = right;
32 }
33
34 @Override
35 public void unifyVariables(Map<String, Variable> variables) {
36 this.left = DNFAtom.unifyVariables(variables,left);
37 this.right = DNFAtom.unifyVariables(variables,right);
38 }
39 @Override
40 public void collectAllVariables(Set<Variable> variables) {
41 DNFAtom.addToCollection(variables, left);
42 DNFAtom.addToCollection(variables, right);
43 }
44}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/building/PredicateAtom.java b/subprojects/store/src/main/java/tools/refinery/store/query/building/PredicateAtom.java
new file mode 100644
index 00000000..42394922
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/building/PredicateAtom.java
@@ -0,0 +1,66 @@
1package tools.refinery.store.query.building;
2
3import java.util.List;
4import java.util.Map;
5import java.util.Set;
6
7public class PredicateAtom implements DNFAtom {
8 private DNFPredicate referred;
9 private List<Variable> substitution;
10 private boolean positive;
11 private boolean transitive;
12
13 public PredicateAtom(boolean positive, boolean transitive, DNFPredicate referred, List<Variable> substitution) {
14 this.positive = positive;
15 this.referred = referred;
16 this.substitution = substitution;
17 this.transitive = transitive;
18 }
19
20 public DNFPredicate getReferred() {
21 return referred;
22 }
23
24 public void setReferred(DNFPredicate referred) {
25 this.referred = referred;
26 }
27
28 public List<Variable> getSubstitution() {
29 return substitution;
30 }
31
32 public void setSubstitution(List<Variable> substitution) {
33 this.substitution = substitution;
34 }
35
36 public boolean isPositive() {
37 return positive;
38 }
39
40 public void setPositive(boolean positive) {
41 this.positive = positive;
42 }
43
44 public boolean isTransitive() {
45 return transitive;
46 }
47
48 public void setTransitive(boolean transitive) {
49 this.transitive = transitive;
50 }
51
52 @Override
53 public void unifyVariables(Map<String, Variable> variables) {
54 for (int i = 0; i < this.substitution.size(); i++) {
55 final Object term = this.substitution.get(i);
56 if (term instanceof Variable variableReference) {
57 this.substitution.set(i, DNFAtom.unifyVariables(variables, variableReference));
58 }
59 }
60 }
61
62 @Override
63 public void collectAllVariables(Set<Variable> variables) {
64 DNFAtom.addToCollection(variables, substitution);
65 }
66}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/building/RelationAtom.java b/subprojects/store/src/main/java/tools/refinery/store/query/building/RelationAtom.java
new file mode 100644
index 00000000..1238f1d7
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/building/RelationAtom.java
@@ -0,0 +1,49 @@
1package tools.refinery.store.query.building;
2
3import java.util.List;
4import java.util.Map;
5import java.util.Set;
6
7import tools.refinery.store.query.view.FilteredRelationView;
8import tools.refinery.store.query.view.RelationView;
9
10public class RelationAtom implements DNFAtom {
11 RelationView<?> view;
12 List<Variable> substitution;
13
14 public RelationAtom(RelationView<?> view, List<Variable> substitution) {
15 this.view = view;
16 this.substitution = substitution;
17 }
18
19 public RelationView<?> getView() {
20 return view;
21 }
22
23 public void setView(FilteredRelationView<?> view) {
24 this.view = view;
25 }
26
27 public List<Variable> getSubstitution() {
28 return substitution;
29 }
30
31 public void setSubstitution(List<Variable> substitution) {
32 this.substitution = substitution;
33 }
34
35 @Override
36 public void unifyVariables(Map<String, Variable> variables) {
37 for (int i = 0; i < this.substitution.size(); i++) {
38 final Object term = this.substitution.get(i);
39 if (term instanceof Variable variableReference) {
40 this.substitution.set(i, DNFAtom.unifyVariables(variables, variableReference));
41 }
42 }
43 }
44
45 @Override
46 public void collectAllVariables(Set<Variable> variables) {
47 DNFAtom.addToCollection(variables, substitution);
48 }
49}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/building/Variable.java b/subprojects/store/src/main/java/tools/refinery/store/query/building/Variable.java
new file mode 100644
index 00000000..9ea7ce83
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/building/Variable.java
@@ -0,0 +1,22 @@
1package tools.refinery.store.query.building;
2
3public class Variable {
4 private final String name;
5 private final String uniqueName;
6
7 public Variable(String name) {
8 super();
9 this.name = name;
10 this.uniqueName = DNFPredicate.generateUniqueName(name, "variable");
11
12 }
13 public String getName() {
14 return name;
15 }
16 public String getUniqueName() {
17 return uniqueName;
18 }
19 public boolean isNamed() {
20 return name != null;
21 }
22}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/internal/DNF2PQuery.java b/subprojects/store/src/main/java/tools/refinery/store/query/internal/DNF2PQuery.java
new file mode 100644
index 00000000..bcc03fb4
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/internal/DNF2PQuery.java
@@ -0,0 +1,189 @@
1package tools.refinery.store.query.internal;
2
3import java.util.ArrayList;
4import java.util.HashMap;
5import java.util.InputMismatchException;
6import java.util.LinkedHashSet;
7import java.util.List;
8import java.util.Map;
9import java.util.Set;
10
11import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
12import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine;
13import org.eclipse.viatra.query.runtime.api.scope.QueryScope;
14import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
15import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
16import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
17import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality;
18import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter;
19import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Inequality;
20import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.NegativePatternCall;
21import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
22import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
23import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
24import org.eclipse.viatra.query.runtime.matchers.psystem.queries.BasePQuery;
25import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
26import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility;
27import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
28
29import tools.refinery.store.query.building.DNFAnd;
30import tools.refinery.store.query.building.DNFAtom;
31import tools.refinery.store.query.building.DNFPredicate;
32import tools.refinery.store.query.building.EquivalenceAtom;
33import tools.refinery.store.query.building.PredicateAtom;
34import tools.refinery.store.query.building.RelationAtom;
35import tools.refinery.store.query.building.Variable;
36
37public class DNF2PQuery {
38
39 public static SimplePQuery translate(DNFPredicate predicate, Map<DNFPredicate, SimplePQuery> dnf2PQueryMap) {
40 SimplePQuery query = dnf2PQueryMap.get(predicate);
41 if (query != null) {
42 return query;
43 }
44 query = new DNF2PQuery().new SimplePQuery(predicate.getName());
45 Map<Variable, PParameter> parameters = new HashMap<>();
46
47 predicate.getVariables().forEach(variable -> parameters.put(variable, new PParameter(variable.getName())));
48 List<PParameter> parameterList = new ArrayList<>();
49 for(var param : predicate.getVariables()) {
50 parameterList.add(parameters.get(param));
51 }
52 query.setParameter(parameterList);
53 for (DNFAnd clause : predicate.getClauses()) {
54 PBody body = new PBody(query);
55 List<ExportedParameter> symbolicParameters = new ArrayList<>();
56 for(var param : predicate.getVariables()) {
57 PVariable pVar = body.getOrCreateVariableByName(param.getName());
58 symbolicParameters.add(new ExportedParameter(body, pVar, parameters.get(param)));
59 }
60 body.setSymbolicParameters(symbolicParameters);
61 query.addBody(body);
62 for (DNFAtom constraint : clause.getConstraints()) {
63 translateDNFAtom(constraint, body, dnf2PQueryMap);
64 }
65 }
66 dnf2PQueryMap.put(predicate, query);
67 return query;
68 }
69
70 private static void translateDNFAtom(DNFAtom constraint, PBody body, Map<DNFPredicate, SimplePQuery> dnf2PQueryMap) {
71 if (constraint instanceof EquivalenceAtom equivalence) {
72 translateEquivalenceAtom(equivalence, body);
73 }
74 if (constraint instanceof RelationAtom relation) {
75 translateRelationAtom(relation, body);
76 }
77 if (constraint instanceof PredicateAtom predicate) {
78 translatePredicateAtom(predicate, body, dnf2PQueryMap);
79 }
80 }
81
82 private static void translateEquivalenceAtom(EquivalenceAtom equivalence, PBody body) {
83 PVariable varSource = body.getOrCreateVariableByName(equivalence.getLeft().getName());
84 PVariable varTarget = body.getOrCreateVariableByName(equivalence.getRight().getName());
85 if (equivalence.isPositive())
86 new Equality(body, varSource, varTarget);
87 else
88 new Inequality(body, varSource, varTarget);
89 }
90
91 private static void translateRelationAtom(RelationAtom relation, PBody body) {
92 if (relation.getSubstitution().size() != relation.getView().getArity()) {
93 throw new IllegalArgumentException("Arity (" + relation.getView().getArity()
94 + ") does not match parameter numbers (" + relation.getSubstitution().size() + ")");
95 }
96 Object[] variables = new Object[relation.getSubstitution().size()];
97 for (int i = 0; i < relation.getSubstitution().size(); i++) {
98 variables[i] = body.getOrCreateVariableByName(relation.getSubstitution().get(i).getName());
99 }
100 new TypeConstraint(body, Tuples.flatTupleOf(variables), relation.getView());
101 }
102
103 private static void translatePredicateAtom(PredicateAtom predicate, PBody body, Map<DNFPredicate, SimplePQuery> dnf2PQueryMap) {
104 Object[] variables = new Object[predicate.getSubstitution().size()];
105 for (int i = 0; i < predicate.getSubstitution().size(); i++) {
106 variables[i] = body.getOrCreateVariableByName(predicate.getSubstitution().get(i).getName());
107 }
108 if (predicate.isPositive()) {
109 if (predicate.isTransitive()) {
110 if (predicate.getSubstitution().size() != 2) {
111 throw new IllegalArgumentException("Transitive Predicate Atoms must be binary.");
112 }
113 new BinaryTransitiveClosure(body, Tuples.flatTupleOf(variables),
114 DNF2PQuery.translate(predicate.getReferred(), dnf2PQueryMap));
115 } else {
116 new PositivePatternCall(body, Tuples.flatTupleOf(variables),
117 DNF2PQuery.translate(predicate.getReferred(), dnf2PQueryMap));
118 }
119 } else {
120 if (predicate.isTransitive()) {
121 throw new InputMismatchException("Transitive Predicate Atoms cannot be negative.");
122 } else {
123 new NegativePatternCall(body, Tuples.flatTupleOf(variables),
124 DNF2PQuery.translate(predicate.getReferred(), dnf2PQueryMap));
125 }
126 }
127 }
128
129 public class SimplePQuery extends BasePQuery {
130
131 private String fullyQualifiedName;
132 private List<PParameter> parameters;
133 private LinkedHashSet<PBody> bodies = new LinkedHashSet<>();
134
135 public SimplePQuery(String name) {
136 super(PVisibility.PUBLIC);
137 fullyQualifiedName = name;
138 }
139
140 @Override
141 public String getFullyQualifiedName() {
142 return fullyQualifiedName;
143 }
144
145 public void setParameter(List<PParameter> parameters) {
146 this.parameters = parameters;
147 }
148
149 @Override
150 public List<PParameter> getParameters() {
151 return parameters;
152 }
153
154 public void addBody(PBody body) {
155 bodies.add(body);
156 }
157
158 @Override
159 protected Set<PBody> doGetContainedBodies() {
160 setEvaluationHints(new QueryEvaluationHint(null, QueryEvaluationHint.BackendRequirement.UNSPECIFIED));
161 return bodies;
162 }
163
164 public GenericQuerySpecification<RawPatternMatcher> build() {
165 return new GenericQuerySpecification<RawPatternMatcher>(this) {
166
167 @Override
168 public Class<? extends QueryScope> getPreferredScopeClass() {
169 return RelationalScope.class;
170 }
171
172 @Override
173 protected RawPatternMatcher instantiate(ViatraQueryEngine engine) {
174 RawPatternMatcher matcher = engine.getExistingMatcher(this);
175 if (matcher == null) {
176 matcher = engine.getMatcher(this);
177 }
178 return matcher;
179 }
180
181 @Override
182 public RawPatternMatcher instantiate() {
183 return new RawPatternMatcher(this);
184 }
185
186 };
187 }
188 }
189} \ No newline at end of file
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/internal/DummyBaseIndexer.java b/subprojects/store/src/main/java/tools/refinery/store/query/internal/DummyBaseIndexer.java
new file mode 100644
index 00000000..49637071
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/internal/DummyBaseIndexer.java
@@ -0,0 +1,59 @@
1package tools.refinery.store.query.internal;
2
3import java.lang.reflect.InvocationTargetException;
4import java.util.concurrent.Callable;
5
6import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex;
7import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener;
8import org.eclipse.viatra.query.runtime.api.scope.IInstanceObserver;
9import org.eclipse.viatra.query.runtime.api.scope.ViatraBaseIndexChangeListener;
10
11/**
12 * copied from org.eclipse.viatra.query.runtime.tabular.TabularEngineContext;
13 */
14public class DummyBaseIndexer implements IBaseIndex{
15
16 @Override
17 public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException {
18 try {
19 return callable.call();
20 } catch (Exception e) {
21 throw new InvocationTargetException(e);
22 }
23 }
24
25 @Override
26 public void addBaseIndexChangeListener(ViatraBaseIndexChangeListener listener) {
27 // no notification support
28 }
29
30 @Override
31 public void removeBaseIndexChangeListener(ViatraBaseIndexChangeListener listener) {
32 // no notification support
33 }
34
35 @Override
36 public void resampleDerivedFeatures() {
37 throw new UnsupportedOperationException();
38 }
39
40 @Override
41 public boolean addIndexingErrorListener(IIndexingErrorListener listener) {
42 return true;
43 }
44
45 @Override
46 public boolean removeIndexingErrorListener(IIndexingErrorListener listener) {
47 return true;
48 }
49
50 @Override
51 public boolean addInstanceObserver(IInstanceObserver observer, Object observedObject) {
52 return true;
53 }
54
55 @Override
56 public boolean removeInstanceObserver(IInstanceObserver observer, Object observedObject) {
57 return true;
58 }
59}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/internal/ModelUpdateListener.java b/subprojects/store/src/main/java/tools/refinery/store/query/internal/ModelUpdateListener.java
new file mode 100644
index 00000000..aa80985f
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/internal/ModelUpdateListener.java
@@ -0,0 +1,103 @@
1package tools.refinery.store.query.internal;
2
3import java.util.HashMap;
4import java.util.HashSet;
5import java.util.Map;
6import java.util.Set;
7
8import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
9import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
10
11import tools.refinery.store.model.Tuple;
12import tools.refinery.store.model.representation.Relation;
13import tools.refinery.store.query.view.RelationView;
14
15public class ModelUpdateListener {
16 /**
17 * Collections of Relations and their Views.
18 */
19 private final Map<Relation<?>, Set<RelationView<?>>> relation2View;
20 /**
21 * Collection of Views and their buffers.
22 */
23 private final Map<RelationView<?>, Set<ViewUpdateBuffer<?>>> view2Buffers;
24
25 public ModelUpdateListener(Set<RelationView<?>> relationViews) {
26 this.relation2View = new HashMap<>();
27 this.view2Buffers = new HashMap<>();
28
29 for (RelationView<?> relationView : relationViews) {
30 registerView(relationView);
31 }
32 }
33
34 private void registerView(RelationView<?> view) {
35 Relation<?> relation = view.getRepresentation();
36
37 // 1. register views to relations, if necessary
38 var views = relation2View.computeIfAbsent(relation, x->new HashSet<>());
39 views.add(view);
40
41 // 2. register notifier map to views, if necessary
42 view2Buffers.computeIfAbsent(view, x->new HashSet<>());
43 }
44
45 boolean containsRelationalView(RelationView<?> relationalKey) {
46 return view2Buffers.containsKey(relationalKey);
47 }
48
49 <D> void addListener(RelationView<D> relationView, ITuple seed, IQueryRuntimeContextListener listener) {
50 if (view2Buffers.containsKey(relationView)) {
51 ViewUpdateTranslator<D> updateListener = new ViewUpdateTranslator<>(relationView, seed, listener);
52 ViewUpdateBuffer<D> updateBuffer = new ViewUpdateBuffer<>(updateListener);
53 view2Buffers.get(relationView).add(updateBuffer);
54 } else
55 throw new IllegalArgumentException();
56 }
57
58 void removeListener(RelationView<?> relationView, ITuple seed, IQueryRuntimeContextListener listener) {
59 if (view2Buffers.containsKey(relationView)) {
60 Set<ViewUpdateBuffer<?>> buffers = this.view2Buffers.get(relationView);
61 for(var buffer : buffers) {
62 if(buffer.getUpdateListener().key == seed && buffer.getUpdateListener().listener == listener) {
63 // remove buffer and terminate immediately, or it will break iterator.
64 buffers.remove(buffer);
65 return;
66 }
67 }
68 } else
69 throw new IllegalArgumentException();
70 }
71
72 public <D> void addUpdate(Relation<D> relation, Tuple key, D oldValue, D newValue) {
73 var views = this.relation2View.get(relation);
74 if (views != null) {
75 for (var view : views) {
76 var buffers = this.view2Buffers.get(view);
77 for (var buffer : buffers) {
78 @SuppressWarnings("unchecked")
79 var typedBuffer = (ViewUpdateBuffer<D>) buffer;
80 typedBuffer.addChange(key, oldValue, newValue);
81 }
82 }
83 }
84 }
85
86 public boolean hasChange() {
87 for (var bufferCollection : this.view2Buffers.values()) {
88 for (ViewUpdateBuffer<?> buffer : bufferCollection) {
89 if (buffer.hasChange())
90 return true;
91 }
92 }
93 return false;
94 }
95
96 public void flush() {
97 for (var bufferCollection : this.view2Buffers.values()) {
98 for (ViewUpdateBuffer<?> buffer : bufferCollection) {
99 buffer.flush();
100 }
101 }
102 }
103}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/internal/PredicateResult.java b/subprojects/store/src/main/java/tools/refinery/store/query/internal/PredicateResult.java
new file mode 100644
index 00000000..65d23eb6
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/internal/PredicateResult.java
@@ -0,0 +1,24 @@
1package tools.refinery.store.query.internal;
2
3import java.util.Optional;
4import java.util.stream.Stream;
5
6public interface PredicateResult {
7
8 boolean hasResult();
9
10 boolean hasResult(Object[] parameters);
11
12 Optional<Object[]> oneResult();
13
14 Optional<Object[]> oneResult(Object[] parameters);
15
16 Stream<Object[]> allResults();
17
18 Stream<Object[]> allResults(Object[] parameters);
19
20 int countResults();
21
22 int countResults(Object[] parameters);
23
24} \ No newline at end of file
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/internal/QueriableModelImpl.java b/subprojects/store/src/main/java/tools/refinery/store/query/internal/QueriableModelImpl.java
new file mode 100644
index 00000000..0f4d609f
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/internal/QueriableModelImpl.java
@@ -0,0 +1,212 @@
1package tools.refinery.store.query.internal;
2
3import java.util.HashMap;
4import java.util.Map;
5import java.util.Optional;
6import java.util.Set;
7import java.util.stream.Stream;
8
9import org.eclipse.viatra.query.runtime.api.AdvancedViatraQueryEngine;
10import org.eclipse.viatra.query.runtime.api.GenericQueryGroup;
11import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
12import org.eclipse.viatra.query.runtime.api.IQueryGroup;
13
14import tools.refinery.store.map.Cursor;
15import tools.refinery.store.map.DiffCursor;
16import tools.refinery.store.model.Model;
17import tools.refinery.store.model.ModelDiffCursor;
18import tools.refinery.store.model.Tuple;
19import tools.refinery.store.model.representation.DataRepresentation;
20import tools.refinery.store.model.representation.Relation;
21import tools.refinery.store.query.QueriableModel;
22import tools.refinery.store.query.QueriableModelStore;
23import tools.refinery.store.query.building.DNFPredicate;
24
25public class QueriableModelImpl implements QueriableModel {
26 protected final QueriableModelStore store;
27 protected final Model model;
28 protected final Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> predicates2PQuery;
29
30 protected RelationalScope scope;
31 protected AdvancedViatraQueryEngine engine;
32 protected Map<DNFPredicate, RawPatternMatcher> predicate2Matcher;
33
34 public QueriableModelImpl(QueriableModelStore store, Model model,
35 Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> predicates2PQuery) {
36 this.store = store;
37 this.model = model;
38 this.predicates2PQuery = predicates2PQuery;
39 initEngine();
40 }
41
42 private void initEngine() {
43 this.scope = new RelationalScope(this.model, this.store.getViews());
44 this.engine = AdvancedViatraQueryEngine.createUnmanagedEngine(this.scope);
45 this.predicate2Matcher = initMatchers(this.engine, this.predicates2PQuery);
46 }
47
48 private Map<DNFPredicate, RawPatternMatcher> initMatchers(AdvancedViatraQueryEngine engine,
49 Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> predicates2pQuery) {
50 // 1. prepare group
51 IQueryGroup queryGroup = GenericQueryGroup.of(Set.copyOf(predicates2pQuery.values()));
52 engine.prepareGroup(queryGroup, null);
53
54 // 2. then get all matchers
55 Map<DNFPredicate, RawPatternMatcher> result = new HashMap<>();
56 for (var entry : predicates2pQuery.entrySet()) {
57 var matcher = engine.getMatcher(entry.getValue());
58 result.put(entry.getKey(), matcher);
59 }
60 return result;
61 }
62
63 @Override
64 public Set<DataRepresentation<?, ?>> getDataRepresentations() {
65 return model.getDataRepresentations();
66 }
67
68 @Override
69 public Set<DNFPredicate> getPredicates() {
70 return store.getPredicates();
71 }
72
73 @Override
74 public <K, V> V get(DataRepresentation<K, V> representation, K key) {
75 return model.get(representation, key);
76 }
77
78 @Override
79 public <K, V> Cursor<K, V> getAll(DataRepresentation<K, V> representation) {
80 return model.getAll(representation);
81 }
82
83 @SuppressWarnings("unchecked")
84 @Override
85 public <K, V> V put(DataRepresentation<K, V> representation, K key, V value) {
86 V oldValue = this.model.put(representation, key, value);
87 if(representation instanceof Relation<?> relation) {
88 this.scope.processUpdate((Relation<V>)relation, (Tuple)key, oldValue, value);
89 }
90 return oldValue;
91 }
92
93 @Override
94 public <K, V> void putAll(DataRepresentation<K, V> representation, Cursor<K, V> cursor) {
95 if(representation instanceof Relation<?>) {
96 @SuppressWarnings("unchecked")
97 Relation<V> relation = (Relation<V>) representation;
98 while(cursor.move()) {
99 Tuple key = (Tuple) cursor.getKey();
100 V newValue = cursor.getValue();
101 V oldValue = this.model.put(relation, key, newValue);
102 this.scope.processUpdate(relation, key, oldValue, newValue);
103 }
104 } else {
105 this.model.putAll(representation, cursor);
106 }
107 }
108
109 @Override
110 public <K, V> long getSize(DataRepresentation<K, V> representation) {
111 return model.getSize(representation);
112 }
113
114 protected PredicateResult getPredicateResult(DNFPredicate predicate) {
115 var result = this.predicate2Matcher.get(predicate);
116 if (result == null) {
117 throw new IllegalArgumentException("Model does not contain predicate " + predicate.getName() + "!");
118 } else
119 return result;
120 }
121
122 protected void validateParameters(DNFPredicate predicate, Object[] parameters) {
123 int predicateArity = predicate.getVariables().size();
124 int parameterArity = parameters.length;
125 if (parameterArity != predicateArity) {
126 throw new IllegalArgumentException("Predicate " + predicate.getName() + " with " + predicateArity
127 + " arity called with different number of parameters (" + parameterArity + ")!");
128 }
129 }
130
131 @Override
132 public boolean hasResult(DNFPredicate predicate) {
133 return getPredicateResult(predicate).hasResult();
134 }
135
136 @Override
137 public boolean hasResult(DNFPredicate predicate, Object[] parameters) {
138 validateParameters(predicate, parameters);
139 return getPredicateResult(predicate).hasResult(parameters);
140 }
141
142 @Override
143 public Optional<Object[]> oneResult(DNFPredicate predicate){
144 return getPredicateResult(predicate).oneResult();
145 }
146
147 @Override
148 public Optional<Object[]> oneResult(DNFPredicate predicate, Object[] parameters){
149 validateParameters(predicate, parameters);
150 return getPredicateResult(predicate).oneResult(parameters);
151 }
152
153 @Override
154 public Stream<Object[]> allResults(DNFPredicate predicate){
155 return getPredicateResult(predicate).allResults();
156 }
157
158 @Override
159 public Stream<Object[]> allResults(DNFPredicate predicate, Object[] parameters){
160 validateParameters(predicate, parameters);
161 return getPredicateResult(predicate).allResults(parameters);
162 }
163
164 @Override
165 public int countResults(DNFPredicate predicate){
166 return getPredicateResult(predicate).countResults();
167 }
168
169 @Override
170 public int countResults(DNFPredicate predicate, Object[] parameters){
171 validateParameters(predicate, parameters);
172 return getPredicateResult(predicate).countResults(parameters);
173
174 }
175 @Override
176 public void flushChanges() {
177 this.scope.flush();
178 }
179
180 @Override
181 public ModelDiffCursor getDiffCursor(long to) {
182 return model.getDiffCursor(to);
183 }
184
185 @Override
186 public long commit() {
187 return this.model.commit();
188 }
189
190 @Override
191 public void restore(long state) {
192 restoreWithDiffReplay(state);
193 }
194
195 public void restoreWithDiffReplay(long state) {
196 var modelDiffCursor = getDiffCursor(state);
197 for(DataRepresentation<?,?> dataRepresentation : this.getDataRepresentations()) {
198 restoreRepresentationWithDiffReplay(modelDiffCursor, dataRepresentation);
199 }
200 }
201
202 private <K,V> void restoreRepresentationWithDiffReplay(ModelDiffCursor modelDiffCursor,
203 DataRepresentation<K, V> dataRepresentation) {
204 DiffCursor<K,V> diffCursor = modelDiffCursor.getCursor(dataRepresentation);
205 this.putAll(dataRepresentation, diffCursor);
206 }
207
208 public void restoreWithReinit(long state) {
209 model.restore(state);
210 this.initEngine();
211 }
212}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/internal/RawPatternMatcher.java b/subprojects/store/src/main/java/tools/refinery/store/query/internal/RawPatternMatcher.java
new file mode 100644
index 00000000..c6d6353c
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/internal/RawPatternMatcher.java
@@ -0,0 +1,57 @@
1package tools.refinery.store.query.internal;
2
3import java.util.Optional;
4import java.util.stream.Stream;
5
6import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher;
7import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
8import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
9import org.eclipse.viatra.query.runtime.matchers.tuple.AbstractTuple;
10
11public class RawPatternMatcher extends GenericPatternMatcher implements PredicateResult{
12
13 protected final Object[] empty;
14
15 public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) {
16 super(specification);
17 this.empty = new Object[specification.getParameterNames().size()];
18 }
19
20 @Override
21 public boolean hasResult() {
22 return hasResult(empty);
23 }
24 @Override
25 public boolean hasResult(Object[] parameters) {
26 return this.backend.hasMatch(parameters);
27 }
28 @Override
29 public Optional<Object[]> oneResult() {
30 return oneResult(empty);
31 }
32 @Override
33 public Optional<Object[]> oneResult(Object[] parameters) {
34 Optional<Tuple> tuple = this.backend.getOneArbitraryMatch(parameters);
35 if(tuple.isPresent()) {
36 return Optional.of(tuple.get().getElements());
37 } else {
38 return Optional.empty();
39 }
40 }
41 @Override
42 public Stream<Object[]> allResults() {
43 return allResults(empty);
44 }
45 @Override
46 public Stream<Object[]> allResults(Object[] parameters) {
47 return this.backend.getAllMatches(parameters).map(AbstractTuple::getElements);
48 }
49 @Override
50 public int countResults() {
51 return countResults(empty);
52 }
53 @Override
54 public int countResults(Object[] parameters) {
55 return backend.countMatches(parameters);
56 }
57}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalEngineContext.java b/subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalEngineContext.java
new file mode 100644
index 00000000..dfbd8545
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalEngineContext.java
@@ -0,0 +1,33 @@
1package tools.refinery.store.query.internal;
2
3import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex;
4import org.eclipse.viatra.query.runtime.api.scope.IEngineContext;
5import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext;
6
7import tools.refinery.store.model.Model;
8
9public class RelationalEngineContext implements IEngineContext{
10 private final IBaseIndex baseIndex = new DummyBaseIndexer();
11 private final RelationalRuntimeContext runtimeContext;
12
13
14 public RelationalEngineContext(Model model, ModelUpdateListener updateListener) {
15 runtimeContext = new RelationalRuntimeContext(model, updateListener);
16 }
17
18 @Override
19 public IBaseIndex getBaseIndex() {
20 return this.baseIndex;
21 }
22
23 @Override
24 public void dispose() {
25 //lifecycle not controlled by engine
26 }
27
28 @Override
29 public IQueryRuntimeContext getQueryRuntimeContext() {
30 return runtimeContext;
31 }
32
33}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalQueryMetaContext.java b/subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalQueryMetaContext.java
new file mode 100644
index 00000000..05fb0904
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalQueryMetaContext.java
@@ -0,0 +1,58 @@
1package tools.refinery.store.query.internal;
2
3import java.util.Collection;
4import java.util.Collections;
5import java.util.HashMap;
6import java.util.HashSet;
7import java.util.Map;
8import java.util.Set;
9
10import org.eclipse.viatra.query.runtime.matchers.context.AbstractQueryMetaContext;
11import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
12import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication;
13
14import tools.refinery.store.query.view.RelationView;
15
16/**
17 * The meta context information for String scopes.
18 */
19public final class RelationalQueryMetaContext extends AbstractQueryMetaContext {
20
21 @Override
22 public boolean isEnumerable(IInputKey key) {
23 ensureValidKey(key);
24 return key.isEnumerable();
25 }
26
27 @Override
28 public boolean isStateless(IInputKey key) {
29 ensureValidKey(key);
30 return key instanceof RelationView<?>;
31 }
32
33 @Override
34 public Collection<InputKeyImplication> getImplications(IInputKey implyingKey) {
35 ensureValidKey(implyingKey);
36 return new HashSet<InputKeyImplication>();
37 }
38
39 @Override
40 public Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key) {
41 ensureValidKey(key);
42 if (key instanceof RelationView) {
43 return new HashMap<Set<Integer>, Set<Integer>>();
44 } else {
45 return Collections.emptyMap();
46 }
47 }
48
49 public void ensureValidKey(IInputKey key) {
50 if (! (key instanceof RelationView<?>))
51 illegalInputKey(key);
52 }
53
54 public void illegalInputKey(IInputKey key) {
55 throw new IllegalArgumentException("The input key " + key + " is not a valid input key.");
56 }
57
58}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalRuntimeContext.java b/subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalRuntimeContext.java
new file mode 100644
index 00000000..a186b5dd
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalRuntimeContext.java
@@ -0,0 +1,178 @@
1package tools.refinery.store.query.internal;
2
3import static tools.refinery.store.util.CollectionsUtil.filter;
4import static tools.refinery.store.util.CollectionsUtil.map;
5
6import java.lang.reflect.InvocationTargetException;
7import java.util.Iterator;
8import java.util.Optional;
9import java.util.concurrent.Callable;
10
11import org.eclipse.viatra.query.runtime.base.core.NavigationHelperImpl;
12import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
13import org.eclipse.viatra.query.runtime.matchers.context.IQueryMetaContext;
14import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext;
15import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
16import org.eclipse.viatra.query.runtime.matchers.context.IndexingService;
17import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
18import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
19import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
20import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
21import org.eclipse.viatra.query.runtime.matchers.util.Accuracy;
22
23import tools.refinery.store.model.Model;
24import tools.refinery.store.query.view.RelationView;
25
26public class RelationalRuntimeContext implements IQueryRuntimeContext {
27 private final RelationalQueryMetaContext metaContext = new RelationalQueryMetaContext();
28 private final ModelUpdateListener modelUpdateListener;
29 private final Model model;
30
31 public RelationalRuntimeContext(Model model, ModelUpdateListener relationUpdateListener) {
32 this.model = model;
33 this.modelUpdateListener = relationUpdateListener;
34 }
35
36 @Override
37 public IQueryMetaContext getMetaContext() {
38 return metaContext;
39 }
40
41 /**
42 * TODO: check {@link NavigationHelperImpl#coalesceTraversals(Callable)}
43 */
44 @Override
45 public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException {
46 try {
47 return callable.call();
48 } catch (Exception e) {
49 throw new InvocationTargetException(e);
50 }
51 }
52
53 @Override
54 public boolean isCoalescing() {
55 return true;
56 }
57
58 @Override
59 public boolean isIndexed(IInputKey key, IndexingService service) {
60 if(key instanceof RelationView<?> relationalKey) {
61 return this.modelUpdateListener.containsRelationalView(relationalKey);
62 } else {
63 return false;
64 }
65 }
66
67 @Override
68 public void ensureIndexed(IInputKey key, IndexingService service) {
69 if(!isIndexed(key, service)) {
70 throw new IllegalStateException("Engine tries to index a new key " +key);
71 }
72 }
73 @SuppressWarnings("squid:S1452")
74 RelationView<?> checkKey(IInputKey key) {
75 if(key instanceof RelationView) {
76 RelationView<?> relationViewKey = (RelationView<?>) key;
77 if(modelUpdateListener.containsRelationalView(relationViewKey)) {
78 return relationViewKey;
79 } else {
80 throw new IllegalStateException("Query is asking for non-indexed key");
81 }
82 } else {
83 throw new IllegalStateException("Query is asking for non-relational key");
84 }
85 }
86
87 @Override
88 public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed) {
89 RelationView<?> relationalViewKey = checkKey(key);
90 Iterable<Object[]> allObjects = relationalViewKey.getAll(model);
91 Iterable<Object[]> filteredBySeed = filter(allObjects,objectArray -> isMatching(objectArray,seedMask,seed));
92 Iterator<Object[]> iterator = filteredBySeed.iterator();
93 int result = 0;
94 while(iterator.hasNext()) {
95 iterator.next();
96 result++;
97 }
98 return result;
99 }
100
101 @Override
102 public Optional<Long> estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) {
103 return Optional.empty();
104 }
105
106 @Override
107 public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed) {
108 RelationView<?> relationalViewKey = checkKey(key);
109 Iterable<Object[]> allObjects = relationalViewKey.getAll(model);
110 Iterable<Object[]> filteredBySeed = filter(allObjects,objectArray -> isMatching(objectArray,seedMask,seed));
111 return map(filteredBySeed,Tuples::flatTupleOf);
112 }
113
114 private boolean isMatching(Object[] tuple, TupleMask seedMask, ITuple seed) {
115 for(int i=0; i<seedMask.indices.length; i++) {
116 final Object seedElement = seed.get(i);
117 final Object tupleElement = tuple[seedMask.indices[i]];
118 if(!tupleElement.equals(seedElement)) {
119 return false;
120 }
121 }
122 return true;
123 }
124
125 @Override
126 public Iterable<? extends Object> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed) {
127 return enumerateTuples(key, seedMask, seed);
128 }
129
130 @Override
131 public boolean containsTuple(IInputKey key, ITuple seed) {
132 RelationView<?> relationalViewKey = checkKey(key);
133 return relationalViewKey.get(model,seed.getElements());
134 }
135
136 @Override
137 public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) {
138 RelationView<?> relationalKey = checkKey(key);
139 this.modelUpdateListener.addListener(relationalKey, seed, listener);
140
141 }
142
143 @Override
144 public void removeUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) {
145 RelationView<?> relationalKey = checkKey(key);
146 this.modelUpdateListener.removeListener(relationalKey, seed, listener);
147 }
148
149 @Override
150 public Object wrapElement(Object externalElement) {
151 return externalElement;
152 }
153
154 @Override
155 public Object unwrapElement(Object internalElement) {
156 return internalElement;
157 }
158
159 @Override
160 public Tuple wrapTuple(Tuple externalElements) {
161 return externalElements;
162 }
163
164 @Override
165 public Tuple unwrapTuple(Tuple internalElements) {
166 return internalElements;
167 }
168
169 @Override
170 public void ensureWildcardIndexing(IndexingService service) {
171 throw new UnsupportedOperationException();
172 }
173
174 @Override
175 public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException {
176 runnable.run();
177 }
178}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalScope.java b/subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalScope.java
new file mode 100644
index 00000000..e8d45356
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/internal/RelationalScope.java
@@ -0,0 +1,43 @@
1package tools.refinery.store.query.internal;
2
3import java.util.Set;
4
5import org.apache.log4j.Logger;
6import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine;
7import org.eclipse.viatra.query.runtime.api.scope.IEngineContext;
8import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener;
9import org.eclipse.viatra.query.runtime.api.scope.QueryScope;
10
11import tools.refinery.store.model.Model;
12import tools.refinery.store.model.Tuple;
13import tools.refinery.store.model.representation.Relation;
14import tools.refinery.store.query.view.RelationView;
15
16public class RelationalScope extends QueryScope{
17 private final Model model;
18 private final ModelUpdateListener updateListener;
19
20 public RelationalScope(Model model, Set<RelationView<?>> relationViews) {
21 this.model = model;
22 this.updateListener = new ModelUpdateListener(relationViews);
23 //this.changeListener = new
24 }
25
26 public <D> void processUpdate(Relation<D> relation, Tuple key, D oldValue, D newValue) {
27 updateListener.addUpdate(relation, key, oldValue, newValue);
28 }
29
30 public boolean hasChange() {
31 return updateListener.hasChange();
32 }
33
34 public void flush() {
35 updateListener.flush();
36 }
37
38 @Override
39 protected IEngineContext createEngineContext(ViatraQueryEngine engine, IIndexingErrorListener errorListener,
40 Logger logger) {
41 return new RelationalEngineContext(model, updateListener);
42 }
43}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/internal/ViewUpdate.java b/subprojects/store/src/main/java/tools/refinery/store/query/internal/ViewUpdate.java
new file mode 100644
index 00000000..7d1a4c05
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/internal/ViewUpdate.java
@@ -0,0 +1,34 @@
1package tools.refinery.store.query.internal;
2
3import java.util.Arrays;
4import java.util.Objects;
5
6record ViewUpdate (Object[] tuple, boolean isInsertion) {
7
8 @Override
9 public int hashCode() {
10 final int prime = 31;
11 int result = 1;
12 result = prime * result + Arrays.deepHashCode(tuple);
13 result = prime * result + Objects.hash(isInsertion);
14 return result;
15 }
16
17 @Override
18 public boolean equals(Object obj) {
19 if (this == obj)
20 return true;
21 if (obj == null)
22 return false;
23 if (getClass() != obj.getClass())
24 return false;
25 ViewUpdate other = (ViewUpdate) obj;
26 return isInsertion == other.isInsertion && Arrays.deepEquals(tuple, other.tuple);
27 }
28
29 @Override
30 public String toString() {
31 return "ViewUpdate [" + Arrays.toString(tuple) + "insertion= "+this.isInsertion+"]";
32 }
33
34}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/internal/ViewUpdateBuffer.java b/subprojects/store/src/main/java/tools/refinery/store/query/internal/ViewUpdateBuffer.java
new file mode 100644
index 00000000..6bc4c96a
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/internal/ViewUpdateBuffer.java
@@ -0,0 +1,46 @@
1package tools.refinery.store.query.internal;
2
3import java.util.ArrayList;
4import java.util.Arrays;
5import java.util.List;
6
7import tools.refinery.store.model.Tuple;
8
9public class ViewUpdateBuffer<D> {
10 protected final ViewUpdateTranslator<D> updateListener;
11 protected final List<ViewUpdate> buffer = new ArrayList<>();
12
13 public ViewUpdateBuffer(ViewUpdateTranslator<D> updateListener) {
14 this.updateListener = updateListener;
15 }
16
17 public ViewUpdateTranslator<D> getUpdateListener() {
18 return updateListener;
19 }
20
21 public boolean hasChange() {
22 return ! buffer.isEmpty();
23 }
24
25 public void addChange(Tuple tuple, D oldValue, D newValue) {
26 if(oldValue != newValue) {
27 Object[] oldTuple = updateListener.isMatching(tuple, oldValue);
28 Object[] newTuple = updateListener.isMatching(tuple, newValue);
29 if(!Arrays.equals(oldTuple, newTuple)) {
30 if(oldTuple != null) {
31 buffer.add(new ViewUpdate(oldTuple, false));
32 }
33 if(newTuple != null) {
34 buffer.add(new ViewUpdate(newTuple, true));
35 }
36 }
37 }
38 }
39
40 public void flush() {
41 for (ViewUpdate viewChange : buffer) {
42 updateListener.processChange(viewChange);
43 }
44 buffer.clear();
45 }
46}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/internal/ViewUpdateTranslator.java b/subprojects/store/src/main/java/tools/refinery/store/query/internal/ViewUpdateTranslator.java
new file mode 100644
index 00000000..1c210c5f
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/internal/ViewUpdateTranslator.java
@@ -0,0 +1,57 @@
1package tools.refinery.store.query.internal;
2
3import java.util.Objects;
4
5import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
6import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
7import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
8
9import tools.refinery.store.model.Tuple;
10import tools.refinery.store.query.view.RelationView;
11
12public class ViewUpdateTranslator<D> {
13 final RelationView<D> key;
14 final ITuple filter;
15 final IQueryRuntimeContextListener listener;
16
17 public ViewUpdateTranslator(RelationView<D> key, ITuple filter, IQueryRuntimeContextListener listener) {
18 super();
19 this.key = key;
20 this.filter = filter;
21 this.listener = listener;
22 }
23
24 public void processChange(ViewUpdate change) {
25 listener.update(key, Tuples.flatTupleOf(change.tuple()), change.isInsertion());
26 }
27
28 public Object[] isMatching(Tuple tuple, D value){
29 return isMatching(key.getWrappedKey().transform(tuple, value), filter);
30 }
31 @SuppressWarnings("squid:S1168")
32 private Object[] isMatching(Object[] tuple, ITuple filter) {
33 for(int i = 0; i<filter.getSize(); i++) {
34 final Object filterObject = filter.get(i);
35 if(filterObject != null && !filterObject.equals(tuple[i])) {
36 return null;
37 }
38 }
39 return tuple;
40 }
41
42 @Override
43 public int hashCode() {
44 return Objects.hash(filter, key, listener);
45 }
46
47 @Override
48 public boolean equals(Object obj) {
49 if (this == obj)
50 return true;
51 if (!(obj instanceof ViewUpdateTranslator))
52 return false;
53 ViewUpdateTranslator<?> other = (ViewUpdateTranslator<?>) obj;
54 return Objects.equals(filter, other.filter) && Objects.equals(key, other.key)
55 && Objects.equals(listener, other.listener);
56 }
57}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/view/FilteredRelationView.java b/subprojects/store/src/main/java/tools/refinery/store/query/view/FilteredRelationView.java
new file mode 100644
index 00000000..3531195a
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/view/FilteredRelationView.java
@@ -0,0 +1,48 @@
1package tools.refinery.store.query.view;
2
3import java.util.function.BiPredicate;
4
5import tools.refinery.store.model.Model;
6import tools.refinery.store.model.Tuple;
7import tools.refinery.store.model.Tuple.Tuple1;
8import tools.refinery.store.model.representation.Relation;
9
10public class FilteredRelationView<D> extends RelationView<D>{
11 private final BiPredicate<Tuple,D> predicate;
12
13 public FilteredRelationView(Relation<D> representation, BiPredicate<Tuple,D> predicate) {
14 super(representation);
15 this.predicate = predicate;
16 }
17 @Override
18 protected Object[] forwardMap(Tuple key, D value) {
19 return toTuple1Array(key);
20 }
21 @Override
22 public boolean get(Model model, Object[] tuple) {
23 int[] content = new int[tuple.length];
24 for(int i = 0; i<tuple.length; i++) {
25 content[i] =((Tuple1)tuple[i]).get(0);
26 }
27 Tuple key = Tuple.of(content);
28 D value = model.get(representation, key);
29 return filter(key, value);
30 }
31
32 public static Object[] toTuple1Array(Tuple t) {
33 Object[] result = new Object[t.getSize()];
34 for(int i = 0; i<t.getSize(); i++) {
35 result[i] = Tuple.of(t.get(i));
36 }
37 return result;
38 }
39
40 @Override
41 public int getArity() {
42 return this.representation.getArity();
43 }
44 @Override
45 protected boolean filter(Tuple key, D value) {
46 return this.predicate.test(key, value);
47 }
48}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java b/subprojects/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java
new file mode 100644
index 00000000..db9ba4b8
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java
@@ -0,0 +1,50 @@
1package tools.refinery.store.query.view;
2
3import tools.refinery.store.model.Model;
4import tools.refinery.store.model.Tuple;
5import tools.refinery.store.model.Tuple.Tuple1;
6import tools.refinery.store.model.representation.Relation;
7
8public class FunctionalRelationView<D> extends RelationView<D> {
9
10 public FunctionalRelationView(Relation<D> representation) {
11 super(representation);
12 }
13
14 @Override
15 protected boolean filter(Tuple key, D value) {
16 return true;
17 }
18
19 @Override
20 protected Object[] forwardMap(Tuple key, D value) {
21 return toTuple1ArrayPlusValue(key, value);
22 }
23
24 @Override
25 public boolean get(Model model, Object[] tuple) {
26 int[] content = new int[tuple.length-1];
27 for(int i = 0; i<tuple.length-1; i++) {
28 content[i] =((Tuple1)tuple[i]).get(0);
29 }
30 Tuple key = Tuple.of(content);
31 @SuppressWarnings("unchecked")
32 D valueInTuple = (D) tuple[tuple.length-1];
33 D valueInMap = model.get(representation, key);
34 return valueInTuple.equals(valueInMap);
35 }
36
37 public static <D> Object[] toTuple1ArrayPlusValue(Tuple t, D value) {
38 Object[] result = new Object[t.getSize()+1];
39 for(int i = 0; i<t.getSize(); i++) {
40 result[i] = Tuple.of(t.get(i));
41 }
42 result[t.getSize()] = value;
43 return result;
44 }
45
46 @Override
47 public int getArity() {
48 return this.representation.getArity()+1;
49 }
50}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java b/subprojects/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java
new file mode 100644
index 00000000..6fa387a1
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.view;
2
3import tools.refinery.store.model.Tuple;
4import tools.refinery.store.model.representation.Relation;
5
6public class KeyOnlyRelationView extends FilteredRelationView<Boolean>{
7
8 public KeyOnlyRelationView(Relation<Boolean> representation) {
9 super(representation, (k,v)->true);
10 }
11 @Override
12 protected boolean filter(Tuple key, Boolean value) {
13 return !value.equals(representation.getDefaultValue());
14 }
15
16}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/view/RelationView.java b/subprojects/store/src/main/java/tools/refinery/store/query/view/RelationView.java
new file mode 100644
index 00000000..fd55eed4
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/view/RelationView.java
@@ -0,0 +1,85 @@
1package tools.refinery.store.query.view;
2
3import java.util.Objects;
4
5import org.eclipse.viatra.query.runtime.matchers.context.common.BaseInputKeyWrapper;
6
7import tools.refinery.store.map.CursorAsIterator;
8import tools.refinery.store.model.Model;
9import tools.refinery.store.model.Tuple;
10import tools.refinery.store.model.representation.Relation;
11
12/**
13 * Represents a view of a {@link Relation} that can be queried.
14 *
15 * @author Oszkar Semerath
16 *
17 * @param <D>
18 */
19public abstract class RelationView<D> extends BaseInputKeyWrapper<RelationView<D>> {
20 protected final Relation<D> representation;
21
22 protected RelationView(Relation<D> representation) {
23 super(null);
24 this.wrappedKey = this;
25 this.representation = representation;
26 }
27
28 @Override
29 public String getPrettyPrintableName() {
30 return representation.getName();
31 }
32
33 @Override
34 public String getStringID() {
35 return representation.getName() + this.getClass().getName();
36 }
37
38 public Relation<D> getRepresentation() {
39 return representation;
40 }
41
42 @Override
43 public boolean isEnumerable() {
44 return true;
45 }
46
47 protected abstract boolean filter(Tuple key, D value);
48
49 protected abstract Object[] forwardMap(Tuple key, D value);
50
51 public abstract boolean get(Model model, Object[] tuple);
52
53 @SuppressWarnings("squid:S1168")
54 public Object[] transform(Tuple tuple, D value) {
55 if (filter(tuple, value)) {
56 return forwardMap(tuple, value);
57 } else
58 return null;
59 }
60
61 public Iterable<Object[]> getAll(Model model) {
62 return (() -> new CursorAsIterator<>(model.getAll(representation), (k, v) -> forwardMap(k, v),
63 (k, v) -> filter(k, v)));
64 }
65
66 @Override
67 public int hashCode() {
68 final int prime = 31;
69 int result = 1;
70 result = prime * result + Objects.hash(representation);
71 return result;
72 }
73
74 @Override
75 public boolean equals(Object obj) {
76 if (this == obj)
77 return true;
78 if (!(obj instanceof RelationView))
79 return false;
80 @SuppressWarnings("unchecked")
81 RelationView<D> other = ((RelationView<D>) obj);
82 return Objects.equals(representation, other.representation);
83 }
84
85}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/util/CollectionsUtil.java b/subprojects/store/src/main/java/tools/refinery/store/util/CollectionsUtil.java
new file mode 100644
index 00000000..841d0dfa
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/util/CollectionsUtil.java
@@ -0,0 +1,72 @@
1package tools.refinery.store.util;
2
3import java.util.Iterator;
4import java.util.NoSuchElementException;
5import java.util.function.Function;
6import java.util.function.Predicate;
7
8public final class CollectionsUtil {
9 private CollectionsUtil() {
10 throw new UnsupportedOperationException();
11 }
12
13 public static <S,T> Iterator<T> map(Iterator<S> source, Function<S, T> transformation) {
14 return new Iterator<T>() {
15
16 @Override
17 public boolean hasNext() {
18 return source.hasNext();
19 }
20
21 @Override
22 public T next() {
23 return transformation.apply(source.next());
24 }
25 };
26 }
27
28 public static <S,T> Iterable<T> map(Iterable<S> source, Function<S, T> transformation) {
29 return (()->map(source.iterator(),transformation));
30 }
31
32 public static <T> Iterator<T> filter(Iterator<T> source, Predicate<T> condition) {
33 return new Iterator<T>() {
34 T internalNext = move();
35 boolean internalHasNext;
36
37 private T move() {
38 internalHasNext = source.hasNext();
39 if(internalHasNext) {
40 internalNext = source.next();
41 }
42 while(internalHasNext && !condition.test(internalNext)) {
43 internalHasNext = source.hasNext();
44 if(internalHasNext) {
45 internalNext = source.next();
46 }
47 }
48 return internalNext;
49 }
50
51 @Override
52 public boolean hasNext() {
53 return internalHasNext;
54 }
55
56 @Override
57 public T next() {
58 if(!internalHasNext) {
59 throw new NoSuchElementException();
60 } else {
61 T result = internalNext;
62 move();
63 return result;
64 }
65 }
66 };
67 }
68
69 public static <T> Iterable<T> filter(Iterable<T> source, Predicate<T> condition) {
70 return (()->filter(source.iterator(),condition));
71 }
72}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java
new file mode 100644
index 00000000..f0d5d927
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java
@@ -0,0 +1,22 @@
1package tools.refinery.store.map.tests;
2
3import static org.junit.jupiter.api.Assertions.assertEquals;
4
5import org.junit.jupiter.api.Test;
6
7import tools.refinery.store.map.VersionedMapStore;
8import tools.refinery.store.map.VersionedMapStoreImpl;
9import tools.refinery.store.model.Tuple;
10import tools.refinery.store.model.TupleHashProvider;
11
12class MapUnitTests {
13 @Test
14 void defaultTest() {
15 VersionedMapStore<Tuple, Boolean> store = new VersionedMapStoreImpl<Tuple, Boolean>(TupleHashProvider.singleton(), false);
16 var map = store.createMap();
17 var out1 = map.put(Tuple.of(0), true);
18 assertEquals(false, out1);
19 var out2 = map.put(Tuple.of(1), true);
20 assertEquals(false, out2);
21 }
22}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java
new file mode 100644
index 00000000..1f9d022f
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java
@@ -0,0 +1,96 @@
1package tools.refinery.store.map.tests.fuzz;
2
3import static org.junit.jupiter.api.Assertions.fail;
4
5import java.util.Random;
6import java.util.stream.Stream;
7
8import org.junit.jupiter.api.Tag;
9import org.junit.jupiter.api.Timeout;
10import org.junit.jupiter.params.ParameterizedTest;
11import org.junit.jupiter.params.provider.Arguments;
12import org.junit.jupiter.params.provider.MethodSource;
13
14import tools.refinery.store.map.ContinousHashProvider;
15import tools.refinery.store.map.VersionedMapStore;
16import tools.refinery.store.map.VersionedMapStoreImpl;
17import tools.refinery.store.map.internal.VersionedMapImpl;
18import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils;
19import tools.refinery.store.map.tests.utils.MapTestEnvironment;
20
21class CommitFuzzTest {
22 private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency,
23 boolean evilHash) {
24 String[] values = MapTestEnvironment.prepareValues(maxValue);
25 ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash);
26
27 VersionedMapStore<Integer, String> store = new VersionedMapStoreImpl<Integer, String>(chp, values[0]);
28 VersionedMapImpl<Integer, String> sut = (VersionedMapImpl<Integer, String>) store.createMap();
29 MapTestEnvironment<Integer, String> e = new MapTestEnvironment<Integer, String>(sut);
30
31 Random r = new Random(seed);
32
33 iterativeRandomPutsAndCommits(scenario, steps, maxKey, values, e, r, commitFrequency);
34 }
35
36 private void iterativeRandomPutsAndCommits(String scenario, int steps, int maxKey, String[] values,
37 MapTestEnvironment<Integer, String> e, Random r, int commitFrequency) {
38 int stopAt = -1;
39 for (int i = 0; i < steps; i++) {
40 int index = i + 1;
41 int nextKey = r.nextInt(maxKey);
42 String nextValue = values[r.nextInt(values.length)];
43 if (index == stopAt) {
44 System.out.println("issue!");
45 System.out.println("State before:");
46 e.printComparison();
47 e.sut.prettyPrint();
48 System.out.println("Next: put(" + nextKey + "," + nextValue + ")");
49 }
50 try {
51 e.put(nextKey, nextValue);
52 if (index == stopAt) {
53 e.sut.prettyPrint();
54 }
55 e.checkEquivalence(scenario + ":" + index);
56 } catch (Exception exception) {
57 exception.printStackTrace();
58 fail(scenario + ":" + index + ": exception happened: " + exception);
59 }
60 MapTestEnvironment.printStatus(scenario, index, steps, null);
61 if (index % commitFrequency == 0) {
62 e.sut.commit();
63 }
64 }
65 }
66
67 @ParameterizedTest(name = "Commit {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}")
68 @MethodSource
69 @Timeout(value = 10)
70 @Tag("fuzz")
71 void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed,
72 boolean evilHash) {
73 runFuzzTest("CommitS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues,
74 commitFrequency, evilHash);
75 }
76
77 static Stream<Arguments> parametrizedFastFuzz() {
78 return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 },
79 new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 },
80 new Object[] { false, true });
81 }
82
83 @ParameterizedTest(name = "Commit {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}")
84 @MethodSource
85 @Tag("fuzz")
86 @Tag("slow")
87 void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed,
88 boolean evilHash) {
89 runFuzzTest("CommitS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues,
90 commitFrequency, evilHash);
91 }
92
93 static Stream<Arguments> parametrizedSlowFuzz() {
94 return FuzzTestUtils.changeStepCount(parametrizedFastFuzz(), 1);
95 }
96}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java
new file mode 100644
index 00000000..263cb2cd
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java
@@ -0,0 +1,143 @@
1package tools.refinery.store.map.tests.fuzz;
2
3import static org.junit.jupiter.api.Assertions.assertEquals;
4import static org.junit.jupiter.api.Assertions.fail;
5
6import java.util.AbstractMap.SimpleEntry;
7import java.util.Collections;
8import java.util.LinkedList;
9import java.util.List;
10import java.util.Random;
11import java.util.stream.Stream;
12
13import org.junit.jupiter.api.Tag;
14import org.junit.jupiter.api.Timeout;
15import org.junit.jupiter.params.ParameterizedTest;
16import org.junit.jupiter.params.provider.Arguments;
17import org.junit.jupiter.params.provider.MethodSource;
18
19import tools.refinery.store.map.ContinousHashProvider;
20import tools.refinery.store.map.Cursor;
21import tools.refinery.store.map.VersionedMap;
22import tools.refinery.store.map.VersionedMapStore;
23import tools.refinery.store.map.VersionedMapStoreImpl;
24import tools.refinery.store.map.internal.VersionedMapImpl;
25import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils;
26import tools.refinery.store.map.tests.utils.MapTestEnvironment;
27
28class ContentEqualsFuzzTest {
29 private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency,
30 boolean evilHash) {
31 String[] values = MapTestEnvironment.prepareValues(maxValue);
32 ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash);
33
34 Random r = new Random(seed);
35
36 iterativeRandomPutsAndCommitsThenCompare(scenario, chp, steps, maxKey, values, r, commitFrequency);
37 }
38
39 private void iterativeRandomPutsAndCommitsThenCompare(String scenario, ContinousHashProvider<Integer> chp, int steps, int maxKey, String[] values, Random r, int commitFrequency) {
40
41 VersionedMapStore<Integer, String> store1 = new VersionedMapStoreImpl<Integer, String>(chp, values[0]);
42 VersionedMap<Integer, String> sut1 = store1.createMap();
43
44 // Fill one map
45 for (int i = 0; i < steps; i++) {
46 int index1 = i + 1;
47 int nextKey = r.nextInt(maxKey);
48 String nextValue = values[r.nextInt(values.length)];
49 try {
50 sut1.put(nextKey, nextValue);
51 } catch (Exception exception) {
52 exception.printStackTrace();
53 fail(scenario + ":" + index1 + ": exception happened: " + exception);
54 }
55 MapTestEnvironment.printStatus(scenario, index1, steps, "Fill");
56 if (index1 % commitFrequency == 0) {
57 sut1.commit();
58 }
59 }
60
61 // Get the content of the first map
62 List<SimpleEntry<Integer, String>> content = new LinkedList<>();
63 Cursor<Integer, String> cursor = sut1.getAll();
64 while (cursor.move()) {
65 content.add(new SimpleEntry<>(cursor.getKey(), cursor.getValue()));
66 }
67
68 // Randomize the order of the content
69 Collections.shuffle(content, r);
70
71 VersionedMapStore<Integer, String> store2 = new VersionedMapStoreImpl<Integer, String>(chp, values[0]);
72 VersionedMap<Integer, String> sut2 = store2.createMap();
73 int index2 = 1;
74 for (SimpleEntry<Integer, String> entry : content) {
75 sut2.put(entry.getKey(), entry.getValue());
76 if(index2++%commitFrequency == 0)
77 sut2.commit();
78 }
79
80 // Check the integrity of the maps
81 ((VersionedMapImpl<Integer,String>) sut1).checkIntegrity();
82 ((VersionedMapImpl<Integer,String>) sut2).checkIntegrity();
83
84// // Compare the two maps
85 // By size
86 assertEquals(sut1.getSize(), content.size());
87 assertEquals(sut2.getSize(), content.size());
88
89
90
91 // By cursors
92 Cursor<Integer, String> cursor1 = sut1.getAll();
93 Cursor<Integer, String> cursor2 = sut2.getAll();
94 int index3 = 1;
95 boolean canMove = true;
96 do{
97 boolean canMove1 = cursor1.move();
98 boolean canMove2 = cursor2.move();
99 assertEquals(canMove1, canMove2, scenario + ":" + index3 +" Cursors stopped at different times!");
100 assertEquals(cursor1.getKey(), cursor2.getKey(), scenario + ":" + index3 +" Cursors have different keys!");
101 assertEquals(cursor1.getValue(), cursor2.getValue(), scenario + ":" + index3 +" Cursors have different values!");
102
103 canMove = canMove1;
104 MapTestEnvironment.printStatus(scenario, index3++, content.size(), "Compare");
105 } while (canMove);
106
107 // By hashcode
108 assertEquals(sut1.hashCode(), sut2.hashCode(), "Hash codes are not equal!");
109
110 // By equals
111 assertEquals(sut1, sut2, "Maps are not equals");
112 }
113
114 @ParameterizedTest(name = "Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}")
115 @MethodSource
116 @Timeout(value = 10)
117 @Tag("fuzz")
118 void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed,
119 boolean evilHash) {
120 runFuzzTest("CompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues,
121 commitFrequency, evilHash);
122 }
123
124 static Stream<Arguments> parametrizedFastFuzz() {
125 return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 },
126 new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 },
127 new Object[] { false, true });
128 }
129
130 @ParameterizedTest(name = "Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}")
131 @MethodSource
132 @Tag("fuzz")
133 @Tag("slow")
134 void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed,
135 boolean evilHash) {
136 runFuzzTest("CompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues,
137 commitFrequency, evilHash);
138 }
139
140 static Stream<Arguments> parametrizedSlowFuzz() {
141 return FuzzTestUtils.changeStepCount(parametrizedFastFuzz(), 1);
142 }
143}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java
new file mode 100644
index 00000000..e6334224
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java
@@ -0,0 +1,117 @@
1package tools.refinery.store.map.tests.fuzz;
2
3import static org.junit.jupiter.api.Assertions.fail;
4
5import java.util.Random;
6import java.util.stream.Stream;
7
8import org.junit.jupiter.api.Tag;
9import org.junit.jupiter.api.Timeout;
10import org.junit.jupiter.params.ParameterizedTest;
11import org.junit.jupiter.params.provider.Arguments;
12import org.junit.jupiter.params.provider.MethodSource;
13
14import tools.refinery.store.map.ContinousHashProvider;
15import tools.refinery.store.map.DiffCursor;
16import tools.refinery.store.map.VersionedMapStore;
17import tools.refinery.store.map.VersionedMapStoreImpl;
18import tools.refinery.store.map.internal.VersionedMapImpl;
19import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils;
20import tools.refinery.store.map.tests.utils.MapTestEnvironment;
21
22class DiffCursorFuzzTest {
23 private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency,
24 boolean evilHash) {
25 String[] values = MapTestEnvironment.prepareValues(maxValue);
26 ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash);
27
28 VersionedMapStore<Integer, String> store = new VersionedMapStoreImpl<Integer, String>(chp, values[0]);
29 iterativeRandomPutsAndCommitsThenDiffcursor(scenario, store, steps, maxKey, values, seed, commitFrequency);
30 }
31
32 private void iterativeRandomPutsAndCommitsThenDiffcursor(String scenario, VersionedMapStore<Integer, String> store,
33 int steps, int maxKey, String[] values, int seed, int commitFrequency) {
34 // 1. build a map with versions
35 Random r = new Random(seed);
36 VersionedMapImpl<Integer, String> versioned = (VersionedMapImpl<Integer, String>) store.createMap();
37 int largestCommit = -1;
38
39 for (int i = 0; i < steps; i++) {
40 int index = i + 1;
41 int nextKey = r.nextInt(maxKey);
42 String nextValue = values[r.nextInt(values.length)];
43 try {
44 versioned.put(nextKey, nextValue);
45 } catch (Exception exception) {
46 exception.printStackTrace();
47 fail(scenario + ":" + index + ": exception happened: " + exception);
48 }
49 if (index % commitFrequency == 0) {
50 long version = versioned.commit();
51 largestCommit = (int) version;
52 }
53 if (index % 10000 == 0)
54 System.out.println(scenario + ":" + index + "/" + steps + " building finished");
55 }
56 // 2. create a non-versioned map,
57 VersionedMapImpl<Integer, String> moving = (VersionedMapImpl<Integer, String>) store.createMap();
58 Random r2 = new Random(seed + 1);
59
60 final int diffTravelFrequency = commitFrequency * 2;
61 for (int i = 0; i < steps; i++) {
62 int index = i + 1;
63 if (index % diffTravelFrequency == 0) {
64 // difftravel
65 long travelToVersion = r2.nextInt(largestCommit + 1);
66 DiffCursor<Integer, String> diffCursor = moving.getDiffCursor(travelToVersion);
67 moving.putAll(diffCursor);
68
69 } else {
70 // random puts
71 int nextKey = r2.nextInt(maxKey);
72 String nextValue = values[r2.nextInt(values.length)];
73 try {
74 moving.put(nextKey, nextValue);
75 } catch (Exception exception) {
76 exception.printStackTrace();
77 fail(scenario + ":" + index + ": exception happened: " + exception);
78 }
79 if (index % commitFrequency == 0) {
80 versioned.commit();
81 }
82 if (index % 10000 == 0)
83 System.out.println(scenario + ":" + index + "/" + steps + " building finished");
84 }
85 }
86
87 }
88
89 @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}")
90 @MethodSource
91 @Timeout(value = 10)
92 @Tag("fuzz")
93 void parametrizedFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed,
94 boolean evilHash) {
95 runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps,
96 noKeys, noValues, commitFrequency, evilHash);
97 }
98
99 static Stream<Arguments> parametrizedFuzz() {
100 return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 },
101 new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 },
102 new Object[] { false, true });
103 }
104 @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}")
105 @MethodSource
106 @Tag("fuzz")
107 @Tag("slow")
108 void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed,
109 boolean evilHash) {
110 runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues,
111 commitFrequency, evilHash);
112 }
113
114 static Stream<Arguments> parametrizedSlowFuzz() {
115 return FuzzTestUtils.changeStepCount(parametrizedFuzz(), 1);
116 }
117}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java
new file mode 100644
index 00000000..1ab431a8
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java
@@ -0,0 +1,97 @@
1package tools.refinery.store.map.tests.fuzz;
2
3import static org.junit.jupiter.api.Assertions.assertEquals;
4import static org.junit.jupiter.api.Assertions.fail;
5
6import java.util.Collections;
7import java.util.LinkedList;
8import java.util.List;
9import java.util.stream.Stream;
10
11import org.junit.jupiter.api.Tag;
12import org.junit.jupiter.api.Timeout;
13import org.junit.jupiter.params.ParameterizedTest;
14import org.junit.jupiter.params.provider.Arguments;
15import org.junit.jupiter.params.provider.MethodSource;
16
17import tools.refinery.store.map.ContinousHashProvider;
18import tools.refinery.store.map.VersionedMapStore;
19import tools.refinery.store.map.VersionedMapStoreImpl;
20import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils;
21import tools.refinery.store.map.tests.utils.MapTestEnvironment;
22
23class MultiThreadFuzzTest {
24 public static final int noThreads = 32;
25
26 private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency,
27 boolean evilHash) {
28 String[] values = MapTestEnvironment.prepareValues(maxValue);
29 ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash);
30
31 VersionedMapStore<Integer, String> store = new VersionedMapStoreImpl<Integer, String>(chp, values[0]);
32
33 // initialize runnables
34 MultiThreadTestRunnable[] runnables = new MultiThreadTestRunnable[noThreads];
35 for(int i = 0; i<noThreads; i++) {
36 runnables[i] = new MultiThreadTestRunnable(scenario+"-T"+(i+1), store, steps, maxKey, values, seed, commitFrequency);
37 }
38
39 // initialize threads
40 Thread[] threads = new Thread[noThreads];
41 for(int i = 0; i<noThreads; i++) {
42 threads[i] = new Thread(runnables[i]);
43 }
44
45 // start threads;
46 for(int i = 0; i<noThreads; i++) {
47 threads[i].start();
48 }
49
50 // wait all the threads;
51 for(int i = 0; i<noThreads; i++) {
52 try {
53 threads[i].join();
54 } catch (InterruptedException e) {
55 fail("Thread "+i+" interrupted.");
56 }
57 }
58
59 // collect errors
60 List<Throwable> errors = new LinkedList<>();
61 for(int i = 0; i<noThreads; i++) {
62 errors.addAll(runnables[i].getErrors());
63 }
64
65 assertEquals(Collections.EMPTY_LIST, errors);
66 }
67
68 @ParameterizedTest(name = "Multithread {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}")
69 @MethodSource
70 @Timeout(value = 10)
71 @Tag("fuzz")
72 void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed,
73 boolean evilHash) {
74 runFuzzTest("MultithreadS" + steps + "K" + noKeys + "V" + noValues + "CF" + commitFrequency + "s" + seed, seed, steps, noKeys, noValues,
75 commitFrequency, evilHash);
76 }
77
78 static Stream<Arguments> parametrizedFastFuzz() {
79 return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 },
80 new Object[] { 2, 3 }, new Object[] { 10, 100 }, new Object[] { 1, 2, 3 },
81 new Object[] { false, true });
82 }
83
84 @ParameterizedTest(name = "Multithread {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}")
85 @MethodSource
86 @Tag("fuzz")
87 @Tag("slow")
88 void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed,
89 boolean evilHash) {
90 runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues,
91 commitFrequency, evilHash);
92 }
93
94 static Stream<Arguments> parametrizedSlowFuzz() {
95 return FuzzTestUtils.changeStepCount(RestoreFuzzTest.parametrizedFastFuzz(), 1);
96 }
97}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java
new file mode 100644
index 00000000..f77f9ee5
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java
@@ -0,0 +1,101 @@
1package tools.refinery.store.map.tests.fuzz;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.HashMap;
6import java.util.LinkedList;
7import java.util.List;
8import java.util.Map;
9import java.util.Random;
10
11import tools.refinery.store.map.VersionedMapStore;
12import tools.refinery.store.map.internal.VersionedMapImpl;
13import tools.refinery.store.map.tests.utils.MapTestEnvironment;
14
15public class MultiThreadTestRunnable implements Runnable {
16 String scenario;
17 VersionedMapStore<Integer, String> store;
18 int steps;
19 int maxKey;
20 String[] values;
21 int seed;
22 int commitFrequency;
23 List<Throwable> errors = new LinkedList<>();
24
25 public MultiThreadTestRunnable(String scenario, VersionedMapStore<Integer, String> store, int steps,
26 int maxKey, String[] values, int seed, int commitFrequency) {
27 super();
28 this.scenario = scenario;
29 this.store = store;
30 this.steps = steps;
31 this.maxKey = maxKey;
32 this.values = values;
33 this.seed = seed;
34 this.commitFrequency = commitFrequency;
35 }
36
37 private void logAndThrowError(String message) {
38 AssertionError error = new AssertionError(message);
39 errors.add(error);
40 }
41
42 public List<Throwable> getErrors() {
43 return errors;
44 }
45
46 @Override
47 public void run() {
48 // 1. build a map with versions
49 Random r = new Random(seed);
50 VersionedMapImpl<Integer, String> versioned = (VersionedMapImpl<Integer, String>) store.createMap();
51 Map<Integer, Long> index2Version = new HashMap<>();
52
53 for (int i = 0; i < steps; i++) {
54 int index = i + 1;
55 int nextKey = r.nextInt(maxKey);
56 String nextValue = values[r.nextInt(values.length)];
57 try {
58 versioned.put(nextKey, nextValue);
59 } catch (Exception exception) {
60 exception.printStackTrace();
61 logAndThrowError(scenario + ":" + index + ": exception happened: " + exception);
62 }
63 if (index % commitFrequency == 0) {
64 long version = versioned.commit();
65 index2Version.put(i, version);
66 }
67 MapTestEnvironment.printStatus(scenario, index, steps, "building");
68 }
69 // 2. create a non-versioned
70 VersionedMapImpl<Integer, String> reference = (VersionedMapImpl<Integer, String>) store.createMap();
71 r = new Random(seed);
72 Random r2 = new Random(seed+1);
73
74 for (int i = 0; i < steps; i++) {
75 int index = i + 1;
76 int nextKey = r.nextInt(maxKey);
77 String nextValue = values[r.nextInt(values.length)];
78 try {
79 reference.put(nextKey, nextValue);
80 } catch (Exception exception) {
81 exception.printStackTrace();
82 logAndThrowError(scenario + ":" + index + ": exception happened: " + exception);
83 }
84 // go back to an existing state and compare to the reference
85 if (index % (commitFrequency) == 0) {
86 versioned.restore(index2Version.get(i));
87 MapTestEnvironment.compareTwoMaps(scenario + ":" + index, reference, versioned,errors);
88
89 // go back to a random state (probably created by another thread)
90 List<Long> states = new ArrayList<>(store.getStates());
91 Collections.shuffle(states, r2);
92 for(Long state : states.subList(0, Math.min(states.size(), 100))) {
93 versioned.restore(state);
94 }
95 versioned.restore(index2Version.get(i));
96 }
97
98 MapTestEnvironment.printStatus(scenario, index, steps, "comparison");
99 }
100 }
101}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java
new file mode 100644
index 00000000..d40c49c4
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java
@@ -0,0 +1,92 @@
1package tools.refinery.store.map.tests.fuzz;
2
3import static org.junit.jupiter.api.Assertions.fail;
4
5import java.util.Random;
6import java.util.stream.Stream;
7
8import org.junit.jupiter.api.Tag;
9import org.junit.jupiter.api.Timeout;
10import org.junit.jupiter.params.ParameterizedTest;
11import org.junit.jupiter.params.provider.Arguments;
12import org.junit.jupiter.params.provider.MethodSource;
13
14import tools.refinery.store.map.ContinousHashProvider;
15import tools.refinery.store.map.VersionedMapStore;
16import tools.refinery.store.map.VersionedMapStoreImpl;
17import tools.refinery.store.map.internal.VersionedMapImpl;
18import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils;
19import tools.refinery.store.map.tests.utils.MapTestEnvironment;
20
21class MutableFuzzTest {
22 private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean evilHash) {
23 String[] values = MapTestEnvironment.prepareValues(maxValue);
24 ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash);
25
26 VersionedMapStore<Integer, String> store = new VersionedMapStoreImpl<Integer, String>(chp, values[0]);
27 VersionedMapImpl<Integer, String> sut = (VersionedMapImpl<Integer, String>) store.createMap();
28 MapTestEnvironment<Integer, String> e = new MapTestEnvironment<Integer, String>(sut);
29
30 Random r = new Random(seed);
31
32 iterativeRandomPuts(scenario, steps, maxKey, values, e, r);
33 }
34
35 private void iterativeRandomPuts(String scenario, int steps, int maxKey, String[] values,
36 MapTestEnvironment<Integer, String> e, Random r) {
37 int stopAt = -1;
38 for (int i = 0; i < steps; i++) {
39 int index = i + 1;
40 int nextKey = r.nextInt(maxKey);
41 String nextValue = values[r.nextInt(values.length)];
42 if (index == stopAt) {
43 System.out.println("issue!");
44 System.out.println("State before:");
45 e.printComparison();
46 e.sut.prettyPrint();
47 System.out.println("Next: put(" + nextKey + "," + nextValue + ")");
48 }
49 try {
50 e.put(nextKey, nextValue);
51 if (index == stopAt) {
52 e.sut.prettyPrint();
53 }
54 e.checkEquivalence(scenario + ":" + index);
55 } catch (Exception exception) {
56 exception.printStackTrace();
57 fail(scenario + ":" + index + ": exception happened: " + exception);
58 }
59 MapTestEnvironment.printStatus(scenario, index, steps, null);
60 }
61 }
62
63 @ParameterizedTest(name = "Mutable {index}/{0} Steps={1} Keys={2} Values={3} seed={4} evil-hash={5}")
64 @MethodSource
65 @Timeout(value = 10)
66 @Tag("fuzz")
67 void parametrizedFuzz(int test, int steps, int noKeys, int noValues, int seed, boolean evilHash) {
68 runFuzzTest(
69 "MutableS" + steps + "K" + noKeys + "V" + noValues + "s" + seed + "H" + (evilHash ? "Evil" : "Normal"),
70 seed, steps, noKeys, noValues, evilHash);
71 }
72
73 static Stream<Arguments> parametrizedFuzz() {
74 return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT },
75 new Object[] { 3, 32, 32 * 32, 32 * 32 * 32 * 32 }, new Object[] { 2, 3 }, new Object[] { 1, 2, 3 },
76 new Object[] { false, true });
77 }
78
79 @ParameterizedTest(name = "Mutable {index}/{0} Steps={1} Keys={2} Values={3} seed={4} evil-hash={5}")
80 @MethodSource
81 @Tag("fuzz")
82 @Tag("slow")
83 void parametrizedSlowFuzz(int test, int steps, int noKeys, int noValues, int seed, boolean evilHash) {
84 runFuzzTest(
85 "MutableS" + steps + "K" + noKeys + "V" + noValues + "s" + seed + "H" + (evilHash ? "Evil" : "Normal"),
86 seed, steps, noKeys, noValues, evilHash);
87 }
88
89 static Stream<Arguments> parametrizedSlowFuzz() {
90 return FuzzTestUtils.changeStepCount(parametrizedFuzz(), 1);
91 }
92}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java
new file mode 100644
index 00000000..410705a2
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java
@@ -0,0 +1,89 @@
1package tools.refinery.store.map.tests.fuzz;
2
3import static org.junit.jupiter.api.Assertions.fail;
4
5import java.util.Random;
6import java.util.stream.Stream;
7
8import org.junit.jupiter.api.Tag;
9import org.junit.jupiter.api.Timeout;
10import org.junit.jupiter.params.ParameterizedTest;
11import org.junit.jupiter.params.provider.Arguments;
12import org.junit.jupiter.params.provider.MethodSource;
13
14import tools.refinery.store.map.ContinousHashProvider;
15import tools.refinery.store.map.VersionedMapStore;
16import tools.refinery.store.map.VersionedMapStoreImpl;
17import tools.refinery.store.map.internal.VersionedMapImpl;
18import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils;
19import tools.refinery.store.map.tests.utils.MapTestEnvironment;
20
21class MutableImmutableCompareFuzzTest {
22 private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency,
23 boolean evilHash) {
24 String[] values = MapTestEnvironment.prepareValues(maxValue);
25 ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash);
26
27 VersionedMapStore<Integer, String> store = new VersionedMapStoreImpl<Integer, String>(chp, values[0]);
28 VersionedMapImpl<Integer, String> immutable = (VersionedMapImpl<Integer, String>) store.createMap();
29 VersionedMapImpl<Integer, String> mutable = (VersionedMapImpl<Integer, String>) store.createMap();
30
31 Random r = new Random(seed);
32
33 iterativeRandomPutsAndCommitsAndCompare(scenario, immutable, mutable, steps, maxKey, values, r,
34 commitFrequency);
35 }
36
37 private void iterativeRandomPutsAndCommitsAndCompare(String scenario, VersionedMapImpl<Integer, String> immutable,
38 VersionedMapImpl<Integer, String> mutable, int steps, int maxKey, String[] values, Random r,
39 int commitFrequency) {
40 for (int i = 0; i < steps; i++) {
41 int index = i + 1;
42 int nextKey = r.nextInt(maxKey);
43 String nextValue = values[r.nextInt(values.length)];
44 try {
45 immutable.put(nextKey, nextValue);
46 mutable.put(nextKey, nextValue);
47 } catch (Exception exception) {
48 exception.printStackTrace();
49 fail(scenario + ":" + index + ": exception happened: " + exception);
50 }
51 if (index % commitFrequency == 0) {
52 immutable.commit();
53 }
54 MapTestEnvironment.compareTwoMaps(scenario + ":" + index, immutable, mutable);
55
56 MapTestEnvironment.printStatus(scenario, index, steps, null);
57 }
58 }
59
60 @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}")
61 @MethodSource
62 @Timeout(value = 10)
63 @Tag("fuzz")
64 void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed,
65 boolean evilHash) {
66 runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps,
67 noKeys, noValues, commitFrequency, evilHash);
68 }
69
70 static Stream<Arguments> parametrizedFastFuzz() {
71 return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 },
72 new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 },
73 new Object[] { false, true });
74 }
75
76 @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}")
77 @MethodSource
78 @Tag("fuzz")
79 @Tag("slow")
80 void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed,
81 boolean evilHash) {
82 runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps,
83 noKeys, noValues, commitFrequency, evilHash);
84 }
85
86 static Stream<Arguments> parametrizedSlowFuzz() {
87 return FuzzTestUtils.changeStepCount(MutableImmutableCompareFuzzTest.parametrizedFastFuzz(), 1);
88 }
89}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java
new file mode 100644
index 00000000..2e29a03f
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java
@@ -0,0 +1,109 @@
1package tools.refinery.store.map.tests.fuzz;
2
3import static org.junit.jupiter.api.Assertions.fail;
4
5import java.util.HashMap;
6import java.util.Map;
7import java.util.Random;
8import java.util.stream.Stream;
9
10import org.junit.jupiter.api.Tag;
11import org.junit.jupiter.api.Timeout;
12import org.junit.jupiter.params.ParameterizedTest;
13import org.junit.jupiter.params.provider.Arguments;
14import org.junit.jupiter.params.provider.MethodSource;
15
16import tools.refinery.store.map.ContinousHashProvider;
17import tools.refinery.store.map.VersionedMapStore;
18import tools.refinery.store.map.VersionedMapStoreImpl;
19import tools.refinery.store.map.internal.VersionedMapImpl;
20import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils;
21import tools.refinery.store.map.tests.utils.MapTestEnvironment;
22
23class RestoreFuzzTest {
24 private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency,
25 boolean evilHash) {
26 String[] values = MapTestEnvironment.prepareValues(maxValue);
27 ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash);
28
29 VersionedMapStore<Integer, String> store = new VersionedMapStoreImpl<Integer, String>(chp, values[0]);
30
31 iterativeRandomPutsAndCommitsThenRestore(scenario, store, steps, maxKey, values, seed, commitFrequency);
32 }
33
34 private void iterativeRandomPutsAndCommitsThenRestore(String scenario, VersionedMapStore<Integer, String> store,
35 int steps, int maxKey, String[] values, int seed, int commitFrequency) {
36 // 1. build a map with versions
37 Random r = new Random(seed);
38 VersionedMapImpl<Integer, String> versioned = (VersionedMapImpl<Integer, String>) store.createMap();
39 Map<Integer, Long> index2Version = new HashMap<>();
40
41 for (int i = 0; i < steps; i++) {
42 int index = i + 1;
43 int nextKey = r.nextInt(maxKey);
44 String nextValue = values[r.nextInt(values.length)];
45 try {
46 versioned.put(nextKey, nextValue);
47 } catch (Exception exception) {
48 exception.printStackTrace();
49 fail(scenario + ":" + index + ": exception happened: " + exception);
50 }
51 if (index % commitFrequency == 0) {
52 long version = versioned.commit();
53 index2Version.put(i, version);
54 }
55 MapTestEnvironment.printStatus(scenario, index, steps, "building");
56 }
57 // 2. create a non-versioned and
58 VersionedMapImpl<Integer, String> reference = (VersionedMapImpl<Integer, String>) store.createMap();
59 r = new Random(seed);
60
61 for (int i = 0; i < steps; i++) {
62 int index = i + 1;
63 int nextKey = r.nextInt(maxKey);
64 String nextValue = values[r.nextInt(values.length)];
65 try {
66 reference.put(nextKey, nextValue);
67 } catch (Exception exception) {
68 exception.printStackTrace();
69 fail(scenario + ":" + index + ": exception happened: " + exception);
70 }
71 if (index % commitFrequency == 0) {
72 versioned.restore(index2Version.get(i));
73 MapTestEnvironment.compareTwoMaps(scenario + ":" + index, reference, versioned);
74 }
75 MapTestEnvironment.printStatus(scenario, index, steps, "comparison");
76 }
77
78 }
79
80 @ParameterizedTest(name = "Restore {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}")
81 @MethodSource
82 @Timeout(value = 10)
83 @Tag("smoke")
84 void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed,
85 boolean evilHash) {
86 runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues,
87 commitFrequency, evilHash);
88 }
89
90 static Stream<Arguments> parametrizedFastFuzz() {
91 return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 },
92 new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 },
93 new Object[] { false, true });
94 }
95
96 @ParameterizedTest(name = "Restore {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}")
97 @MethodSource
98 @Tag("smoke")
99 @Tag("slow")
100 void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed,
101 boolean evilHash) {
102 runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues,
103 commitFrequency, evilHash);
104 }
105
106 static Stream<Arguments> parametrizedSlowFuzz() {
107 return FuzzTestUtils.changeStepCount(RestoreFuzzTest.parametrizedFastFuzz(), 1);
108 }
109}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java
new file mode 100644
index 00000000..914a0f63
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java
@@ -0,0 +1,113 @@
1package tools.refinery.store.map.tests.fuzz;
2
3import java.util.HashMap;
4import java.util.LinkedList;
5import java.util.List;
6import java.util.Map;
7import java.util.Random;
8import java.util.stream.Stream;
9
10import org.junit.jupiter.api.Tag;
11import org.junit.jupiter.api.Timeout;
12import org.junit.jupiter.params.ParameterizedTest;
13import org.junit.jupiter.params.provider.Arguments;
14import org.junit.jupiter.params.provider.MethodSource;
15
16import tools.refinery.store.map.ContinousHashProvider;
17import tools.refinery.store.map.VersionedMapStore;
18import tools.refinery.store.map.VersionedMapStoreImpl;
19import tools.refinery.store.map.internal.VersionedMapImpl;
20import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils;
21import tools.refinery.store.map.tests.utils.MapTestEnvironment;
22
23class SharedStoreFuzzTest {
24 private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency,
25 boolean evilHash) {
26 String[] values = MapTestEnvironment.prepareValues(maxValue);
27 ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash);
28
29 List<VersionedMapStore<Integer, String>> stores = VersionedMapStoreImpl.createSharedVersionedMapStores(5, chp, values[0]);
30
31 iterativeRandomPutsAndCommitsThenRestore(scenario, stores, steps, maxKey, values, seed, commitFrequency);
32 }
33
34 private void iterativeRandomPutsAndCommitsThenRestore(String scenario, List<VersionedMapStore<Integer, String>> stores,
35 int steps, int maxKey, String[] values, int seed, int commitFrequency) {
36 // 1. maps with versions
37 Random r = new Random(seed);
38 List<VersionedMapImpl<Integer, String>> versioneds = new LinkedList<>();
39 for(VersionedMapStore<Integer, String> store : stores) {
40 versioneds.add((VersionedMapImpl<Integer, String>) store.createMap());
41 }
42
43 List<Map<Integer, Long>> index2Version = new LinkedList<>();
44 for(int i = 0; i<stores.size(); i++) {
45 index2Version.add(new HashMap<>());
46 }
47
48 for (int i = 0; i < steps; i++) {
49 int stepIndex = i + 1;
50 for (int storeIndex = 0; storeIndex<versioneds.size(); storeIndex++) {
51 int nextKey = r.nextInt(maxKey);
52 String nextValue = values[r.nextInt(values.length)];
53 versioneds.get(storeIndex).put(nextKey, nextValue);
54 if (stepIndex % commitFrequency == 0) {
55 long version = versioneds.get(storeIndex).commit();
56 index2Version.get(storeIndex).put(i, version);
57 }
58 MapTestEnvironment.printStatus(scenario, stepIndex, steps, "building");
59 }
60 }
61 // 2. create a non-versioned and
62 List<VersionedMapImpl<Integer, String>> reference = new LinkedList<>();
63 for(VersionedMapStore<Integer, String> store : stores) {
64 reference.add((VersionedMapImpl<Integer, String>) store.createMap());
65 }
66 r = new Random(seed);
67
68 for (int i = 0; i < steps; i++) {
69 int index = i + 1;
70 for (int storeIndex = 0; storeIndex<versioneds.size(); storeIndex++) {
71 int nextKey = r.nextInt(maxKey);
72 String nextValue = values[r.nextInt(values.length)];
73 reference.get(storeIndex).put(nextKey, nextValue);
74 if (index % commitFrequency == 0) {
75 versioneds.get(storeIndex).restore(index2Version.get(storeIndex).get(i));
76 MapTestEnvironment.compareTwoMaps(scenario + ":" + index, reference.get(storeIndex), versioneds.get(storeIndex));
77 }
78 }
79 MapTestEnvironment.printStatus(scenario, index, steps, "comparison");
80 }
81
82 }
83
84 @ParameterizedTest(name = "Shared Store {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}")
85 @MethodSource
86 @Timeout(value = 10)
87 @Tag("smoke")
88 void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed,
89 boolean evilHash) {
90 runFuzzTest("SharedS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues,
91 commitFrequency, evilHash);
92 }
93
94 static Stream<Arguments> parametrizedFastFuzz() {
95 return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 },
96 new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 },
97 new Object[] { false, true });
98 }
99
100 @ParameterizedTest(name = "Shared Store {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}")
101 @MethodSource
102 @Tag("smoke")
103 @Tag("slow")
104 void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed,
105 boolean evilHash) {
106 runFuzzTest("SharedS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues,
107 commitFrequency, evilHash);
108 }
109
110 static Stream<Arguments> parametrizedSlowFuzz() {
111 return FuzzTestUtils.changeStepCount(RestoreFuzzTest.parametrizedFastFuzz(), 1);
112 }
113}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java
new file mode 100644
index 00000000..e75d7f5a
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java
@@ -0,0 +1,64 @@
1package tools.refinery.store.map.tests.fuzz.utils;
2
3import java.util.Arrays;
4import java.util.LinkedList;
5import java.util.List;
6import java.util.stream.Stream;
7
8import org.junit.jupiter.params.provider.Arguments;
9
10public final class FuzzTestUtils {
11 public static final int FAST_STEP_COUNT = 500;
12 public static final int SLOW_STEP_COUNT = 32 * 32 * 32 * 32;
13
14 private FuzzTestUtils() {
15 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
16 }
17
18 public static Stream<Arguments> changeStepCount(Stream<Arguments> arguments, int parameterIndex) {
19 return arguments.map(x -> Arguments.of(updatedStepCount(x.get(), parameterIndex)));
20 }
21
22 public static Object[] updatedStepCount(Object[] arguments, int parameterIndex) {
23 Object[] copy = Arrays.copyOf(arguments, arguments.length);
24 copy[parameterIndex] = SLOW_STEP_COUNT;
25 return copy;
26 }
27
28 static List<List<Object>> permutationInternal(int from, Object[]... valueOption) {
29 if (valueOption.length == from) {
30 return List.of(List.of());
31 } else {
32 Object[] permuteThis = valueOption[from];
33 List<List<Object>> otherCombination = permutationInternal(from + 1, valueOption);
34 List<List<Object>> result = new LinkedList<>();
35 for (Object permuteThisElement : permuteThis) {
36 for (List<Object> otherCombinationList : otherCombination) {
37 List<Object> newResult = new LinkedList<>();
38 newResult.add(permuteThisElement);
39 newResult.addAll(otherCombinationList);
40 result.add(newResult);
41 }
42 }
43 return result;
44 }
45 }
46
47 public static Stream<Arguments> permutation(Object[]... valueOption) {
48 List<List<Object>> permutations = permutationInternal(0, valueOption);
49 return permutations.stream().map(x -> Arguments.of(x.toArray()));
50 }
51
52 public static Stream<Arguments> permutationWithSize(Object[]... valueOption) {
53 int size = 1;
54 for (int i = 0; i < valueOption.length; i++) {
55 size *= valueOption[i].length;
56 }
57 Object[][] newValueOption = new Object[valueOption.length + 1][];
58 newValueOption[0] = new Object[] { size };
59 for (int i = 1; i < newValueOption.length; i++) {
60 newValueOption[i] = valueOption[i - 1];
61 }
62 return permutation(newValueOption);
63 }
64}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtilsTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtilsTest.java
new file mode 100644
index 00000000..72f2a46c
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtilsTest.java
@@ -0,0 +1,33 @@
1package tools.refinery.store.map.tests.fuzz.utils;
2
3import static org.junit.jupiter.api.Assertions.assertEquals;
4
5import java.util.List;
6
7import org.junit.jupiter.api.Test;
8
9class FuzzTestUtilsTest {
10 @Test
11 void permutationInternalTest() {
12 List<List<Object>> res = FuzzTestUtils.permutationInternal(0, new Object[] { 1, 2, 3 },
13 new Object[] { 'a', 'b', 'c' }, new Object[] { "alpha", "beta", "gamma", "delta" });
14 assertEquals(3 * 3 * 4, res.size());
15 }
16
17 @Test
18 void permutationTest1() {
19 var res = FuzzTestUtils.permutation(new Object[] { 1, 2, 3 }, new Object[] { 'a', 'b', 'c' },
20 new Object[] { "alpha", "beta", "gamma", "delta" });
21 assertEquals(3 * 3 * 4, res.count());
22 }
23
24 @Test
25 void permutationTest2() {
26 var res = FuzzTestUtils.permutation(new Object[] { 1, 2, 3 }, new Object[] { 'a', 'b', 'c' },
27 new Object[] { "alpha", "beta", "gamma", "delta" });
28 var arguments = res.findFirst().get().get();
29 assertEquals(1, arguments[0]);
30 assertEquals('a', arguments[1]);
31 assertEquals("alpha", arguments[2]);
32 }
33}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java
new file mode 100644
index 00000000..991b4f51
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java
@@ -0,0 +1,214 @@
1package tools.refinery.store.map.tests.utils;
2
3import static org.junit.jupiter.api.Assertions.assertEquals;
4import static org.junit.jupiter.api.Assertions.assertTrue;
5import static org.junit.jupiter.api.Assertions.fail;
6
7import java.util.HashMap;
8import java.util.Iterator;
9import java.util.List;
10import java.util.Map;
11import java.util.Map.Entry;
12
13import tools.refinery.store.map.ContinousHashProvider;
14import tools.refinery.store.map.Cursor;
15import tools.refinery.store.map.VersionedMap;
16import tools.refinery.store.map.internal.VersionedMapImpl;
17
18import java.util.TreeMap;
19
20public class MapTestEnvironment<K, V> {
21 public static String[] prepareValues(int maxValue) {
22 String[] values = new String[maxValue];
23 values[0] = "DEFAULT";
24 for (int i = 1; i < values.length; i++) {
25 values[i] = "VAL" + i;
26 }
27 return values;
28 }
29
30 public static ContinousHashProvider<Integer> prepareHashProvider(final boolean evil) {
31 // Use maxPrime = 2147483629
32
33 ContinousHashProvider<Integer> chp = new ContinousHashProvider<Integer>() {
34
35 @Override
36 public int getHash(Integer key, int index) {
37 if (evil && index < 15 && index < key / 3) {
38 return 7;
39 }
40 int result = 1;
41 final int prime = 31;
42
43 result = prime * result + key;
44 result = prime * result + index;
45
46 return result;
47 }
48 };
49 return chp;
50 }
51
52 public static void printStatus(String scenario, int actual, int max, String stepName) {
53 if (actual % 10000 == 0) {
54 String printStepName = stepName == null ? "" : stepName;
55 System.out.format(scenario + ":%d/%d (%d%%) " + printStepName + "%n", actual, max, actual * 100 / max);
56 }
57
58 }
59
60 public static <K, V> void compareTwoMaps(String title, VersionedMapImpl<K, V> map1,
61 VersionedMapImpl<K, V> map2) {
62 compareTwoMaps(title, map1, map2, null);
63 }
64 public static <K, V> void compareTwoMaps(String title, VersionedMapImpl<K, V> map1,
65 VersionedMapImpl<K, V> map2, List<Throwable> errors) {
66 // 1. Comparing cursors.
67 Cursor<K, V> cursor1 = map1.getAll();
68 Cursor<K, V> cursor2 = map2.getAll();
69 while (!cursor1.isTerminated()) {
70 if (cursor2.isTerminated()) {
71 fail("cursor 2 terminated before cursor1");
72 }
73 assertEqualsList(cursor1.getKey(), cursor2.getKey(),"Keys not equal", errors);
74 assertEqualsList(cursor2.getValue(), cursor2.getValue(), "Values not equal", errors);
75 cursor1.move();
76 cursor2.move();
77 }
78 if (!cursor2.isTerminated())
79 fail("cursor 1 terminated before cursor 2");
80
81 // 2.1. comparing hash codes
82 assertEqualsList(map1.hashCode(), map2.hashCode(), title + ": hash code check",errors);
83 assertEqualsList(map1, map2, title + ": 1.equals(2)",errors);
84 assertEqualsList(map2, map1, title + ": 2.equals(1)",errors);
85 }
86 private static void assertEqualsList(Object o1, Object o2, String message, List<Throwable> errors) {
87 if(errors == null) {
88 assertEquals(o1, o2, message);
89 } else {
90 if(o1 != null) {
91 if(!(o1.equals(o2))) {
92 AssertionError error = new AssertionError((message != null ? message+" " : "") + "expected: " + o1 + " but was : " + o2);
93 errors.add(error);
94 }
95 }
96 }
97 }
98
99 public VersionedMapImpl<K, V> sut;
100 Map<K, V> oracle = new HashMap<K, V>();
101
102 public MapTestEnvironment(VersionedMapImpl<K, V> sut) {
103 this.sut = sut;
104 }
105
106 public void put(K key, V value) {
107 V oldSutValue = sut.put(key, value);
108 V oldOracleValue;
109 if (value != sut.getDefaultValue()) {
110 oldOracleValue = oracle.put(key, value);
111 } else {
112 oldOracleValue = oracle.remove(key);
113 }
114 if(oldSutValue == sut.getDefaultValue() && oldOracleValue != null) {
115 fail("After put, SUT old value was default, but oracle old walue was " + oldOracleValue);
116 }
117 if(oldSutValue != sut.getDefaultValue()) {
118 assertEquals(oldOracleValue, oldSutValue);
119 }
120 }
121
122 public void checkEquivalence(String title) {
123 // 0. Checking integrity
124 try {
125 sut.checkIntegrity();
126 } catch (IllegalStateException e) {
127 fail(title + ": " + e.getMessage());
128 }
129
130 // 1. Checking: if Reference contains <key,value> pair, then SUT contains
131 // <key,value> pair.
132 // Tests get functions
133 for (Entry<K, V> entry : oracle.entrySet()) {
134 V sutValue = sut.get(entry.getKey());
135 V oracleValue = entry.getValue();
136 if (sutValue != oracleValue) {
137 printComparison();
138 fail(title + ": Non-equivalent get(" + entry.getKey() + ") results: SUT=" + sutValue + ", Oracle="
139 + oracleValue + "!");
140 }
141 }
142
143 // 2. Checking: if SUT contains <key,value> pair, then Reference contains
144 // <key,value> pair.
145 // Tests iterators
146 int elementsInSutEntrySet = 0;
147 Cursor<K, V> cursor = sut.getAll();
148 while (cursor.move()) {
149 elementsInSutEntrySet++;
150 K key = cursor.getKey();
151 V sutValue = cursor.getValue();
152 // System.out.println(key + " -> " + sutValue);
153 V oracleValue = oracle.get(key);
154 if (sutValue != oracleValue) {
155 printComparison();
156 fail(title + ": Non-equivalent entry in iterator: SUT=<" + key + "," + sutValue + ">, Oracle=<" + key
157 + "," + oracleValue + ">!");
158 }
159
160 }
161
162 // 3. Checking sizes
163 // Counting of non-default value pairs.
164 int oracleSize = oracle.entrySet().size();
165 long sutSize = sut.getSize();
166 if (oracleSize != sutSize || oracleSize != elementsInSutEntrySet) {
167 printComparison();
168 fail(title + ": Non-eqivalent size() result: SUT.getSize()=" + sutSize + ", SUT.entryset.size="
169 + elementsInSutEntrySet + ", Oracle=" + oracleSize + "!");
170 }
171 }
172
173 public static <K,V> void checkOrder(String scenario, VersionedMap<K,V> versionedMap) {
174 K previous = null;
175 Cursor<K, V> cursor = versionedMap.getAll();
176 while(cursor.move()) {
177 System.out.println(cursor.getKey() + " " + ((VersionedMapImpl<K, V>) versionedMap).getHashProvider().getHash(cursor.getKey(), 0));
178 if(previous != null) {
179 int comparisonResult = ((VersionedMapImpl<K, V>) versionedMap).getHashProvider().compare(previous, cursor.getKey());
180 assertTrue(comparisonResult<0,scenario+" Cursor order is not incremental!");
181 }
182 previous = cursor.getKey();
183 }
184 System.out.println();
185 }
186
187 public void printComparison() {
188 System.out.println("SUT:");
189 printEntrySet(sut.getAll());
190 System.out.println("Oracle:");
191 printEntrySet(oracle.entrySet().iterator());
192 }
193
194 private void printEntrySet(Iterator<Entry<K, V>> iterator) {
195 TreeMap<K, V> treemap = new TreeMap<>();
196 while (iterator.hasNext()) {
197 Entry<K, V> entry = iterator.next();
198 treemap.put(entry.getKey(), entry.getValue());
199 }
200 for (Entry<K, V> e : treemap.entrySet()) {
201 System.out.println("\t" + e.getKey() + " -> " + e.getValue());
202 }
203 }
204
205 private void printEntrySet(Cursor<K, V> cursor) {
206 TreeMap<K, V> treemap = new TreeMap<>();
207 while (cursor.move()) {
208 treemap.put(cursor.getKey(), cursor.getValue());
209 }
210 for (Entry<K, V> e : treemap.entrySet()) {
211 System.out.println("\t" + e.getKey() + " -> " + e.getValue());
212 }
213 }
214}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/model/hashTests/HashEfficiencyTest.java b/subprojects/store/src/test/java/tools/refinery/store/model/hashTests/HashEfficiencyTest.java
new file mode 100644
index 00000000..7d070380
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/model/hashTests/HashEfficiencyTest.java
@@ -0,0 +1,161 @@
1package tools.refinery.store.model.hashTests;
2
3import static org.junit.jupiter.api.Assertions.assertEquals;
4
5import java.util.ArrayList;
6import java.util.LinkedList;
7import java.util.List;
8import java.util.Random;
9
10import org.junit.jupiter.api.Test;
11
12import tools.refinery.store.map.ContinousHashProvider;
13import tools.refinery.store.model.Tuple;
14import tools.refinery.store.model.TupleHashProvider;
15import tools.refinery.store.model.TupleHashProviderBitMagic;
16
17class HashEfficiencyTest {
18
19 private static List<Tuple> permutations(int range, int arity) {
20 if(arity == 1) {
21 List<Tuple> result = new ArrayList<>(range);
22 for(int i=0; i<range; i++) {
23 result.add(Tuple.of(i));
24 }
25 return result;
26 } else if(arity > 1) {
27 List<Tuple> smallers = permutations(range, arity-1);
28 List<Tuple> result = new ArrayList<>(range*smallers.size());
29 for(Tuple smaller : smallers) {
30 for(int i=0; i<range; i++) {
31 int[] larger = new int[arity];
32 for(int x = 0; x<smaller.getSize(); x++) {
33 larger[x] = smaller.get(x);
34 }
35 larger[arity-1] = i;
36 result.add(Tuple.of(larger));
37 }
38 }
39 return result;
40 } else throw new IllegalArgumentException();
41 }
42
43 private static int amountToRange(int arity, int n) {
44 int range = 1;
45 while(Math.pow(range,arity)<n+0.1) {
46 range++;
47 }
48 return 1024;
49 }
50
51 public static List<Tuple> nPermutations(int arity, int n) {
52 int range = amountToRange(arity, n);
53 List<Tuple> permutations = permutations(range, arity);
54 return permutations.subList(0, n);
55 }
56
57 public static List<Tuple> nRandoms(int arity, int n, int seed) {
58 int range = amountToRange(arity, n);
59 List<Tuple> permutations = new ArrayList<>(n);
60 Random r = new Random(seed);
61 for(int i = 0; i<n; i++) {
62 int[] tuple = new int[arity];
63 for(int j=0; j<arity; j++) {
64 tuple[j] = r.nextInt(range);
65 }
66 permutations.add(Tuple.of(tuple));
67 }
68 return permutations;
69 }
70
71 @Test
72 void permutationTest() {
73 List<Tuple> p = permutations(10, 2);
74 assertEquals(p.size(),10*10);
75 }
76// private void printTuples(List<Tuple> p) {
77// for(Tuple element : p) {
78// System.out.println(element);
79// }
80// }
81 @Test
82 void nPermutationTest() {
83 final int amount = 500;
84 List<Tuple> p = nPermutations(2, amount);
85 assertEquals(amount,p.size());
86 }
87 @Test
88 void nRandomTest() {
89 final int amount = 500;
90 List<Tuple> p = nRandoms(2, amount, 1);;
91 assertEquals(amount,p.size());
92 }
93 private static double calculateHashClashes(List<Tuple> tuples, ContinousHashProvider<Tuple> chp) {
94 int sumClashes = 0;
95
96 for(int i = 0; i<tuples.size(); i++) {
97 int height = 0;
98 for(int j=0; j<tuples.size(); j++) {
99 int clashes = calculateHashClash(chp, tuples.get(i), tuples.get(j));
100 height = Math.max(height, clashes);
101 }
102 sumClashes += height;
103 }
104 return (sumClashes+0.0) / tuples.size();
105 }
106 private static int calculateHashClash(ContinousHashProvider<Tuple> chp, Tuple a, Tuple b) {
107 if(a.equals(b)) return 0;
108 final int bits = 5;
109 final int segments = Integer.SIZE/bits;
110 final int mask = (1<<bits)-1;
111 for(int i = 0;;i++) {
112 int index = i/segments;
113 int depth = i%segments;
114 int aHash = (chp.getHash(a, index)>>(depth*5))&mask;
115 int bHash = (chp.getHash(b, index)>>(depth*5))&mask;
116 if(aHash != bHash) {
117 return i+1;
118 }
119 if(i>400) {
120 throw new IllegalStateException(a+" vs "+b);
121 }
122 }
123 }
124 private static double caclulateOptimalHashClash(int size) {
125 return (Math.log(size)/Math.log(32));
126 }
127 public static void main(String[] args) {
128 List<String> hashNames = new LinkedList<>();
129 List<ContinousHashProvider<Tuple>> hashes = new LinkedList<>();
130 hashNames.add("PrimeGroup");
131 hashes.add(new TupleHashProvider());
132 hashNames.add("BitMagic");
133 hashes.add(new TupleHashProviderBitMagic());
134
135 int[] arities = new int[] {2,3,4,5};
136 int[] sizes = new int[] {32*32,32*32*8};
137
138 System.out.println("Size,Arity,DataSource,Hash,Chashes,Optimal,Badness");
139 for(int size : sizes) {
140 double optimalClashes = caclulateOptimalHashClash(size);
141 for(int arity : arities) {
142 List<String> dataSourceNames = new LinkedList<>();
143 List<List<Tuple>> dataSources = new LinkedList<>();
144
145// dataSourceNames.add("Permutation");
146// dataSources.add(nPermutations(arity, size));
147 dataSourceNames.add("Random");
148 dataSources.add(nRandoms(arity, size, 0));
149
150 for(int dataSourceIndex = 0; dataSourceIndex<dataSourceNames.size(); dataSourceIndex++) {
151 for(int hashIndex = 0; hashIndex<hashNames.size(); hashIndex++) {
152 double clashes = calculateHashClashes(dataSources.get(dataSourceIndex),hashes.get(hashIndex));
153 System.out.println(
154 size+","+arity+","+dataSourceNames.get(dataSourceIndex)+","+hashNames.get(hashIndex)+","+
155 clashes+","+optimalClashes+","+(clashes+0.0)/optimalClashes);
156 }
157 }
158 }
159 }
160 }
161}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java b/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java
new file mode 100644
index 00000000..9d90b1e1
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java
@@ -0,0 +1,148 @@
1package tools.refinery.store.model.tests;
2
3import static org.junit.jupiter.api.Assertions.assertEquals;
4import static org.junit.jupiter.api.Assertions.assertFalse;
5import static org.junit.jupiter.api.Assertions.assertTrue;
6
7import java.util.Set;
8
9import org.junit.jupiter.api.Assertions;
10import org.junit.jupiter.api.Test;
11
12import tools.refinery.store.model.Model;
13import tools.refinery.store.model.ModelStore;
14import tools.refinery.store.model.ModelStoreImpl;
15import tools.refinery.store.model.Tuple;
16import tools.refinery.store.model.representation.Relation;
17
18class ModelTest {
19
20 @Test
21 void modelConstructionTest() {
22 Relation<Boolean> person = new Relation<>("Person", 1, false);
23 Relation<Boolean> friend = new Relation<>("friend", 2, false);
24
25 ModelStore store = new ModelStoreImpl(Set.of(person, friend));
26 Model model = store.createModel();
27
28 assertTrue(store.getDataRepresentations().contains(person));
29 assertTrue(store.getDataRepresentations().contains(friend));
30 assertTrue(model.getDataRepresentations().contains(person));
31 assertTrue(model.getDataRepresentations().contains(friend));
32
33 Relation<Integer> other = new Relation<Integer>("other", 2, null);
34 assertFalse(model.getDataRepresentations().contains(other));
35 }
36
37 @Test
38 void modelBuildingTest() {
39 Relation<Boolean> person = new Relation<>("Person", 1, false);
40 Relation<Integer> age = new Relation<Integer>("age", 1, null);
41 Relation<Boolean> friend = new Relation<>("friend", 2, false);
42
43 ModelStore store = new ModelStoreImpl(Set.of(person, age, friend));
44 Model model = store.createModel();
45
46 model.put(person, Tuple.of(0), true);
47 model.put(person, Tuple.of(1), true);
48 model.put(age, Tuple.of(0), 3);
49 model.put(age, Tuple.of(1), 1);
50 model.put(friend, Tuple.of(0, 1), true);
51 model.put(friend, Tuple.of(1, 0), true);
52
53 assertTrue(model.get(person, Tuple.of(0)));
54 assertTrue(model.get(person, Tuple.of(1)));
55 assertFalse(model.get(person, Tuple.of(2)));
56
57 assertEquals(3, model.get(age, Tuple.of(0)));
58 assertEquals(1, model.get(age, Tuple.of(1)));
59 assertEquals(null, model.get(age, Tuple.of(2)));
60
61 assertTrue(model.get(friend, Tuple.of(0, 1)));
62 assertFalse(model.get(friend, Tuple.of(0, 5)));
63 }
64
65 @Test
66 void modelBuildingArityFailTest() {
67 Relation<Boolean> person = new Relation<>("Person", 1, false);
68 ModelStore store = new ModelStoreImpl(Set.of(person));
69 Model model = store.createModel();
70
71 final Tuple tuple3 = Tuple.of(1, 1, 1);
72 Assertions.assertThrows(IllegalArgumentException.class, () -> model.put(person, tuple3, true));
73 Assertions.assertThrows(IllegalArgumentException.class, () -> model.get(person, tuple3));
74 }
75
76 @Test
77 void modelBuildingNullFailTest() {
78 Relation<Integer> age = new Relation<Integer>("age", 1, null);
79 ModelStore store = new ModelStoreImpl(Set.of(age));
80 Model model = store.createModel();
81
82 model.put(age, Tuple.of(1), null); // valid
83 Assertions.assertThrows(IllegalArgumentException.class, () -> model.put(age, null, 1));
84 Assertions.assertThrows(IllegalArgumentException.class, () -> model.get(age, null));
85
86 }
87
88 @Test
89 void modelUpdateTest() {
90 Relation<Boolean> person = new Relation<>("Person", 1, false);
91 Relation<Integer> age = new Relation<Integer>("age", 1, null);
92 Relation<Boolean> friend = new Relation<>("friend", 2, false);
93
94 ModelStore store = new ModelStoreImpl(Set.of(person, age, friend));
95 Model model = store.createModel();
96
97 model.put(person, Tuple.of(0), true);
98 model.put(person, Tuple.of(1), true);
99 model.put(age, Tuple.of(0), 3);
100 model.put(age, Tuple.of(1), 1);
101 model.put(friend, Tuple.of(0, 1), true);
102 model.put(friend, Tuple.of(1, 0), true);
103
104 assertEquals(3, model.get(age, Tuple.of(0)));
105 assertTrue(model.get(friend, Tuple.of(0, 1)));
106
107 model.put(age, Tuple.of(0), 4);
108 model.put(friend, Tuple.of(0, 1), false);
109
110 assertEquals(4, model.get(age, Tuple.of(0)));
111 assertFalse(model.get(friend, Tuple.of(0, 1)));
112 }
113
114 @Test
115 void restoreTest() {
116 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
117 Relation<Boolean> friend = new Relation<Boolean>("friend", 2, false);
118
119 ModelStore store = new ModelStoreImpl(Set.of(person, friend));
120 Model model = store.createModel();
121
122 model.put(person, Tuple.of(0), true);
123 model.put(person, Tuple.of(1), true);
124 model.put(friend, Tuple.of(0, 1), true);
125 model.put(friend, Tuple.of(1, 0), true);
126 long state1 = model.commit();
127
128 assertFalse(model.get(person, Tuple.of(2)));
129 assertFalse(model.get(friend, Tuple.of(0, 2)));
130
131 model.put(person, Tuple.of(2), true);
132 model.put(friend, Tuple.of(0, 2), true);
133 long state2 = model.commit();
134
135 assertTrue(model.get(person, Tuple.of(2)));
136 assertTrue(model.get(friend, Tuple.of(0, 2)));
137
138 model.restore(state1);
139
140 assertFalse(model.get(person, Tuple.of(2)));
141 assertFalse(model.get(friend, Tuple.of(0, 2)));
142
143 model.restore(state2);
144
145 assertTrue(model.get(person, Tuple.of(2)));
146 assertTrue(model.get(friend, Tuple.of(0, 2)));
147 }
148}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/query/test/QueryTest.java b/subprojects/store/src/test/java/tools/refinery/store/query/test/QueryTest.java
new file mode 100644
index 00000000..02381bcd
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/query/test/QueryTest.java
@@ -0,0 +1,445 @@
1package tools.refinery.store.query.test;
2
3import static org.junit.jupiter.api.Assertions.assertEquals;
4
5import java.util.ArrayList;
6import java.util.Arrays;
7import java.util.Collections;
8import java.util.HashSet;
9import java.util.List;
10import java.util.Set;
11import java.util.stream.Stream;
12
13import org.junit.jupiter.api.Test;
14
15import tools.refinery.store.model.Tuple;
16import tools.refinery.store.model.representation.Relation;
17import tools.refinery.store.model.representation.TruthValue;
18import tools.refinery.store.query.QueriableModel;
19import tools.refinery.store.query.QueriableModelStore;
20import tools.refinery.store.query.QueriableModelStoreImpl;
21import tools.refinery.store.query.building.DNFAnd;
22import tools.refinery.store.query.building.DNFPredicate;
23import tools.refinery.store.query.building.EquivalenceAtom;
24import tools.refinery.store.query.building.PredicateAtom;
25import tools.refinery.store.query.building.RelationAtom;
26import tools.refinery.store.query.building.Variable;
27import tools.refinery.store.query.view.FilteredRelationView;
28import tools.refinery.store.query.view.KeyOnlyRelationView;
29import tools.refinery.store.query.view.RelationView;
30
31class QueryTest {
32
33 static void compareMatchSets(Stream<Object[]> matchSet, Set<List<Tuple>> expected) {
34 Set<List<Tuple>> translatedMatchSet = new HashSet<>();
35 var interator = matchSet.iterator();
36 while (interator.hasNext()) {
37 var element = interator.next();
38 List<Tuple> elementToTranslatedMatchSet = new ArrayList<>();
39 for (int i = 0; i < element.length; i++) {
40 elementToTranslatedMatchSet.add((Tuple) element[i]);
41 }
42 translatedMatchSet.add(elementToTranslatedMatchSet);
43 }
44
45 assertEquals(expected, translatedMatchSet);
46 }
47
48 @Test
49 void typeConstraintTest() {
50 Relation<Boolean> person = new Relation<>("Person", 1, false);
51 Relation<Boolean> asset = new Relation<>("Asset", 1, false);
52 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
53
54 List<Variable> parameters = Arrays.asList(new Variable("p1"));
55 RelationAtom personRelationAtom = new RelationAtom(persionView, parameters);
56 DNFAnd clause = new DNFAnd(Collections.emptySet(), Arrays.asList(personRelationAtom));
57 DNFPredicate predicate = new DNFPredicate("TypeConstraint", parameters, Arrays.asList(clause));
58
59 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, asset), Set.of(persionView),
60 Set.of(predicate));
61 QueriableModel model = store.createModel();
62
63 model.put(person, Tuple.of(0), true);
64 model.put(person, Tuple.of(1), true);
65 model.put(asset, Tuple.of(1), true);
66 model.put(asset, Tuple.of(2), true);
67
68 model.flushChanges();
69 assertEquals(2, model.countResults(predicate));
70 compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0)), List.of(Tuple.of(1))));
71 }
72
73 @Test
74 void relationConstraintTest() {
75 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
76 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
77 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
78 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
79
80 Variable p1 = new Variable("p1");
81 Variable p2 = new Variable("p2");
82 List<Variable> parameters = Arrays.asList(p1, p2);
83
84 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
85 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
86 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
87 DNFAnd clause = new DNFAnd(Collections.emptySet(),
88 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom));
89 DNFPredicate predicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause));
90
91 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend),
92 Set.of(persionView, friendMustView), Set.of(predicate));
93 QueriableModel model = store.createModel();
94
95 assertEquals(0, model.countResults(predicate));
96
97 model.put(person, Tuple.of(0), true);
98 model.put(person, Tuple.of(1), true);
99 model.put(person, Tuple.of(2), true);
100 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
101 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
102 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
103
104 assertEquals(0, model.countResults(predicate));
105
106 model.flushChanges();
107 assertEquals(3, model.countResults(predicate));
108 compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0), Tuple.of(1)),
109 List.of(Tuple.of(1), Tuple.of(0)), List.of(Tuple.of(1), Tuple.of(2))));
110 }
111
112 @Test
113 void andTest() {
114 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
115 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
116 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
117 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
118
119 Variable p1 = new Variable("p1");
120 Variable p2 = new Variable("p2");
121 List<Variable> parameters = Arrays.asList(p1, p2);
122
123 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
124 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
125 RelationAtom friendRelationAtom1 = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
126 RelationAtom friendRelationAtom2 = new RelationAtom(friendMustView, Arrays.asList(p2, p1));
127 DNFAnd clause = new DNFAnd(Collections.emptySet(),
128 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom1, friendRelationAtom2));
129 DNFPredicate predicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause));
130
131 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend),
132 Set.of(persionView, friendMustView), Set.of(predicate));
133 QueriableModel model = store.createModel();
134
135 assertEquals(0, model.countResults(predicate));
136
137 model.put(person, Tuple.of(0), true);
138 model.put(person, Tuple.of(1), true);
139 model.put(person, Tuple.of(2), true);
140
141 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
142 model.put(friend, Tuple.of(0, 2), TruthValue.TRUE);
143
144 model.flushChanges();
145 assertEquals(0, model.countResults(predicate));
146
147 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
148 model.flushChanges();
149 assertEquals(2, model.countResults(predicate));
150 compareMatchSets(model.allResults(predicate),
151 Set.of(List.of(Tuple.of(0), Tuple.of(1)), List.of(Tuple.of(1), Tuple.of(0))));
152
153 model.put(friend, Tuple.of(2, 0), TruthValue.TRUE);
154 model.flushChanges();
155 assertEquals(4, model.countResults(predicate));
156 compareMatchSets(model.allResults(predicate),
157 Set.of(List.of(Tuple.of(0), Tuple.of(1)), List.of(Tuple.of(1), Tuple.of(0)),
158 List.of(Tuple.of(0), Tuple.of(2)), List.of(Tuple.of(2), Tuple.of(0))));
159 }
160
161 @Test
162 void existTest() {
163 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
164 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
165 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
166 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
167
168 Variable p1 = new Variable("p1");
169 Variable p2 = new Variable("p2");
170 List<Variable> parameters = Arrays.asList(p1);
171
172 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
173 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
174 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
175 DNFAnd clause = new DNFAnd(Set.of(p2),
176 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom));
177 DNFPredicate predicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause));
178
179 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend),
180 Set.of(persionView, friendMustView), Set.of(predicate));
181 QueriableModel model = store.createModel();
182
183 assertEquals(0, model.countResults(predicate));
184
185 model.put(person, Tuple.of(0), true);
186 model.put(person, Tuple.of(1), true);
187 model.put(person, Tuple.of(2), true);
188 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
189 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
190 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
191
192 assertEquals(0, model.countResults(predicate));
193
194 model.flushChanges();
195 assertEquals(2, model.countResults(predicate));
196 compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0)), List.of(Tuple.of(1))));
197 }
198
199 @Test
200 void orTest() {
201 Relation<Boolean> person = new Relation<>("Person", 1, false);
202 Relation<Boolean> animal = new Relation<>("Animal", 1, false);
203 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
204 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
205 RelationView<Boolean> animalView = new KeyOnlyRelationView(animal);
206 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
207
208 Variable p1 = new Variable("p1");
209 Variable p2 = new Variable("p2");
210 List<Variable> parameters = Arrays.asList(p1, p2);
211
212 // Person-Person friendship
213 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
214 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
215 RelationAtom friendRelationAtom1 = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
216 DNFAnd clause1 = new DNFAnd(Collections.emptySet(),
217 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom1));
218
219 // Animal-Animal friendship
220 RelationAtom animalRelationAtom1 = new RelationAtom(animalView, Arrays.asList(p1));
221 RelationAtom animalRelationAtom2 = new RelationAtom(animalView, Arrays.asList(p2));
222 RelationAtom friendRelationAtom2 = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
223 DNFAnd clause2 = new DNFAnd(Collections.emptySet(),
224 Arrays.asList(animalRelationAtom1, animalRelationAtom2, friendRelationAtom2));
225
226 // No inter-species friendship
227
228 DNFPredicate predicate = new DNFPredicate("Or", parameters, Arrays.asList(clause1, clause2));
229
230 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, animal, friend),
231 Set.of(persionView, animalView, friendMustView), Set.of(predicate));
232 QueriableModel model = store.createModel();
233
234 model.put(person, Tuple.of(0), true);
235 model.put(person, Tuple.of(1), true);
236 model.put(animal, Tuple.of(2), true);
237 model.put(animal, Tuple.of(3), true);
238 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
239 model.put(friend, Tuple.of(0, 2), TruthValue.TRUE);
240 model.put(friend, Tuple.of(2, 3), TruthValue.TRUE);
241 model.put(friend, Tuple.of(3, 0), TruthValue.TRUE);
242
243 model.flushChanges();
244 assertEquals(2, model.countResults(predicate));
245 compareMatchSets(model.allResults(predicate),
246 Set.of(List.of(Tuple.of(0), Tuple.of(1)), List.of(Tuple.of(2), Tuple.of(3))));
247 }
248
249 @Test
250 void equalityTest() {
251 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
252 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
253
254 Variable p1 = new Variable("p1");
255 Variable p2 = new Variable("p2");
256 List<Variable> parameters = Arrays.asList(p1, p2);
257
258 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
259 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
260 EquivalenceAtom equivalenceAtom = new EquivalenceAtom(true, p1, p2);
261 DNFAnd clause = new DNFAnd(Collections.emptySet(),
262 Arrays.asList(personRelationAtom1, personRelationAtom2, equivalenceAtom));
263 DNFPredicate predicate = new DNFPredicate("Equality", parameters, Arrays.asList(clause));
264
265 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person), Set.of(persionView), Set.of(predicate));
266 QueriableModel model = store.createModel();
267
268 model.put(person, Tuple.of(0), true);
269 model.put(person, Tuple.of(1), true);
270 model.put(person, Tuple.of(2), true);
271
272 model.flushChanges();
273 assertEquals(3, model.countResults(predicate));
274 compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0), Tuple.of(0)),
275 List.of(Tuple.of(1), Tuple.of(1)), List.of(Tuple.of(2), Tuple.of(2))));
276 }
277
278 @Test
279 void inequalityTest() {
280 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
281 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
282 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
283 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
284
285 Variable p1 = new Variable("p1");
286 Variable p2 = new Variable("p2");
287 Variable p3 = new Variable("p3");
288 List<Variable> parameters = Arrays.asList(p1, p2, p3);
289
290 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
291 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
292 RelationAtom friendRelationAtom1 = new RelationAtom(friendMustView, Arrays.asList(p1, p3));
293 RelationAtom friendRelationAtom2 = new RelationAtom(friendMustView, Arrays.asList(p2, p3));
294 EquivalenceAtom inequivalenceAtom = new EquivalenceAtom(false, p1, p2);
295 DNFAnd clause = new DNFAnd(Collections.emptySet(), Arrays.asList(personRelationAtom1, personRelationAtom2,
296 friendRelationAtom1, friendRelationAtom2, inequivalenceAtom));
297 DNFPredicate predicate = new DNFPredicate("Inequality", parameters, Arrays.asList(clause));
298
299 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend),
300 Set.of(persionView, friendMustView), Set.of(predicate));
301 QueriableModel model = store.createModel();
302
303 model.put(person, Tuple.of(0), true);
304 model.put(person, Tuple.of(1), true);
305 model.put(person, Tuple.of(2), true);
306 model.put(friend, Tuple.of(0, 2), TruthValue.TRUE);
307 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
308
309 model.flushChanges();
310 assertEquals(2, model.countResults(predicate));
311 compareMatchSets(model.allResults(predicate),
312 Set.of(List.of(Tuple.of(0), Tuple.of(1), Tuple.of(2)), List.of(Tuple.of(1), Tuple.of(0), Tuple.of(2))));
313 }
314
315 @Test
316 void patternCallTest() {
317 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
318 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
319 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
320 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
321
322 Variable p1 = new Variable("p1");
323 Variable p2 = new Variable("p2");
324 List<Variable> parameters = Arrays.asList(p1, p2);
325
326 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
327 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
328 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
329 DNFAnd clause = new DNFAnd(Collections.emptySet(),
330 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom));
331 DNFPredicate friendPredicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause));
332
333 Variable p3 = new Variable("p3");
334 Variable p4 = new Variable("p4");
335 List<Variable> substitution = Arrays.asList(p3, p4);
336 RelationAtom personRelationAtom3 = new RelationAtom(persionView, Arrays.asList(p3));
337 RelationAtom personRelationAtom4 = new RelationAtom(persionView, Arrays.asList(p4));
338 PredicateAtom friendPredicateAtom = new PredicateAtom(true, false, friendPredicate, substitution);
339 DNFAnd patternCallClause = new DNFAnd(Collections.emptySet(),
340 Arrays.asList(personRelationAtom3, personRelationAtom4, friendPredicateAtom));
341 DNFPredicate predicate = new DNFPredicate("PatternCall", substitution, Arrays.asList(patternCallClause));
342
343 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend),
344 Set.of(persionView, friendMustView), Set.of(friendPredicate, predicate));
345 QueriableModel model = store.createModel();
346
347 model.put(person, Tuple.of(0), true);
348 model.put(person, Tuple.of(1), true);
349 model.put(person, Tuple.of(2), true);
350 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
351 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
352 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
353
354 model.flushChanges();
355
356 assertEquals(3, model.countResults(friendPredicate));
357 }
358
359 @Test
360 void negativePatternCallTest() {
361 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
362 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
363 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
364 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
365
366 Variable p1 = new Variable("p1");
367 Variable p2 = new Variable("p2");
368 List<Variable> parameters = Arrays.asList(p1, p2);
369
370 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
371 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
372 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
373 DNFAnd clause = new DNFAnd(Collections.emptySet(),
374 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom));
375 DNFPredicate friendPredicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause));
376
377 Variable p3 = new Variable("p3");
378 Variable p4 = new Variable("p4");
379 List<Variable> substitution = Arrays.asList(p3, p4);
380 RelationAtom personRelationAtom3 = new RelationAtom(persionView, Arrays.asList(p3));
381 RelationAtom personRelationAtom4 = new RelationAtom(persionView, Arrays.asList(p4));
382 PredicateAtom friendPredicateAtom = new PredicateAtom(false, false, friendPredicate, substitution);
383 DNFAnd negativePatternCallClause = new DNFAnd(Collections.emptySet(),
384 Arrays.asList(personRelationAtom3, personRelationAtom4, friendPredicateAtom));
385 DNFPredicate predicate = new DNFPredicate("NegativePatternCall", substitution,
386 Arrays.asList(negativePatternCallClause));
387
388 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend),
389 Set.of(persionView, friendMustView), Set.of(friendPredicate, predicate));
390 QueriableModel model = store.createModel();
391
392 model.put(person, Tuple.of(0), true);
393 model.put(person, Tuple.of(1), true);
394 model.put(person, Tuple.of(2), true);
395 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
396 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
397 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
398
399 model.flushChanges();
400 assertEquals(6, model.countResults(predicate));
401 }
402
403 @Test
404 void transitivePatternCallTest() {
405 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
406 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
407 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
408 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
409
410 Variable p1 = new Variable("p1");
411 Variable p2 = new Variable("p2");
412 List<Variable> parameters = Arrays.asList(p1, p2);
413
414 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
415 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
416 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
417 DNFAnd clause = new DNFAnd(Collections.emptySet(),
418 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom));
419 DNFPredicate friendPredicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause));
420
421 Variable p3 = new Variable("p3");
422 Variable p4 = new Variable("p4");
423 List<Variable> substitution = Arrays.asList(p3, p4);
424 RelationAtom personRelationAtom3 = new RelationAtom(persionView, Arrays.asList(p3));
425 RelationAtom personRelationAtom4 = new RelationAtom(persionView, Arrays.asList(p4));
426 PredicateAtom friendPredicateAtom = new PredicateAtom(true, true, friendPredicate, substitution);
427 DNFAnd patternCallClause = new DNFAnd(Collections.emptySet(),
428 Arrays.asList(personRelationAtom3, personRelationAtom4, friendPredicateAtom));
429 DNFPredicate predicate = new DNFPredicate("TransitivePatternCall", substitution,
430 Arrays.asList(patternCallClause));
431
432 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend),
433 Set.of(persionView, friendMustView), Set.of(friendPredicate, predicate));
434 QueriableModel model = store.createModel();
435
436 model.put(person, Tuple.of(0), true);
437 model.put(person, Tuple.of(1), true);
438 model.put(person, Tuple.of(2), true);
439 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
440 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
441
442 model.flushChanges();
443 assertEquals(3, model.countResults(predicate));
444 }
445} \ No newline at end of file
diff --git a/subprojects/store/src/test/java/tools/refinery/store/query/test/QueryTransactionTest.java b/subprojects/store/src/test/java/tools/refinery/store/query/test/QueryTransactionTest.java
new file mode 100644
index 00000000..e72186b9
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/query/test/QueryTransactionTest.java
@@ -0,0 +1,58 @@
1package tools.refinery.store.query.test;
2
3import static org.junit.jupiter.api.Assertions.assertEquals;
4
5import java.util.Arrays;
6import java.util.Collections;
7import java.util.List;
8import java.util.Set;
9
10import org.junit.jupiter.api.Test;
11
12import tools.refinery.store.model.Tuple;
13import tools.refinery.store.model.representation.Relation;
14import tools.refinery.store.query.QueriableModel;
15import tools.refinery.store.query.QueriableModelStore;
16import tools.refinery.store.query.QueriableModelStoreImpl;
17import tools.refinery.store.query.building.DNFAnd;
18import tools.refinery.store.query.building.DNFPredicate;
19import tools.refinery.store.query.building.RelationAtom;
20import tools.refinery.store.query.building.Variable;
21import tools.refinery.store.query.view.KeyOnlyRelationView;
22import tools.refinery.store.query.view.RelationView;
23
24class QueryTransactionTest {
25 @Test
26 void flushTest() {
27 Relation<Boolean> person = new Relation<>("Person", 1, false);
28 Relation<Boolean> asset = new Relation<>("Asset", 1, false);
29 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
30
31 List<Variable> parameters = Arrays.asList(new Variable("p1"));
32 RelationAtom personRelationAtom = new RelationAtom(persionView, parameters);
33 DNFAnd clause = new DNFAnd(Collections.emptySet(), Arrays.asList(personRelationAtom));
34 DNFPredicate predicate = new DNFPredicate("TypeConstraint", parameters, Arrays.asList(clause));
35
36 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, asset), Set.of(persionView),
37 Set.of(predicate));
38 QueriableModel model = store.createModel();
39
40 assertEquals(0, model.countResults(predicate));
41
42 model.put(person, Tuple.of(0), true);
43 model.put(person, Tuple.of(1), true);
44 model.put(asset, Tuple.of(1), true);
45 model.put(asset, Tuple.of(2), true);
46
47 assertEquals(0, model.countResults(predicate));
48
49 model.flushChanges();
50 assertEquals(2, model.countResults(predicate));
51
52 model.put(person, Tuple.of(4), true);
53 assertEquals(2, model.countResults(predicate));
54
55 model.flushChanges();
56 assertEquals(3, model.countResults(predicate));
57 }
58}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/util/CollectionsUtilTests.java b/subprojects/store/src/test/java/tools/refinery/store/util/CollectionsUtilTests.java
new file mode 100644
index 00000000..171be0e5
--- /dev/null
+++ b/subprojects/store/src/test/java/tools/refinery/store/util/CollectionsUtilTests.java
@@ -0,0 +1,78 @@
1package tools.refinery.store.util;
2
3import static org.junit.jupiter.api.Assertions.assertEquals;
4import static tools.refinery.store.util.CollectionsUtil.filter;
5import static tools.refinery.store.util.CollectionsUtil.map;
6
7import java.util.ArrayList;
8import java.util.Iterator;
9import java.util.List;
10import java.util.NoSuchElementException;
11
12import org.junit.jupiter.api.Assertions;
13import org.junit.jupiter.api.Test;
14
15class CollectionsUtilTests {
16 List<Integer> list10 = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
17 List<String> listTen = List.of("1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
18
19 private static <T> void compare(Iterable<T> a, Iterable<T> b) {
20 List<T> listA = toList(a);
21 List<T> listB = toList(b);
22 assertEquals(listA, listB);
23 }
24
25 private static <T> List<T> toList(Iterable<T> a) {
26 List<T> result = new ArrayList<T>();
27 Iterator<T> iterator = a.iterator();
28 while (iterator.hasNext()) {
29 result.add(iterator.next());
30 }
31 return result;
32 }
33
34 @Test
35 void testFilterEven() {
36 compare(List.of(2, 4, 6, 8, 10), filter(list10, (x -> x % 2 == 0)));
37 }
38
39 @Test
40 void testFilterOdd() {
41 compare(List.of(1, 3, 5, 7, 9), filter(list10, (x -> x % 2 == 1)));
42 }
43
44 @Test
45 void testFilterFalse() {
46 compare(List.of(), filter(list10, (x -> false)));
47 }
48
49 @Test
50 void testFilterTrue() {
51 compare(list10, filter(list10, (x -> true)));
52 }
53
54 @Test
55 void testFilterEmpty() {
56 compare(List.of(), filter(List.of(), (x -> true)));
57 }
58
59 @Test()
60 void testNoSuchElement() {
61 Iterable<Integer> iterable = filter(list10, (x -> x % 2 == 0));
62 Iterator<Integer> iterator = iterable.iterator();
63 while (iterator.hasNext()) {
64 iterator.next();
65 }
66 Assertions.assertThrows(NoSuchElementException.class, () -> iterator.next());
67 }
68
69 @Test()
70 void mapTest() {
71 compare(listTen, map(list10, x -> x.toString()));
72 }
73
74 @Test()
75 void mapEmtyTest() {
76 compare(List.of(), map(List.of(), x -> x.toString()));
77 }
78}