diff options
Diffstat (limited to 'subprojects')
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 @@ | |||
1 | plugins { | ||
2 | id 'refinery-java-library' | ||
3 | id 'refinery-xtext-conventions' | ||
4 | } | ||
5 | |||
6 | dependencies { | ||
7 | api project(':refinery-language') | ||
8 | api libs.xtext.ide | ||
9 | api libs.xtext.xbase.ide | ||
10 | } | ||
11 | |||
12 | def generateXtextLanguage = project(':refinery-language').tasks.named('generateXtextLanguage') | ||
13 | |||
14 | for (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 | */ | ||
4 | package tools.refinery.language.ide; | ||
5 | |||
6 | import org.eclipse.xtext.ide.editor.contentassist.IPrefixMatcher; | ||
7 | import org.eclipse.xtext.ide.editor.contentassist.IdeCrossrefProposalProvider; | ||
8 | import org.eclipse.xtext.ide.editor.syntaxcoloring.ISemanticHighlightingCalculator; | ||
9 | |||
10 | import tools.refinery.language.ide.contentassist.FuzzyMatcher; | ||
11 | import tools.refinery.language.ide.contentassist.ProblemCrossrefProposalProvider; | ||
12 | import tools.refinery.language.ide.syntaxcoloring.ProblemSemanticHighlightingCalculator; | ||
13 | |||
14 | /** | ||
15 | * Use this class to register ide components. | ||
16 | */ | ||
17 | public 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 | */ | ||
4 | package tools.refinery.language.ide; | ||
5 | |||
6 | import org.eclipse.xtext.util.Modules2; | ||
7 | |||
8 | import com.google.inject.Guice; | ||
9 | import com.google.inject.Injector; | ||
10 | |||
11 | import tools.refinery.language.ProblemRuntimeModule; | ||
12 | import tools.refinery.language.ProblemStandaloneSetup; | ||
13 | |||
14 | /** | ||
15 | * Initialization support for running Xtext languages as language servers. | ||
16 | */ | ||
17 | public 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 @@ | |||
1 | package tools.refinery.language.ide.contentassist; | ||
2 | |||
3 | import org.eclipse.xtext.ide.editor.contentassist.IPrefixMatcher; | ||
4 | |||
5 | import 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 | ||
20 | public 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 @@ | |||
1 | package tools.refinery.language.ide.contentassist; | ||
2 | |||
3 | import java.util.Objects; | ||
4 | |||
5 | import org.eclipse.emf.ecore.EObject; | ||
6 | import org.eclipse.emf.ecore.util.EcoreUtil; | ||
7 | import org.eclipse.xtext.CrossReference; | ||
8 | import org.eclipse.xtext.GrammarUtil; | ||
9 | import org.eclipse.xtext.ide.editor.contentassist.ContentAssistContext; | ||
10 | import org.eclipse.xtext.ide.editor.contentassist.ContentAssistEntry; | ||
11 | import org.eclipse.xtext.ide.editor.contentassist.IdeCrossrefProposalProvider; | ||
12 | import org.eclipse.xtext.nodemodel.util.NodeModelUtils; | ||
13 | import org.eclipse.xtext.resource.IEObjectDescription; | ||
14 | |||
15 | import com.google.inject.Inject; | ||
16 | |||
17 | import tools.refinery.language.model.ProblemUtil; | ||
18 | import tools.refinery.language.model.problem.Problem; | ||
19 | import tools.refinery.language.resource.ReferenceCounter; | ||
20 | |||
21 | public 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 @@ | |||
1 | package tools.refinery.language.ide.syntaxcoloring; | ||
2 | |||
3 | import java.util.List; | ||
4 | |||
5 | import org.eclipse.emf.common.util.EList; | ||
6 | import org.eclipse.emf.ecore.EObject; | ||
7 | import org.eclipse.emf.ecore.EReference; | ||
8 | import org.eclipse.xtext.ide.editor.syntaxcoloring.DefaultSemanticHighlightingCalculator; | ||
9 | import org.eclipse.xtext.ide.editor.syntaxcoloring.IHighlightedPositionAcceptor; | ||
10 | import org.eclipse.xtext.nodemodel.INode; | ||
11 | import org.eclipse.xtext.nodemodel.util.NodeModelUtils; | ||
12 | import org.eclipse.xtext.service.OperationCanceledManager; | ||
13 | import org.eclipse.xtext.util.CancelIndicator; | ||
14 | |||
15 | import com.google.common.collect.ImmutableList; | ||
16 | import com.google.inject.Inject; | ||
17 | |||
18 | import tools.refinery.language.model.ProblemUtil; | ||
19 | import tools.refinery.language.model.problem.ClassDeclaration; | ||
20 | import tools.refinery.language.model.problem.NamedElement; | ||
21 | import tools.refinery.language.model.problem.Node; | ||
22 | import tools.refinery.language.model.problem.PredicateDefinition; | ||
23 | import tools.refinery.language.model.problem.ProblemPackage; | ||
24 | import tools.refinery.language.model.problem.ReferenceDeclaration; | ||
25 | |||
26 | public 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 @@ | |||
1 | Manifest-Version: 1.0 | ||
2 | Bundle-ManifestVersion: 2 | ||
3 | Bundle-Name: %pluginName | ||
4 | Bundle-SymbolicName: language-model;singleton:=true | ||
5 | Automatic-Module-Name: language-model | ||
6 | Bundle-Version: 0.0.0.qualifier | ||
7 | Bundle-ClassPath: . | ||
8 | Bundle-Vendor: %providerName | ||
9 | Bundle-Localization: plugin | ||
10 | Bundle-RequiredExecutionEnvironment: JavaSE-17 | ||
11 | Export-Package: tools.refinery.language.model.problem, | ||
12 | tools.refinery.language.model.problem.impl, | ||
13 | tools.refinery.language.model.problem.util | ||
14 | Require-Bundle: org.eclipse.emf.ecore;visibility:=reexport, | ||
15 | org.eclipse.emf.ecore.xmi;visibility:=reexport, | ||
16 | org.eclipse.core.runtime | ||
17 | Bundle-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 @@ | |||
1 | plugins { | ||
2 | id 'java-test-fixtures' | ||
3 | id 'refinery-java-library' | ||
4 | id 'refinery-mwe2' | ||
5 | id 'refinery-sonarqube' | ||
6 | } | ||
7 | |||
8 | dependencies { | ||
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 | |||
18 | sourceSets { | ||
19 | main { | ||
20 | java.srcDirs += ['src/main/emf-gen'] | ||
21 | } | ||
22 | } | ||
23 | |||
24 | def 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 | |||
36 | for (taskName in ['compileJava', 'processResources', 'generateEclipseSourceFolders']) { | ||
37 | tasks.named(taskName) { | ||
38 | dependsOn generateEPackage | ||
39 | } | ||
40 | } | ||
41 | |||
42 | tasks.named('clean') { | ||
43 | delete 'src/main/emf-gen' | ||
44 | } | ||
45 | |||
46 | sonarqube.properties { | ||
47 | properties['sonar.exclusions'] += [ | ||
48 | 'src/main/emf-gen/**', | ||
49 | ] | ||
50 | } | ||
51 | |||
52 | eclipse.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 | |||
3 | bin.includes = .,\ | ||
4 | src/main/resources/model/,\ | ||
5 | META-INF/,\ | ||
6 | plugin.xml,\ | ||
7 | plugin.properties | ||
8 | jars.compile.order = . | ||
9 | source.. = 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 | |||
3 | pluginName = tools.refinery.language.model | ||
4 | providerName = 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 @@ | |||
1 | module tools.refinery.language.model.GenerateProblemModel | ||
2 | |||
3 | Workflow { | ||
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 @@ | |||
1 | package tools.refinery.language.model; | ||
2 | |||
3 | import org.eclipse.emf.ecore.EPackage; | ||
4 | import org.eclipse.emf.ecore.resource.Resource; | ||
5 | |||
6 | import tools.refinery.language.model.problem.ProblemPackage; | ||
7 | import tools.refinery.language.model.problem.impl.ProblemFactoryImpl; | ||
8 | |||
9 | public 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 @@ | |||
1 | package tools.refinery.language.model; | ||
2 | |||
3 | import java.util.ArrayDeque; | ||
4 | import java.util.Collection; | ||
5 | import java.util.Deque; | ||
6 | import java.util.HashSet; | ||
7 | import java.util.Optional; | ||
8 | import java.util.Set; | ||
9 | |||
10 | import org.eclipse.emf.common.util.URI; | ||
11 | import org.eclipse.emf.ecore.EObject; | ||
12 | import org.eclipse.emf.ecore.resource.Resource; | ||
13 | |||
14 | import tools.refinery.language.model.problem.ClassDeclaration; | ||
15 | import tools.refinery.language.model.problem.ImplicitVariable; | ||
16 | import tools.refinery.language.model.problem.Node; | ||
17 | import tools.refinery.language.model.problem.Problem; | ||
18 | import tools.refinery.language.model.problem.ProblemPackage; | ||
19 | import tools.refinery.language.model.problem.ReferenceDeclaration; | ||
20 | import tools.refinery.language.model.problem.Relation; | ||
21 | import tools.refinery.language.model.problem.Variable; | ||
22 | |||
23 | public 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 @@ | |||
1 | package tools.refinery.language.model.tests; | ||
2 | |||
3 | import java.util.List; | ||
4 | import java.util.stream.Stream; | ||
5 | |||
6 | import org.eclipse.emf.ecore.resource.Resource.Diagnostic; | ||
7 | import org.eclipse.emf.ecore.util.EcoreUtil; | ||
8 | |||
9 | import tools.refinery.language.model.ProblemUtil; | ||
10 | import tools.refinery.language.model.problem.ActionLiteral; | ||
11 | import tools.refinery.language.model.problem.Argument; | ||
12 | import tools.refinery.language.model.problem.Assertion; | ||
13 | import tools.refinery.language.model.problem.AssertionArgument; | ||
14 | import tools.refinery.language.model.problem.Atom; | ||
15 | import tools.refinery.language.model.problem.ClassDeclaration; | ||
16 | import tools.refinery.language.model.problem.Conjunction; | ||
17 | import tools.refinery.language.model.problem.DeleteActionLiteral; | ||
18 | import tools.refinery.language.model.problem.EnumDeclaration; | ||
19 | import tools.refinery.language.model.problem.IndividualDeclaration; | ||
20 | import tools.refinery.language.model.problem.Literal; | ||
21 | import tools.refinery.language.model.problem.NamedElement; | ||
22 | import tools.refinery.language.model.problem.NegativeLiteral; | ||
23 | import tools.refinery.language.model.problem.NewActionLiteral; | ||
24 | import tools.refinery.language.model.problem.Node; | ||
25 | import tools.refinery.language.model.problem.NodeAssertionArgument; | ||
26 | import tools.refinery.language.model.problem.NodeValueAssertion; | ||
27 | import tools.refinery.language.model.problem.Parameter; | ||
28 | import tools.refinery.language.model.problem.ParametricDefinition; | ||
29 | import tools.refinery.language.model.problem.PredicateDefinition; | ||
30 | import tools.refinery.language.model.problem.Problem; | ||
31 | import tools.refinery.language.model.problem.ReferenceDeclaration; | ||
32 | import tools.refinery.language.model.problem.Relation; | ||
33 | import tools.refinery.language.model.problem.RuleDefinition; | ||
34 | import tools.refinery.language.model.problem.Statement; | ||
35 | import tools.refinery.language.model.problem.ValueActionLiteral; | ||
36 | import tools.refinery.language.model.problem.ValueLiteral; | ||
37 | import tools.refinery.language.model.problem.Variable; | ||
38 | import tools.refinery.language.model.problem.VariableOrNode; | ||
39 | import tools.refinery.language.model.problem.VariableOrNodeArgument; | ||
40 | |||
41 | public 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 @@ | |||
1 | plugins { | ||
2 | id 'refinery-java-library' | ||
3 | id 'refinery-xtext-conventions' | ||
4 | } | ||
5 | |||
6 | dependencies { | ||
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 @@ | |||
1 | package tools.refinery.language.mapping; | ||
2 | |||
3 | import java.util.HashMap; | ||
4 | import java.util.HashSet; | ||
5 | import java.util.Map; | ||
6 | import java.util.Optional; | ||
7 | |||
8 | import org.eclipse.emf.common.util.EList; | ||
9 | |||
10 | import tools.refinery.language.model.ProblemUtil; | ||
11 | import tools.refinery.language.model.problem.Assertion; | ||
12 | import tools.refinery.language.model.problem.AssertionArgument; | ||
13 | import tools.refinery.language.model.problem.ClassDeclaration; | ||
14 | import tools.refinery.language.model.problem.EnumDeclaration; | ||
15 | import tools.refinery.language.model.problem.IndividualDeclaration; | ||
16 | import tools.refinery.language.model.problem.LogicValue; | ||
17 | import tools.refinery.language.model.problem.Node; | ||
18 | import tools.refinery.language.model.problem.NodeAssertionArgument; | ||
19 | import tools.refinery.language.model.problem.PredicateDefinition; | ||
20 | import tools.refinery.language.model.problem.Problem; | ||
21 | import tools.refinery.language.model.problem.ReferenceDeclaration; | ||
22 | import tools.refinery.language.model.problem.Statement; | ||
23 | import tools.refinery.store.model.Model; | ||
24 | import tools.refinery.store.model.ModelStore; | ||
25 | import tools.refinery.store.model.ModelStoreImpl; | ||
26 | import tools.refinery.store.model.Tuple; | ||
27 | import tools.refinery.store.model.representation.Relation; | ||
28 | import tools.refinery.store.model.representation.TruthValue; | ||
29 | |||
30 | public 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 @@ | |||
1 | package tools.refinery.language.mapping; | ||
2 | |||
3 | import java.util.Map; | ||
4 | |||
5 | import tools.refinery.language.model.problem.Node; | ||
6 | import tools.refinery.store.model.Model; | ||
7 | import tools.refinery.store.model.representation.Relation; | ||
8 | import tools.refinery.store.model.representation.TruthValue; | ||
9 | |||
10 | public 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 @@ | |||
1 | package tools.refinery.language.mapping.tests | ||
2 | |||
3 | import com.google.inject.Inject | ||
4 | import org.eclipse.emf.ecore.util.EcoreUtil | ||
5 | import org.eclipse.xtext.testing.InjectWith | ||
6 | import org.eclipse.xtext.testing.extensions.InjectionExtension | ||
7 | import org.eclipse.xtext.testing.util.ParseHelper | ||
8 | import org.junit.jupiter.api.BeforeEach | ||
9 | import org.junit.jupiter.api.Test | ||
10 | import org.junit.jupiter.api.^extension.ExtendWith | ||
11 | import tools.refinery.language.mapping.PartialModelMapper | ||
12 | import tools.refinery.language.model.problem.Problem | ||
13 | import tools.refinery.language.model.tests.ProblemTestUtil | ||
14 | import tools.refinery.language.tests.ProblemInjectorProvider | ||
15 | import tools.refinery.store.model.Tuple | ||
16 | import tools.refinery.store.model.representation.TruthValue | ||
17 | |||
18 | import static org.hamcrest.MatcherAssert.assertThat | ||
19 | import static org.hamcrest.Matchers.* | ||
20 | import static org.junit.jupiter.api.Assertions.assertTrue | ||
21 | |||
22 | @ExtendWith(InjectionExtension) | ||
23 | @InjectWith(ProblemInjectorProvider) | ||
24 | class 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] | ||
2 | indent_style = tab | ||
3 | |||
4 | [src/main/js/xtext/**.js] | ||
5 | indent_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 | ||
3 | module.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 @@ | |||
1 | module.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 @@ | |||
1 | plugins { | ||
2 | id 'refinery-frontend-workspace' | ||
3 | id 'refinery-java-application' | ||
4 | id 'refinery-xtext-conventions' | ||
5 | } | ||
6 | |||
7 | import org.siouan.frontendgradleplugin.infrastructure.gradle.RunYarn | ||
8 | |||
9 | dependencies { | ||
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 | |||
23 | def generateXtextLanguage = project(':refinery-language').tasks.named('generateXtextLanguage') | ||
24 | |||
25 | for (taskName in ['compileJava', 'processResources']) { | ||
26 | tasks.named(taskName) { | ||
27 | dependsOn generateXtextLanguage | ||
28 | } | ||
29 | } | ||
30 | |||
31 | def webpackOutputDir = "${buildDir}/webpack" | ||
32 | def productionResources = "${webpackOutputDir}/production" | ||
33 | def serverMainClass = 'tools.refinery.language.web.ServerLauncher' | ||
34 | |||
35 | frontend { | ||
36 | assembleScript = 'assemble:webpack' | ||
37 | } | ||
38 | |||
39 | def installFrontend = tasks.named('installFrontend') | ||
40 | |||
41 | def 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 | |||
50 | def assembleFrontend = tasks.named('assembleFrontend') | ||
51 | assembleFrontend.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 | |||
62 | def 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 | |||
76 | def 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 | |||
90 | tasks.named('check') { | ||
91 | dependsOn(eslint, stylelint) | ||
92 | } | ||
93 | |||
94 | mainClassName = serverMainClass | ||
95 | |||
96 | tasks.named('jar') { | ||
97 | dependsOn assembleFrontend | ||
98 | from(productionResources) { | ||
99 | into 'webapp' | ||
100 | } | ||
101 | } | ||
102 | |||
103 | tasks.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 | |||
116 | def 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 | |||
127 | tasks.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 | |||
136 | sonarqube.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 @@ | |||
1 | package tools.refinery.language.web; | ||
2 | |||
3 | import java.io.IOException; | ||
4 | import java.time.Duration; | ||
5 | import java.util.regex.Pattern; | ||
6 | |||
7 | import org.eclipse.jetty.http.HttpHeader; | ||
8 | |||
9 | import jakarta.servlet.Filter; | ||
10 | import jakarta.servlet.FilterChain; | ||
11 | import jakarta.servlet.FilterConfig; | ||
12 | import jakarta.servlet.ServletException; | ||
13 | import jakarta.servlet.ServletRequest; | ||
14 | import jakarta.servlet.ServletResponse; | ||
15 | import jakarta.servlet.http.HttpServletRequest; | ||
16 | import jakarta.servlet.http.HttpServletResponse; | ||
17 | |||
18 | public 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 | */ | ||
4 | package tools.refinery.language.web; | ||
5 | |||
6 | import org.eclipse.xtext.web.server.XtextServiceDispatcher; | ||
7 | import org.eclipse.xtext.web.server.model.IWebDocumentProvider; | ||
8 | import org.eclipse.xtext.web.server.model.XtextWebDocumentAccess; | ||
9 | import org.eclipse.xtext.web.server.occurrences.OccurrencesService; | ||
10 | |||
11 | import tools.refinery.language.web.occurrences.ProblemOccurrencesService; | ||
12 | import tools.refinery.language.web.xtext.server.push.PushServiceDispatcher; | ||
13 | import tools.refinery.language.web.xtext.server.push.PushWebDocumentAccess; | ||
14 | import 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 | */ | ||
19 | public 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 | */ | ||
4 | package tools.refinery.language.web; | ||
5 | |||
6 | import org.eclipse.xtext.util.Modules2; | ||
7 | |||
8 | import com.google.inject.Guice; | ||
9 | import com.google.inject.Injector; | ||
10 | |||
11 | import tools.refinery.language.ProblemRuntimeModule; | ||
12 | import tools.refinery.language.ProblemStandaloneSetup; | ||
13 | import tools.refinery.language.ide.ProblemIdeModule; | ||
14 | |||
15 | /** | ||
16 | * Initialization support for running Xtext languages in web applications. | ||
17 | */ | ||
18 | public 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 @@ | |||
1 | package tools.refinery.language.web; | ||
2 | |||
3 | import org.eclipse.xtext.util.DisposableRegistry; | ||
4 | |||
5 | import jakarta.servlet.ServletException; | ||
6 | import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet; | ||
7 | |||
8 | public 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 | */ | ||
4 | package tools.refinery.language.web; | ||
5 | |||
6 | import java.io.File; | ||
7 | import java.io.IOException; | ||
8 | import java.net.InetSocketAddress; | ||
9 | import java.net.URI; | ||
10 | import java.net.URISyntaxException; | ||
11 | import java.util.EnumSet; | ||
12 | import java.util.Optional; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import org.eclipse.jetty.server.Server; | ||
16 | import org.eclipse.jetty.server.session.SessionHandler; | ||
17 | import org.eclipse.jetty.servlet.DefaultServlet; | ||
18 | import org.eclipse.jetty.servlet.ServletContextHandler; | ||
19 | import org.eclipse.jetty.servlet.ServletHolder; | ||
20 | import org.eclipse.jetty.util.resource.Resource; | ||
21 | import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; | ||
22 | import org.slf4j.Logger; | ||
23 | import org.slf4j.LoggerFactory; | ||
24 | |||
25 | import jakarta.servlet.DispatcherType; | ||
26 | import jakarta.servlet.SessionTrackingMode; | ||
27 | import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet; | ||
28 | |||
29 | public 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 @@ | |||
1 | package tools.refinery.language.web.occurrences; | ||
2 | |||
3 | import org.eclipse.emf.ecore.EObject; | ||
4 | import org.eclipse.xtext.web.server.occurrences.OccurrencesService; | ||
5 | |||
6 | import com.google.inject.Singleton; | ||
7 | |||
8 | import tools.refinery.language.model.problem.NamedElement; | ||
9 | |||
10 | @Singleton | ||
11 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server; | ||
2 | |||
3 | import java.util.Objects; | ||
4 | |||
5 | import org.eclipse.xtext.web.server.IServiceResult; | ||
6 | |||
7 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server; | ||
2 | |||
3 | import tools.refinery.language.web.xtext.server.message.XtextWebResponse; | ||
4 | |||
5 | @FunctionalInterface | ||
6 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server; | ||
2 | |||
3 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server; | ||
2 | |||
3 | import java.util.Set; | ||
4 | |||
5 | import org.eclipse.xtext.web.server.IServiceContext; | ||
6 | import org.eclipse.xtext.web.server.ISession; | ||
7 | |||
8 | import tools.refinery.language.web.xtext.server.push.PrecomputationListener; | ||
9 | |||
10 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server; | ||
2 | |||
3 | import java.lang.ref.WeakReference; | ||
4 | import java.util.ArrayList; | ||
5 | import java.util.HashMap; | ||
6 | import java.util.List; | ||
7 | import java.util.Map; | ||
8 | |||
9 | import org.eclipse.emf.common.util.URI; | ||
10 | import org.eclipse.xtext.resource.IResourceServiceProvider; | ||
11 | import org.eclipse.xtext.util.IDisposable; | ||
12 | import org.eclipse.xtext.web.server.IServiceContext; | ||
13 | import org.eclipse.xtext.web.server.IServiceResult; | ||
14 | import org.eclipse.xtext.web.server.ISession; | ||
15 | import org.eclipse.xtext.web.server.InvalidRequestException; | ||
16 | import org.eclipse.xtext.web.server.InvalidRequestException.UnknownLanguageException; | ||
17 | import org.eclipse.xtext.web.server.XtextServiceDispatcher; | ||
18 | import org.slf4j.Logger; | ||
19 | import org.slf4j.LoggerFactory; | ||
20 | |||
21 | import com.google.common.base.Strings; | ||
22 | import com.google.inject.Injector; | ||
23 | |||
24 | import tools.refinery.language.web.xtext.server.message.XtextWebErrorKind; | ||
25 | import tools.refinery.language.web.xtext.server.message.XtextWebErrorResponse; | ||
26 | import tools.refinery.language.web.xtext.server.message.XtextWebOkResponse; | ||
27 | import tools.refinery.language.web.xtext.server.message.XtextWebPushMessage; | ||
28 | import tools.refinery.language.web.xtext.server.message.XtextWebRequest; | ||
29 | import tools.refinery.language.web.xtext.server.push.PrecomputationListener; | ||
30 | import tools.refinery.language.web.xtext.server.push.PushWebDocument; | ||
31 | import tools.refinery.language.web.xtext.servlet.SimpleServiceContext; | ||
32 | |||
33 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server.message; | ||
2 | |||
3 | import com.google.gson.annotations.SerializedName; | ||
4 | |||
5 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server.message; | ||
2 | |||
3 | import java.util.Objects; | ||
4 | |||
5 | import com.google.gson.annotations.SerializedName; | ||
6 | |||
7 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server.message; | ||
2 | |||
3 | import java.util.Objects; | ||
4 | |||
5 | import org.eclipse.xtext.web.server.IServiceResult; | ||
6 | import org.eclipse.xtext.web.server.IUnwrappableServiceResult; | ||
7 | |||
8 | import com.google.gson.annotations.SerializedName; | ||
9 | |||
10 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server.message; | ||
2 | |||
3 | import java.util.Objects; | ||
4 | |||
5 | import com.google.gson.annotations.SerializedName; | ||
6 | |||
7 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server.message; | ||
2 | |||
3 | import java.util.Map; | ||
4 | import java.util.Objects; | ||
5 | |||
6 | import com.google.gson.annotations.SerializedName; | ||
7 | |||
8 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server.message; | ||
2 | |||
3 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server.push; | ||
2 | |||
3 | import org.eclipse.xtext.web.server.IServiceResult; | ||
4 | |||
5 | import tools.refinery.language.web.xtext.server.ResponseHandlerException; | ||
6 | |||
7 | @FunctionalInterface | ||
8 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server.push; | ||
2 | |||
3 | import org.eclipse.xtext.web.server.IServiceContext; | ||
4 | import org.eclipse.xtext.web.server.XtextServiceDispatcher; | ||
5 | import org.eclipse.xtext.web.server.model.XtextWebDocument; | ||
6 | |||
7 | import com.google.inject.Singleton; | ||
8 | |||
9 | import tools.refinery.language.web.xtext.server.SubscribingServiceContext; | ||
10 | |||
11 | @Singleton | ||
12 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server.push; | ||
2 | |||
3 | import java.util.ArrayList; | ||
4 | import java.util.HashMap; | ||
5 | import java.util.List; | ||
6 | import java.util.Map; | ||
7 | |||
8 | import org.eclipse.xtext.util.CancelIndicator; | ||
9 | import org.eclipse.xtext.web.server.IServiceResult; | ||
10 | import org.eclipse.xtext.web.server.model.AbstractCachedService; | ||
11 | import org.eclipse.xtext.web.server.model.DocumentSynchronizer; | ||
12 | import org.eclipse.xtext.web.server.model.XtextWebDocument; | ||
13 | import org.slf4j.Logger; | ||
14 | import org.slf4j.LoggerFactory; | ||
15 | |||
16 | import com.google.common.collect.ImmutableList; | ||
17 | |||
18 | import tools.refinery.language.web.xtext.server.ResponseHandlerException; | ||
19 | |||
20 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server.push; | ||
2 | |||
3 | import org.eclipse.xtext.service.OperationCanceledManager; | ||
4 | import org.eclipse.xtext.util.CancelIndicator; | ||
5 | import org.eclipse.xtext.util.concurrent.CancelableUnitOfWork; | ||
6 | import org.eclipse.xtext.web.server.IServiceResult; | ||
7 | import org.eclipse.xtext.web.server.model.AbstractCachedService; | ||
8 | import org.eclipse.xtext.web.server.model.IXtextWebDocument; | ||
9 | import org.eclipse.xtext.web.server.model.PrecomputedServiceRegistry; | ||
10 | import org.eclipse.xtext.web.server.model.XtextWebDocument; | ||
11 | import org.eclipse.xtext.web.server.model.XtextWebDocumentAccess; | ||
12 | import org.eclipse.xtext.web.server.syntaxcoloring.HighlightingService; | ||
13 | import org.eclipse.xtext.web.server.validation.ValidationService; | ||
14 | |||
15 | import com.google.inject.Inject; | ||
16 | |||
17 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.server.push; | ||
2 | |||
3 | import org.eclipse.xtext.web.server.IServiceContext; | ||
4 | import org.eclipse.xtext.web.server.model.DocumentSynchronizer; | ||
5 | import org.eclipse.xtext.web.server.model.IWebDocumentProvider; | ||
6 | import org.eclipse.xtext.web.server.model.XtextWebDocument; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import com.google.inject.Provider; | ||
10 | import 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 | ||
19 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.servlet; | ||
2 | |||
3 | import java.util.Map; | ||
4 | import java.util.Set; | ||
5 | |||
6 | import org.eclipse.xtext.web.server.IServiceContext; | ||
7 | import org.eclipse.xtext.web.server.ISession; | ||
8 | |||
9 | import com.google.common.collect.ImmutableSet; | ||
10 | |||
11 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.servlet; | ||
2 | |||
3 | import java.util.HashMap; | ||
4 | import java.util.Map; | ||
5 | |||
6 | import org.eclipse.xtext.web.server.ISession; | ||
7 | import org.eclipse.xtext.xbase.lib.Functions.Function0; | ||
8 | |||
9 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.servlet; | ||
2 | |||
3 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.servlet; | ||
2 | |||
3 | import java.io.IOException; | ||
4 | import java.io.Reader; | ||
5 | |||
6 | import org.eclipse.jetty.websocket.api.Session; | ||
7 | import org.eclipse.jetty.websocket.api.StatusCode; | ||
8 | import org.eclipse.jetty.websocket.api.WriteCallback; | ||
9 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; | ||
10 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; | ||
11 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; | ||
12 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; | ||
13 | import org.eclipse.jetty.websocket.api.annotations.WebSocket; | ||
14 | import org.eclipse.xtext.resource.IResourceServiceProvider; | ||
15 | import org.eclipse.xtext.web.server.ISession; | ||
16 | import org.slf4j.Logger; | ||
17 | import org.slf4j.LoggerFactory; | ||
18 | |||
19 | import com.google.gson.Gson; | ||
20 | import com.google.gson.JsonIOException; | ||
21 | import com.google.gson.JsonParseException; | ||
22 | |||
23 | import tools.refinery.language.web.xtext.server.ResponseHandler; | ||
24 | import tools.refinery.language.web.xtext.server.ResponseHandlerException; | ||
25 | import tools.refinery.language.web.xtext.server.TransactionExecutor; | ||
26 | import tools.refinery.language.web.xtext.server.message.XtextWebRequest; | ||
27 | import tools.refinery.language.web.xtext.server.message.XtextWebResponse; | ||
28 | |||
29 | @WebSocket | ||
30 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.servlet; | ||
2 | |||
3 | import java.io.IOException; | ||
4 | import java.time.Duration; | ||
5 | import java.util.Set; | ||
6 | |||
7 | import org.eclipse.jetty.websocket.server.JettyServerUpgradeRequest; | ||
8 | import org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse; | ||
9 | import org.eclipse.jetty.websocket.server.JettyWebSocketCreator; | ||
10 | import org.eclipse.jetty.websocket.server.JettyWebSocketServlet; | ||
11 | import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory; | ||
12 | import org.eclipse.xtext.resource.IResourceServiceProvider; | ||
13 | import org.slf4j.Logger; | ||
14 | import org.slf4j.LoggerFactory; | ||
15 | |||
16 | import jakarta.servlet.ServletConfig; | ||
17 | import jakarta.servlet.ServletException; | ||
18 | |||
19 | public 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 @@ | |||
1 | import AppBar from '@mui/material/AppBar'; | ||
2 | import Box from '@mui/material/Box'; | ||
3 | import IconButton from '@mui/material/IconButton'; | ||
4 | import Toolbar from '@mui/material/Toolbar'; | ||
5 | import Typography from '@mui/material/Typography'; | ||
6 | import MenuIcon from '@mui/icons-material/Menu'; | ||
7 | import React from 'react'; | ||
8 | |||
9 | import { EditorArea } from './editor/EditorArea'; | ||
10 | import { EditorButtons } from './editor/EditorButtons'; | ||
11 | import { GenerateButton } from './editor/GenerateButton'; | ||
12 | |||
13 | export 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 @@ | |||
1 | import React, { createContext, useContext } from 'react'; | ||
2 | |||
3 | import { EditorStore } from './editor/EditorStore'; | ||
4 | import { ThemeStore } from './theme/ThemeStore'; | ||
5 | |||
6 | export 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 | |||
17 | const StoreContext = createContext<RootStore | undefined>(undefined); | ||
18 | |||
19 | export interface RootStoreProviderProps { | ||
20 | children: JSX.Element; | ||
21 | |||
22 | rootStore: RootStore; | ||
23 | } | ||
24 | |||
25 | export function RootStoreProvider({ children, rootStore }: RootStoreProviderProps): JSX.Element { | ||
26 | return ( | ||
27 | <StoreContext.Provider value={rootStore}> | ||
28 | {children} | ||
29 | </StoreContext.Provider> | ||
30 | ); | ||
31 | } | ||
32 | |||
33 | export 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 @@ | |||
1 | import { Command, EditorView } from '@codemirror/view'; | ||
2 | import { closeSearchPanel, openSearchPanel } from '@codemirror/search'; | ||
3 | import { closeLintPanel, openLintPanel } from '@codemirror/lint'; | ||
4 | import { observer } from 'mobx-react-lite'; | ||
5 | import React, { | ||
6 | useCallback, | ||
7 | useEffect, | ||
8 | useRef, | ||
9 | useState, | ||
10 | } from 'react'; | ||
11 | |||
12 | import { EditorParent } from './EditorParent'; | ||
13 | import { useRootStore } from '../RootStore'; | ||
14 | import { getLogger } from '../utils/logger'; | ||
15 | |||
16 | const log = getLogger('editor.EditorArea'); | ||
17 | |||
18 | function 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 | |||
66 | function 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 | |||
73 | export 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 @@ | |||
1 | import type { Diagnostic } from '@codemirror/lint'; | ||
2 | import { observer } from 'mobx-react-lite'; | ||
3 | import IconButton from '@mui/material/IconButton'; | ||
4 | import Stack from '@mui/material/Stack'; | ||
5 | import ToggleButton from '@mui/material/ToggleButton'; | ||
6 | import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; | ||
7 | import CheckIcon from '@mui/icons-material/Check'; | ||
8 | import ErrorIcon from '@mui/icons-material/Error'; | ||
9 | import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered'; | ||
10 | import FormatPaint from '@mui/icons-material/FormatPaint'; | ||
11 | import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; | ||
12 | import RedoIcon from '@mui/icons-material/Redo'; | ||
13 | import SearchIcon from '@mui/icons-material/Search'; | ||
14 | import UndoIcon from '@mui/icons-material/Undo'; | ||
15 | import WarningIcon from '@mui/icons-material/Warning'; | ||
16 | import React from 'react'; | ||
17 | |||
18 | import { useRootStore } from '../RootStore'; | ||
19 | |||
20 | // Exhastive switch as proven by TypeScript. | ||
21 | // eslint-disable-next-line consistent-return | ||
22 | function 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 | |||
35 | export 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 @@ | |||
1 | import { 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 | */ | ||
12 | function 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 | |||
20 | export 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 @@ | |||
1 | import { autocompletion, completionKeymap } from '@codemirror/autocomplete'; | ||
2 | import { closeBrackets, closeBracketsKeymap } from '@codemirror/closebrackets'; | ||
3 | import { defaultKeymap, indentWithTab } from '@codemirror/commands'; | ||
4 | import { commentKeymap } from '@codemirror/comment'; | ||
5 | import { foldGutter, foldKeymap } from '@codemirror/fold'; | ||
6 | import { highlightActiveLineGutter, lineNumbers } from '@codemirror/gutter'; | ||
7 | import { classHighlightStyle } from '@codemirror/highlight'; | ||
8 | import { | ||
9 | history, | ||
10 | historyKeymap, | ||
11 | redo, | ||
12 | redoDepth, | ||
13 | undo, | ||
14 | undoDepth, | ||
15 | } from '@codemirror/history'; | ||
16 | import { indentOnInput } from '@codemirror/language'; | ||
17 | import { | ||
18 | Diagnostic, | ||
19 | lintKeymap, | ||
20 | setDiagnostics, | ||
21 | } from '@codemirror/lint'; | ||
22 | import { bracketMatching } from '@codemirror/matchbrackets'; | ||
23 | import { rectangularSelection } from '@codemirror/rectangular-selection'; | ||
24 | import { searchConfig, searchKeymap } from '@codemirror/search'; | ||
25 | import { | ||
26 | EditorState, | ||
27 | StateCommand, | ||
28 | StateEffect, | ||
29 | Transaction, | ||
30 | TransactionSpec, | ||
31 | } from '@codemirror/state'; | ||
32 | import { | ||
33 | drawSelection, | ||
34 | EditorView, | ||
35 | highlightActiveLine, | ||
36 | highlightSpecialChars, | ||
37 | keymap, | ||
38 | } from '@codemirror/view'; | ||
39 | import { | ||
40 | makeAutoObservable, | ||
41 | observable, | ||
42 | reaction, | ||
43 | } from 'mobx'; | ||
44 | |||
45 | import { findOccurrences, IOccurrence, setOccurrences } from './findOccurrences'; | ||
46 | import { problemLanguageSupport } from '../language/problemLanguageSupport'; | ||
47 | import { | ||
48 | IHighlightRange, | ||
49 | semanticHighlighting, | ||
50 | setSemanticHighlighting, | ||
51 | } from './semanticHighlighting'; | ||
52 | import type { ThemeStore } from '../theme/ThemeStore'; | ||
53 | import { getLogger } from '../utils/logger'; | ||
54 | import { XtextClient } from '../xtext/XtextClient'; | ||
55 | |||
56 | const log = getLogger('editor.EditorStore'); | ||
57 | |||
58 | export 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 @@ | |||
1 | import { observer } from 'mobx-react-lite'; | ||
2 | import Button from '@mui/material/Button'; | ||
3 | import PlayArrowIcon from '@mui/icons-material/PlayArrow'; | ||
4 | import React from 'react'; | ||
5 | |||
6 | import { useRootStore } from '../RootStore'; | ||
7 | |||
8 | const GENERATE_LABEL = 'Generate'; | ||
9 | |||
10 | export 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 @@ | |||
1 | import { StateEffect, StateField, TransactionSpec } from '@codemirror/state'; | ||
2 | import { EditorView, Decoration, DecorationSet } from '@codemirror/view'; | ||
3 | |||
4 | export type TransactionSpecFactory = (decorations: DecorationSet) => TransactionSpec; | ||
5 | |||
6 | export 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 @@ | |||
1 | import { Range, RangeSet } from '@codemirror/rangeset'; | ||
2 | import type { TransactionSpec } from '@codemirror/state'; | ||
3 | import { Decoration } from '@codemirror/view'; | ||
4 | |||
5 | import { decorationSetExtension } from './decorationSetExtension'; | ||
6 | |||
7 | export interface IOccurrence { | ||
8 | from: number; | ||
9 | |||
10 | to: number; | ||
11 | } | ||
12 | |||
13 | const [setOccurrencesInteral, findOccurrences] = decorationSetExtension(); | ||
14 | |||
15 | const writeDecoration = Decoration.mark({ | ||
16 | class: 'cm-problem-write', | ||
17 | }); | ||
18 | |||
19 | const readDecoration = Decoration.mark({ | ||
20 | class: 'cm-problem-read', | ||
21 | }); | ||
22 | |||
23 | export 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 | |||
35 | export { 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 @@ | |||
1 | import { RangeSet } from '@codemirror/rangeset'; | ||
2 | import type { TransactionSpec } from '@codemirror/state'; | ||
3 | import { Decoration } from '@codemirror/view'; | ||
4 | |||
5 | import { decorationSetExtension } from './decorationSetExtension'; | ||
6 | |||
7 | export interface IHighlightRange { | ||
8 | from: number; | ||
9 | |||
10 | to: number; | ||
11 | |||
12 | classes: string[]; | ||
13 | } | ||
14 | |||
15 | const [setSemanticHighlightingInternal, semanticHighlighting] = decorationSetExtension(); | ||
16 | |||
17 | export 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 | |||
24 | export { 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 @@ | |||
1 | declare const DEBUG: boolean; | ||
2 | |||
3 | declare const PACKAGE_NAME: string; | ||
4 | |||
5 | declare const PACKAGE_VERSION: string; | ||
6 | |||
7 | declare 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 @@ | |||
1 | import React from 'react'; | ||
2 | import { render } from 'react-dom'; | ||
3 | import CssBaseline from '@mui/material/CssBaseline'; | ||
4 | |||
5 | import { App } from './App'; | ||
6 | import { RootStore, RootStoreProvider } from './RootStore'; | ||
7 | import { ThemeProvider } from './theme/ThemeProvider'; | ||
8 | |||
9 | import '../css/index.scss'; | ||
10 | |||
11 | const initialValue = `class Family { | ||
12 | contains Person[] members | ||
13 | } | ||
14 | |||
15 | class Person { | ||
16 | Person[] children opposite parent | ||
17 | Person[0..1] parent opposite children | ||
18 | int age | ||
19 | TaxStatus taxStatus | ||
20 | } | ||
21 | |||
22 | enum TaxStatus { | ||
23 | child, student, adult, retired | ||
24 | } | ||
25 | |||
26 | % A child cannot have any dependents. | ||
27 | pred invalidTaxStatus(Person p) <-> | ||
28 | taxStatus(p, child), | ||
29 | children(p, _q) | ||
30 | ; taxStatus(p, retired), | ||
31 | parent(p, q), | ||
32 | !taxStatus(q, retired). | ||
33 | |||
34 | direct 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 | |||
41 | indiv family. | ||
42 | Family(family). | ||
43 | members(family, anne). | ||
44 | members(family, bob). | ||
45 | members(family, ciri). | ||
46 | children(anne, ciri). | ||
47 | ?children(bob, ciri). | ||
48 | default children(ciri, *): false. | ||
49 | taxStatus(anne, adult). | ||
50 | age(anne, 35). | ||
51 | bobAge: 27. | ||
52 | age(bob, bobAge). | ||
53 | !age(ciri, bobAge). | ||
54 | |||
55 | scope Family = 1, Person += 5..10. | ||
56 | `; | ||
57 | |||
58 | const rootStore = new RootStore(initialValue); | ||
59 | |||
60 | const app = ( | ||
61 | <RootStoreProvider rootStore={rootStore}> | ||
62 | <ThemeProvider> | ||
63 | <CssBaseline /> | ||
64 | <App /> | ||
65 | </ThemeProvider> | ||
66 | </RootStoreProvider> | ||
67 | ); | ||
68 | |||
69 | render(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 @@ | |||
1 | import { EditorState } from '@codemirror/state'; | ||
2 | import type { SyntaxNode } from '@lezer/common'; | ||
3 | |||
4 | export 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 | */ | ||
12 | export 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 | */ | ||
50 | export 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 | */ | ||
78 | function 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 | |||
103 | export function foldWholeNode(node: SyntaxNode): FoldRange { | ||
104 | return { | ||
105 | from: node.from, | ||
106 | to: node.to, | ||
107 | }; | ||
108 | } | ||
109 | |||
110 | export 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 @@ | |||
1 | import { 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 | */ | ||
13 | function 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 | */ | ||
61 | function 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 | |||
69 | export function indentBlockComment(): number { | ||
70 | // Do not indent. | ||
71 | return -1; | ||
72 | } | ||
73 | |||
74 | export function indentDeclaration(context: TreeIndentContext): number { | ||
75 | return indentDeclarationStrategy(context, 1); | ||
76 | } | ||
77 | |||
78 | export 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 | |||
7 | statement { | ||
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 | |||
45 | ReferenceDeclaration { | ||
46 | (kw<"refers"> | kw<"contains">)? | ||
47 | RelationName | ||
48 | RelationName | ||
49 | ( "[" Multiplicity? "]" )? | ||
50 | (kw<"opposite"> RelationName)? | ||
51 | ";"? | ||
52 | } | ||
53 | |||
54 | Parameter { RelationName? VariableName } | ||
55 | |||
56 | Conjunction { ("," | Literal)+ } | ||
57 | |||
58 | OrOp { ";" } | ||
59 | |||
60 | Literal { NotOp? Atom (("=" | ":") sep1<"|", LogicValue>)? } | ||
61 | |||
62 | Atom { RelationName "+"? ParameterList<Argument> } | ||
63 | |||
64 | Action { ("," | ActionLiteral)+ } | ||
65 | |||
66 | ActionLiteral { | ||
67 | ckw<"new"> VariableName | | ||
68 | ckw<"delete"> VariableName | | ||
69 | Literal | ||
70 | } | ||
71 | |||
72 | Argument { VariableName | Constant } | ||
73 | |||
74 | AssertionArgument { NodeName | StarArgument | Constant } | ||
75 | |||
76 | Constant { Real | String } | ||
77 | |||
78 | LogicValue { | ||
79 | ckw<"true"> | ckw<"false"> | ckw<"unknown"> | ckw<"error"> | ||
80 | } | ||
81 | |||
82 | ScopeElement { RelationName ("=" | "+=") Multiplicity } | ||
83 | |||
84 | Multiplicity { (IntMult "..")? (IntMult | StarMult)} | ||
85 | |||
86 | RelationName { QualifiedName } | ||
87 | |||
88 | RuleName { QualifiedName } | ||
89 | |||
90 | IndividualNodeName { QualifiedName } | ||
91 | |||
92 | VariableName { QualifiedName } | ||
93 | |||
94 | NodeName { QualifiedName } | ||
95 | |||
96 | QualifiedName[implicitCompletion=true] { identifier ("::" identifier)* } | ||
97 | |||
98 | kw<term> { @specialize[@name={term},implicitCompletion=true]<identifier, term> } | ||
99 | |||
100 | ckw<term> { @extend[@name={term},implicitCompletion=true]<identifier, term> } | ||
101 | |||
102 | ParameterList<content> { "(" sep<",", content> ")" } | ||
103 | |||
104 | sep<separator, content> { sep1<separator, content>? } | ||
105 | |||
106 | sep1<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 @@ | |||
1 | import { styleTags, tags as t } from '@codemirror/highlight'; | ||
2 | import { | ||
3 | foldInside, | ||
4 | foldNodeProp, | ||
5 | indentNodeProp, | ||
6 | indentUnit, | ||
7 | LanguageSupport, | ||
8 | LRLanguage, | ||
9 | } from '@codemirror/language'; | ||
10 | import { LRParser } from '@lezer/lr'; | ||
11 | |||
12 | import { parser } from '../../../../build/generated/sources/lezer/problem'; | ||
13 | import { | ||
14 | foldBlockComment, | ||
15 | foldConjunction, | ||
16 | foldDeclaration, | ||
17 | foldWholeNode, | ||
18 | } from './folding'; | ||
19 | import { | ||
20 | indentBlockComment, | ||
21 | indentDeclaration, | ||
22 | indentPredicateOrRule, | ||
23 | } from './indentation'; | ||
24 | |||
25 | const 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 | |||
74 | const 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 | |||
88 | export 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 @@ | |||
1 | import { NodeProp } from '@lezer/common'; | ||
2 | |||
3 | export 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 @@ | |||
1 | import type { PaletteMode } from '@mui/material'; | ||
2 | |||
3 | import cssVariables from '../../css/themeVariables.module.scss'; | ||
4 | |||
5 | export enum EditorTheme { | ||
6 | Light, | ||
7 | Dark, | ||
8 | } | ||
9 | |||
10 | export 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 | |||
42 | export const DEFAULT_THEME = EditorTheme.Dark; | ||
43 | |||
44 | export 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 @@ | |||
1 | import { observer } from 'mobx-react-lite'; | ||
2 | import { ThemeProvider as MaterialUiThemeProvider } from '@mui/material/styles'; | ||
3 | import React from 'react'; | ||
4 | |||
5 | import { useRootStore } from '../RootStore'; | ||
6 | |||
7 | export 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 @@ | |||
1 | import { makeAutoObservable } from 'mobx'; | ||
2 | import { | ||
3 | Theme, | ||
4 | createTheme, | ||
5 | responsiveFontSizes, | ||
6 | } from '@mui/material/styles'; | ||
7 | |||
8 | import { | ||
9 | EditorTheme, | ||
10 | EditorThemeData, | ||
11 | DEFAULT_THEME, | ||
12 | EDITOR_THEMES, | ||
13 | } from './EditorTheme'; | ||
14 | |||
15 | export 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 @@ | |||
1 | import { getLogger } from './logger'; | ||
2 | import { PendingTask } from './PendingTask'; | ||
3 | |||
4 | const log = getLogger('utils.ConditionVariable'); | ||
5 | |||
6 | export type Condition = () => boolean; | ||
7 | |||
8 | export 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 @@ | |||
1 | import { getLogger } from './logger'; | ||
2 | |||
3 | const log = getLogger('utils.PendingTask'); | ||
4 | |||
5 | export 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 @@ | |||
1 | export 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 @@ | |||
1 | import styles, { CSPair } from 'ansi-styles'; | ||
2 | import log from 'loglevel'; | ||
3 | import * as prefix from 'loglevel-plugin-prefix'; | ||
4 | |||
5 | const 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 | |||
13 | prefix.reg(log); | ||
14 | |||
15 | if (DEBUG) { | ||
16 | log.setLevel(log.levels.DEBUG); | ||
17 | } else { | ||
18 | log.setLevel(log.levels.WARN); | ||
19 | } | ||
20 | |||
21 | if ('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 | |||
38 | const appLogger = log.getLogger(PACKAGE_NAME); | ||
39 | |||
40 | appLogger.info('Version:', PACKAGE_NAME, PACKAGE_VERSION); | ||
41 | appLogger.info('Debug mode:', DEBUG); | ||
42 | |||
43 | export function getLoggerFromRoot(name: string | symbol): log.Logger { | ||
44 | return log.getLogger(name); | ||
45 | } | ||
46 | |||
47 | export 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 @@ | |||
1 | import type { | ||
2 | Completion, | ||
3 | CompletionContext, | ||
4 | CompletionResult, | ||
5 | } from '@codemirror/autocomplete'; | ||
6 | import { syntaxTree } from '@codemirror/language'; | ||
7 | import type { Transaction } from '@codemirror/state'; | ||
8 | import escapeStringRegexp from 'escape-string-regexp'; | ||
9 | |||
10 | import { implicitCompletion } from '../language/props'; | ||
11 | import type { UpdateService } from './UpdateService'; | ||
12 | import { getLogger } from '../utils/logger'; | ||
13 | import type { ContentAssistEntry } from './xtextServiceResults'; | ||
14 | |||
15 | const PROPOSALS_LIMIT = 1000; | ||
16 | |||
17 | const IDENTIFIER_REGEXP_STR = '[a-zA-Z0-9_]*'; | ||
18 | |||
19 | const HIGH_PRIORITY_KEYWORDS = ['<->', '~>']; | ||
20 | |||
21 | const log = getLogger('xtext.ContentAssistService'); | ||
22 | |||
23 | interface IFoundToken { | ||
24 | from: number; | ||
25 | |||
26 | to: number; | ||
27 | |||
28 | implicitCompletion: boolean; | ||
29 | |||
30 | text: string; | ||
31 | } | ||
32 | |||
33 | function 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 | |||
51 | function shouldCompleteImplicitly(token: IFoundToken | null, context: CompletionContext): boolean { | ||
52 | return token !== null | ||
53 | && token.implicitCompletion | ||
54 | && context.pos - token.from >= 2; | ||
55 | } | ||
56 | |||
57 | function 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 | |||
70 | function 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 | |||
97 | export 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 @@ | |||
1 | import type { EditorStore } from '../editor/EditorStore'; | ||
2 | import type { IHighlightRange } from '../editor/semanticHighlighting'; | ||
3 | import type { UpdateService } from './UpdateService'; | ||
4 | import { highlightingResult } from './xtextServiceResults'; | ||
5 | |||
6 | export 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 @@ | |||
1 | import { Transaction } from '@codemirror/state'; | ||
2 | |||
3 | import type { EditorStore } from '../editor/EditorStore'; | ||
4 | import type { IOccurrence } from '../editor/findOccurrences'; | ||
5 | import type { UpdateService } from './UpdateService'; | ||
6 | import { getLogger } from '../utils/logger'; | ||
7 | import { Timer } from '../utils/Timer'; | ||
8 | import { XtextWebSocketClient } from './XtextWebSocketClient'; | ||
9 | import { | ||
10 | isConflictResult, | ||
11 | occurrencesResult, | ||
12 | TextRegion, | ||
13 | } from './xtextServiceResults'; | ||
14 | |||
15 | const 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. | ||
19 | const CLEAR_OCCURRENCES_TIMEOUT_MS = 10; | ||
20 | |||
21 | const log = getLogger('xtext.OccurrencesService'); | ||
22 | |||
23 | function 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 | |||
36 | export 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 @@ | |||
1 | import { | ||
2 | ChangeDesc, | ||
3 | ChangeSet, | ||
4 | ChangeSpec, | ||
5 | StateEffect, | ||
6 | Transaction, | ||
7 | } from '@codemirror/state'; | ||
8 | import { nanoid } from 'nanoid'; | ||
9 | |||
10 | import type { EditorStore } from '../editor/EditorStore'; | ||
11 | import type { XtextWebSocketClient } from './XtextWebSocketClient'; | ||
12 | import { ConditionVariable } from '../utils/ConditionVariable'; | ||
13 | import { getLogger } from '../utils/logger'; | ||
14 | import { Timer } from '../utils/Timer'; | ||
15 | import { | ||
16 | ContentAssistEntry, | ||
17 | contentAssistResult, | ||
18 | documentStateResult, | ||
19 | formattingResult, | ||
20 | isConflictResult, | ||
21 | } from './xtextServiceResults'; | ||
22 | |||
23 | const UPDATE_TIMEOUT_MS = 500; | ||
24 | |||
25 | const WAIT_FOR_UPDATE_TIMEOUT_MS = 1000; | ||
26 | |||
27 | const log = getLogger('xtext.UpdateService'); | ||
28 | |||
29 | const setDirtyChanges = StateEffect.define<ChangeSet>(); | ||
30 | |||
31 | export interface IAbortSignal { | ||
32 | aborted: boolean; | ||
33 | } | ||
34 | |||
35 | export 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 @@ | |||
1 | import type { Diagnostic } from '@codemirror/lint'; | ||
2 | |||
3 | import type { EditorStore } from '../editor/EditorStore'; | ||
4 | import type { UpdateService } from './UpdateService'; | ||
5 | import { validationResult } from './xtextServiceResults'; | ||
6 | |||
7 | export 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 @@ | |||
1 | import type { | ||
2 | CompletionContext, | ||
3 | CompletionResult, | ||
4 | } from '@codemirror/autocomplete'; | ||
5 | import type { Transaction } from '@codemirror/state'; | ||
6 | |||
7 | import type { EditorStore } from '../editor/EditorStore'; | ||
8 | import { ContentAssistService } from './ContentAssistService'; | ||
9 | import { HighlightingService } from './HighlightingService'; | ||
10 | import { OccurrencesService } from './OccurrencesService'; | ||
11 | import { UpdateService } from './UpdateService'; | ||
12 | import { getLogger } from '../utils/logger'; | ||
13 | import { ValidationService } from './ValidationService'; | ||
14 | import { XtextWebSocketClient } from './XtextWebSocketClient'; | ||
15 | import { XtextWebPushService } from './xtextMessages'; | ||
16 | |||
17 | const log = getLogger('xtext.XtextClient'); | ||
18 | |||
19 | export 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 @@ | |||
1 | import { nanoid } from 'nanoid'; | ||
2 | |||
3 | import { getLogger } from '../utils/logger'; | ||
4 | import { PendingTask } from '../utils/PendingTask'; | ||
5 | import { Timer } from '../utils/Timer'; | ||
6 | import { | ||
7 | xtextWebErrorResponse, | ||
8 | XtextWebRequest, | ||
9 | xtextWebOkResponse, | ||
10 | xtextWebPushMessage, | ||
11 | XtextWebPushService, | ||
12 | } from './xtextMessages'; | ||
13 | import { pongResult } from './xtextServiceResults'; | ||
14 | |||
15 | const XTEXT_SUBPROTOCOL_V1 = 'tools.refinery.language.web.xtext.v1'; | ||
16 | |||
17 | const WEBSOCKET_CLOSE_OK = 1000; | ||
18 | |||
19 | const RECONNECT_DELAY_MS = [200, 1000, 5000, 30_000]; | ||
20 | |||
21 | const MAX_RECONNECT_DELAY_MS = RECONNECT_DELAY_MS[RECONNECT_DELAY_MS.length - 1]; | ||
22 | |||
23 | const BACKGROUND_IDLE_TIMEOUT_MS = 5 * 60 * 1000; | ||
24 | |||
25 | const PING_TIMEOUT_MS = 10 * 1000; | ||
26 | |||
27 | const REQUEST_TIMEOUT_MS = 1000; | ||
28 | |||
29 | const log = getLogger('xtext.XtextWebSocketClient'); | ||
30 | |||
31 | export type ReconnectHandler = () => void; | ||
32 | |||
33 | export type PushHandler = ( | ||
34 | resourceId: string, | ||
35 | stateId: string, | ||
36 | service: XtextWebPushService, | ||
37 | data: unknown, | ||
38 | ) => void; | ||
39 | |||
40 | enum State { | ||
41 | Initial, | ||
42 | Opening, | ||
43 | TabVisible, | ||
44 | TabHiddenIdle, | ||
45 | TabHiddenWaiting, | ||
46 | Error, | ||
47 | TimedOut, | ||
48 | } | ||
49 | |||
50 | export 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 @@ | |||
1 | import { z } from 'zod'; | ||
2 | |||
3 | export const xtextWebRequest = z.object({ | ||
4 | id: z.string().nonempty(), | ||
5 | request: z.unknown(), | ||
6 | }); | ||
7 | |||
8 | export type XtextWebRequest = z.infer<typeof xtextWebRequest>; | ||
9 | |||
10 | export const xtextWebOkResponse = z.object({ | ||
11 | id: z.string().nonempty(), | ||
12 | response: z.unknown(), | ||
13 | }); | ||
14 | |||
15 | export type XtextWebOkResponse = z.infer<typeof xtextWebOkResponse>; | ||
16 | |||
17 | export const xtextWebErrorKind = z.enum(['request', 'server']); | ||
18 | |||
19 | export type XtextWebErrorKind = z.infer<typeof xtextWebErrorKind>; | ||
20 | |||
21 | export const xtextWebErrorResponse = z.object({ | ||
22 | id: z.string().nonempty(), | ||
23 | error: xtextWebErrorKind, | ||
24 | message: z.string(), | ||
25 | }); | ||
26 | |||
27 | export type XtextWebErrorResponse = z.infer<typeof xtextWebErrorResponse>; | ||
28 | |||
29 | export const xtextWebPushService = z.enum(['highlight', 'validate']); | ||
30 | |||
31 | export type XtextWebPushService = z.infer<typeof xtextWebPushService>; | ||
32 | |||
33 | export const xtextWebPushMessage = z.object({ | ||
34 | resource: z.string().nonempty(), | ||
35 | stateId: z.string().nonempty(), | ||
36 | service: xtextWebPushService, | ||
37 | push: z.unknown(), | ||
38 | }); | ||
39 | |||
40 | export 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 @@ | |||
1 | import { z } from 'zod'; | ||
2 | |||
3 | export const pongResult = z.object({ | ||
4 | pong: z.string().nonempty(), | ||
5 | }); | ||
6 | |||
7 | export type PongResult = z.infer<typeof pongResult>; | ||
8 | |||
9 | export const documentStateResult = z.object({ | ||
10 | stateId: z.string().nonempty(), | ||
11 | }); | ||
12 | |||
13 | export type DocumentStateResult = z.infer<typeof documentStateResult>; | ||
14 | |||
15 | export const conflict = z.enum(['invalidStateId', 'canceled']); | ||
16 | |||
17 | export type Conflict = z.infer<typeof conflict>; | ||
18 | |||
19 | export const serviceConflictResult = z.object({ | ||
20 | conflict, | ||
21 | }); | ||
22 | |||
23 | export type ServiceConflictResult = z.infer<typeof serviceConflictResult>; | ||
24 | |||
25 | export function isConflictResult(result: unknown, conflictType: Conflict): boolean { | ||
26 | const parsedConflictResult = serviceConflictResult.safeParse(result); | ||
27 | return parsedConflictResult.success && parsedConflictResult.data.conflict === conflictType; | ||
28 | } | ||
29 | |||
30 | export const severity = z.enum(['error', 'warning', 'info', 'ignore']); | ||
31 | |||
32 | export type Severity = z.infer<typeof severity>; | ||
33 | |||
34 | export 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 | |||
43 | export type Issue = z.infer<typeof issue>; | ||
44 | |||
45 | export const validationResult = z.object({ | ||
46 | issues: issue.array(), | ||
47 | }); | ||
48 | |||
49 | export type ValidationResult = z.infer<typeof validationResult>; | ||
50 | |||
51 | export const replaceRegion = z.object({ | ||
52 | offset: z.number().int().nonnegative(), | ||
53 | length: z.number().int().nonnegative(), | ||
54 | text: z.string(), | ||
55 | }); | ||
56 | |||
57 | export type ReplaceRegion = z.infer<typeof replaceRegion>; | ||
58 | |||
59 | export const textRegion = z.object({ | ||
60 | offset: z.number().int().nonnegative(), | ||
61 | length: z.number().int().nonnegative(), | ||
62 | }); | ||
63 | |||
64 | export type TextRegion = z.infer<typeof textRegion>; | ||
65 | |||
66 | export 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 | |||
78 | export type ContentAssistEntry = z.infer<typeof contentAssistEntry>; | ||
79 | |||
80 | export const contentAssistResult = documentStateResult.extend({ | ||
81 | entries: contentAssistEntry.array(), | ||
82 | }); | ||
83 | |||
84 | export type ContentAssistResult = z.infer<typeof contentAssistResult>; | ||
85 | |||
86 | export const highlightingRegion = z.object({ | ||
87 | offset: z.number().int().nonnegative(), | ||
88 | length: z.number().int().nonnegative(), | ||
89 | styleClasses: z.string().nonempty().array(), | ||
90 | }); | ||
91 | |||
92 | export type HighlightingRegion = z.infer<typeof highlightingRegion>; | ||
93 | |||
94 | export const highlightingResult = z.object({ | ||
95 | regions: highlightingRegion.array(), | ||
96 | }); | ||
97 | |||
98 | export type HighlightingResult = z.infer<typeof highlightingResult>; | ||
99 | |||
100 | export const occurrencesResult = documentStateResult.extend({ | ||
101 | writeRegions: textRegion.array(), | ||
102 | readRegions: textRegion.array(), | ||
103 | }); | ||
104 | |||
105 | export type OccurrencesResult = z.infer<typeof occurrencesResult>; | ||
106 | |||
107 | export const formattingResult = documentStateResult.extend({ | ||
108 | formattedText: z.string(), | ||
109 | replaceRegion: textRegion, | ||
110 | }); | ||
111 | |||
112 | export 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 @@ | |||
1 | package tools.refinery.language.web; | ||
2 | |||
3 | import static org.hamcrest.MatcherAssert.assertThat; | ||
4 | import static org.hamcrest.Matchers.equalTo; | ||
5 | import static org.hamcrest.Matchers.hasSize; | ||
6 | import static org.hamcrest.Matchers.instanceOf; | ||
7 | import static org.hamcrest.Matchers.startsWith; | ||
8 | import static org.junit.jupiter.api.Assertions.assertThrows; | ||
9 | |||
10 | import java.io.IOException; | ||
11 | import java.net.InetSocketAddress; | ||
12 | import java.net.URI; | ||
13 | import java.util.concurrent.CompletableFuture; | ||
14 | import java.util.concurrent.CompletionException; | ||
15 | |||
16 | import org.eclipse.jetty.http.HttpHeader; | ||
17 | import org.eclipse.jetty.http.HttpStatus; | ||
18 | import org.eclipse.jetty.server.Server; | ||
19 | import org.eclipse.jetty.servlet.ServletContextHandler; | ||
20 | import org.eclipse.jetty.servlet.ServletHolder; | ||
21 | import org.eclipse.jetty.websocket.api.Session; | ||
22 | import org.eclipse.jetty.websocket.api.StatusCode; | ||
23 | import org.eclipse.jetty.websocket.api.annotations.WebSocket; | ||
24 | import org.eclipse.jetty.websocket.api.exceptions.UpgradeException; | ||
25 | import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; | ||
26 | import org.eclipse.jetty.websocket.client.WebSocketClient; | ||
27 | import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; | ||
28 | import org.eclipse.xtext.testing.GlobalRegistries; | ||
29 | import org.eclipse.xtext.testing.GlobalRegistries.GlobalStateMemento; | ||
30 | import org.junit.jupiter.api.AfterEach; | ||
31 | import org.junit.jupiter.api.BeforeEach; | ||
32 | import org.junit.jupiter.api.Test; | ||
33 | import org.junit.jupiter.params.ParameterizedTest; | ||
34 | import org.junit.jupiter.params.provider.ValueSource; | ||
35 | |||
36 | import tools.refinery.language.web.tests.WebSocketIntegrationTestClient; | ||
37 | import tools.refinery.language.web.xtext.servlet.XtextStatusCode; | ||
38 | import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet; | ||
39 | |||
40 | class 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 @@ | |||
1 | package tools.refinery.language.web.tests; | ||
2 | |||
3 | import java.util.ArrayList; | ||
4 | import java.util.List; | ||
5 | import java.util.concurrent.ExecutorService; | ||
6 | |||
7 | import org.eclipse.xtext.ide.ExecutorServiceProvider; | ||
8 | |||
9 | import com.google.inject.Singleton; | ||
10 | |||
11 | @Singleton | ||
12 | public 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 @@ | |||
1 | package tools.refinery.language.web.tests; | ||
2 | |||
3 | import org.eclipse.xtext.ide.ExecutorServiceProvider; | ||
4 | import org.eclipse.xtext.util.DisposableRegistry; | ||
5 | import org.eclipse.xtext.util.Modules2; | ||
6 | |||
7 | import com.google.inject.Guice; | ||
8 | import com.google.inject.Injector; | ||
9 | |||
10 | import tools.refinery.language.ide.ProblemIdeModule; | ||
11 | import tools.refinery.language.tests.ProblemInjectorProvider; | ||
12 | import tools.refinery.language.web.ProblemWebModule; | ||
13 | import tools.refinery.language.web.ProblemWebSetup; | ||
14 | |||
15 | public 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 @@ | |||
1 | package tools.refinery.language.web.tests; | ||
2 | |||
3 | import java.util.Collection; | ||
4 | import java.util.List; | ||
5 | import java.util.concurrent.Callable; | ||
6 | import java.util.concurrent.ExecutionException; | ||
7 | import java.util.concurrent.ExecutorService; | ||
8 | import java.util.concurrent.Executors; | ||
9 | import java.util.concurrent.Future; | ||
10 | import java.util.concurrent.TimeUnit; | ||
11 | import java.util.concurrent.TimeoutException; | ||
12 | |||
13 | import org.slf4j.Logger; | ||
14 | import org.slf4j.LoggerFactory; | ||
15 | |||
16 | public 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 @@ | |||
1 | package tools.refinery.language.web.tests; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.fail; | ||
4 | |||
5 | import java.io.IOException; | ||
6 | import java.time.Duration; | ||
7 | import java.util.ArrayList; | ||
8 | import java.util.List; | ||
9 | |||
10 | import org.eclipse.jetty.websocket.api.Session; | ||
11 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; | ||
12 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; | ||
13 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; | ||
14 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; | ||
15 | |||
16 | public 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 @@ | |||
1 | package tools.refinery.language.web.xtext.servlet; | ||
2 | |||
3 | import static org.hamcrest.MatcherAssert.assertThat; | ||
4 | import static org.hamcrest.Matchers.equalTo; | ||
5 | import static org.hamcrest.Matchers.hasProperty; | ||
6 | import static org.hamcrest.Matchers.instanceOf; | ||
7 | import static org.mockito.Mockito.mock; | ||
8 | import static org.mockito.Mockito.times; | ||
9 | import static org.mockito.Mockito.verify; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Map; | ||
13 | |||
14 | import org.eclipse.emf.common.util.URI; | ||
15 | import org.eclipse.xtext.resource.IResourceServiceProvider; | ||
16 | import org.eclipse.xtext.testing.InjectWith; | ||
17 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
18 | import org.eclipse.xtext.web.server.model.DocumentStateResult; | ||
19 | import org.eclipse.xtext.web.server.syntaxcoloring.HighlightingResult; | ||
20 | import org.eclipse.xtext.web.server.validation.ValidationResult; | ||
21 | import org.junit.jupiter.api.BeforeEach; | ||
22 | import org.junit.jupiter.api.Test; | ||
23 | import org.junit.jupiter.api.extension.ExtendWith; | ||
24 | import org.mockito.ArgumentCaptor; | ||
25 | import org.mockito.junit.jupiter.MockitoExtension; | ||
26 | |||
27 | import com.google.inject.Inject; | ||
28 | |||
29 | import tools.refinery.language.web.tests.AwaitTerminationExecutorServiceProvider; | ||
30 | import tools.refinery.language.web.tests.ProblemWebInjectorProvider; | ||
31 | import tools.refinery.language.web.xtext.server.ResponseHandler; | ||
32 | import tools.refinery.language.web.xtext.server.ResponseHandlerException; | ||
33 | import tools.refinery.language.web.xtext.server.TransactionExecutor; | ||
34 | import tools.refinery.language.web.xtext.server.message.XtextWebOkResponse; | ||
35 | import tools.refinery.language.web.xtext.server.message.XtextWebRequest; | ||
36 | import tools.refinery.language.web.xtext.server.message.XtextWebResponse; | ||
37 | |||
38 | @ExtendWith(MockitoExtension.class) | ||
39 | @ExtendWith(InjectionExtension.class) | ||
40 | @InjectWith(ProblemWebInjectorProvider.class) | ||
41 | class 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 @@ | |||
1 | const fs = require('fs'); | ||
2 | const path = require('path'); | ||
3 | |||
4 | const { DefinePlugin } = require('webpack'); | ||
5 | const HtmlWebpackPlugin = require('html-webpack-plugin'); | ||
6 | const HtmlWebpackInjectPreload = require('@principalstudio/html-webpack-inject-preload'); | ||
7 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); | ||
8 | const { SubresourceIntegrityPlugin } = require('webpack-subresource-integrity'); | ||
9 | |||
10 | const packageInfo = require('./package.json'); | ||
11 | |||
12 | const currentNodeEnv = process.env.NODE_ENV || 'development'; | ||
13 | const devMode = currentNodeEnv !== 'production'; | ||
14 | const outputPath = path.resolve(__dirname, 'build/webpack', currentNodeEnv); | ||
15 | |||
16 | const portNumberOrElse = (envName, fallback) => { | ||
17 | const value = process.env[envName]; | ||
18 | return value ? parseInt(value) : fallback; | ||
19 | }; | ||
20 | const listenHost = process.env['LISTEN_HOST'] || 'localhost'; | ||
21 | const listenPort = portNumberOrElse('LISTEN_PORT', 1313); | ||
22 | const apiHost = process.env['API_HOST'] || listenHost; | ||
23 | const apiPort = portNumberOrElse('API_PORT', 1312); | ||
24 | const publicHost = process.env['PUBLIC_HOST'] || listenHost; | ||
25 | const publicPort = portNumberOrElse('PUBLIC_PORT', listenPort); | ||
26 | |||
27 | const resolveSources = sources => path.resolve(__dirname, 'src', sources); | ||
28 | const mainJsSources = resolveSources('main/js'); | ||
29 | const babelLoaderFilters = { | ||
30 | include: [mainJsSources], | ||
31 | }; | ||
32 | const babelPresets = [ | ||
33 | [ | ||
34 | '@babel/preset-env', | ||
35 | { | ||
36 | targets: 'defaults', | ||
37 | }, | ||
38 | ], | ||
39 | '@babel/preset-react', | ||
40 | ]; | ||
41 | const babelPlugins = [ | ||
42 | '@babel/plugin-transform-runtime', | ||
43 | ] | ||
44 | const magicCommentsLoader = { | ||
45 | loader: 'magic-comments-loader', | ||
46 | options: { | ||
47 | webpackChunkName: true, | ||
48 | } | ||
49 | }; | ||
50 | |||
51 | module.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 @@ | |||
1 | plugins { | ||
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 | |||
10 | dependencies { | ||
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 | |||
22 | sourceSets { | ||
23 | testFixtures { | ||
24 | java.srcDirs += ['src/testFixtures/xtext-gen'] | ||
25 | resources.srcDirs += ['src/testFixtures/xtext-gen'] | ||
26 | } | ||
27 | } | ||
28 | |||
29 | tasks.named('jar') { | ||
30 | from(sourceSets.main.allSource) { | ||
31 | include '**/*.xtext' | ||
32 | } | ||
33 | } | ||
34 | |||
35 | def 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 | |||
49 | for (taskName in ['compileJava', 'processResources', 'generateXtext', 'generateEclipseSourceFolders']) { | ||
50 | tasks.named(taskName) { | ||
51 | dependsOn generateXtextLanguage | ||
52 | } | ||
53 | } | ||
54 | |||
55 | tasks.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 | |||
62 | sonarqube.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 @@ | |||
1 | module tools.refinery.language.GenerateProblem | ||
2 | |||
3 | import org.eclipse.xtext.xtext.generator.* | ||
4 | import org.eclipse.xtext.xtext.generator.model.project.* | ||
5 | |||
6 | var rootPath = '..' | ||
7 | |||
8 | Workflow { | ||
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 @@ | |||
1 | grammar tools.refinery.language.Problem with org.eclipse.xtext.common.Terminals | ||
2 | |||
3 | import "http://www.eclipse.org/emf/2002/Ecore" as ecore | ||
4 | import "https://refinery.tools/emf/2021/Problem" | ||
5 | |||
6 | Problem: | ||
7 | ("problem" name=Identifier ".")? | ||
8 | statements+=Statement*; | ||
9 | |||
10 | Statement: | ||
11 | ClassDeclaration | EnumDeclaration | PredicateDefinition | RuleDefinition | Assertion | NodeValueAssertion | | ||
12 | ScopeDeclaration | | ||
13 | IndividualDeclaration; | ||
14 | |||
15 | ClassDeclaration: | ||
16 | abstract?="abstract"? "class" | ||
17 | name=Identifier | ||
18 | ("extends" superTypes+=[Relation|QualifiedName] ("," superTypes+=[Relation|QualifiedName])*)? | ||
19 | ("{" (referenceDeclarations+=ReferenceDeclaration ";"?)* "}" | "."); | ||
20 | |||
21 | EnumDeclaration: | ||
22 | "enum" | ||
23 | name=Identifier | ||
24 | ("{" (literals+=EnumLiteral ("," literals+=EnumLiteral)* ("," | ";")?)? "}" | "."); | ||
25 | |||
26 | EnumLiteral returns Node: | ||
27 | name=Identifier; | ||
28 | |||
29 | ReferenceDeclaration: | ||
30 | (containment?="contains" | "refers")? | ||
31 | referenceType=[Relation|QualifiedName] | ||
32 | ("[" multiplicity=Multiplicity "]")? | ||
33 | name=Identifier | ||
34 | ("opposite" opposite=[ReferenceDeclaration|QualifiedName])?; | ||
35 | |||
36 | enum PredicateKind: | ||
37 | DIRECT="direct"; | ||
38 | |||
39 | PredicateDefinition: | ||
40 | (error?="error" "pred"? | kind=PredicateKind? "pred") | ||
41 | name=Identifier | ||
42 | "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")" | ||
43 | ("<->" bodies+=Conjunction (";" bodies+=Conjunction)*)? | ||
44 | "."; | ||
45 | |||
46 | enum RuleKind: | ||
47 | DIRECT="direct"; | ||
48 | |||
49 | RuleDefinition: | ||
50 | kind=RuleKind "rule" | ||
51 | name=Identifier | ||
52 | "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")" | ||
53 | (":" bodies+=Conjunction (";" bodies+=Conjunction)* | ||
54 | "~>" action=Action)? | ||
55 | "."; | ||
56 | |||
57 | Parameter: | ||
58 | parameterType=[Relation|QualifiedName]? name=Identifier; | ||
59 | |||
60 | Conjunction: | ||
61 | literals+=Literal ("," literals+=Literal)*; | ||
62 | |||
63 | Action: | ||
64 | actionLiterals+=ActionLiteral ("," actionLiterals+=ActionLiteral)*; | ||
65 | |||
66 | Literal: | ||
67 | Atom | ValueLiteral | NegativeLiteral; | ||
68 | |||
69 | ValueLiteral: | ||
70 | atom=Atom | ||
71 | (refinement?=":" | "=") | ||
72 | values+=LogicConstant ("|" values+=LogicConstant)*; | ||
73 | |||
74 | NegativeLiteral: | ||
75 | "!" atom=Atom; | ||
76 | |||
77 | ActionLiteral: | ||
78 | ValueActionLiteral | DeleteActionLiteral | NewActionLiteral; | ||
79 | |||
80 | ValueActionLiteral: | ||
81 | atom=Atom | ||
82 | (refinement?=":" | "=") | ||
83 | value=LogicValue; | ||
84 | |||
85 | DeleteActionLiteral: | ||
86 | "delete" variableOrNode=[VariableOrNode|QualifiedName]; | ||
87 | |||
88 | NewActionLiteral: | ||
89 | "new" variable=NewVariable; | ||
90 | |||
91 | NewVariable: | ||
92 | name=Identifier; | ||
93 | |||
94 | Atom: | ||
95 | relation=[Relation|QualifiedName] | ||
96 | transitiveClosure?="+"? | ||
97 | "(" (arguments+=Argument ("," arguments+=Argument)*)? ")"; | ||
98 | |||
99 | LogicConstant: | ||
100 | value=LogicValue; | ||
101 | |||
102 | Argument: | ||
103 | VariableOrNodeArgument | ConstantArgument; | ||
104 | |||
105 | VariableOrNodeArgument: | ||
106 | variableOrNode=[VariableOrNode|QualifiedName]; | ||
107 | |||
108 | ConstantArgument: | ||
109 | constant=Constant; | ||
110 | |||
111 | Assertion: | ||
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 | |||
121 | AssertionArgument: | ||
122 | NodeAssertionArgument | WildcardAssertionArgument | ConstantAssertionArgument; | ||
123 | |||
124 | NodeAssertionArgument: | ||
125 | node=[Node|QualifiedName]; | ||
126 | |||
127 | WildcardAssertionArgument: | ||
128 | {WildcardAssertionArgument} "*"; | ||
129 | |||
130 | ConstantAssertionArgument: | ||
131 | constant=Constant; | ||
132 | |||
133 | enum LogicValue: | ||
134 | TRUE="true" | FALSE="false" | UNKNOWN="unknown" | ERROR="error"; | ||
135 | |||
136 | enum ShortLogicValue returns LogicValue: | ||
137 | FALSE="!" | UNKNOWN="?"; | ||
138 | |||
139 | NodeValueAssertion: | ||
140 | node=[Node|QualifiedName] ":" value=Constant "."; | ||
141 | |||
142 | Constant: | ||
143 | RealConstant | IntConstant | StringConstant; | ||
144 | |||
145 | IntConstant: | ||
146 | intValue=Integer; | ||
147 | |||
148 | RealConstant: | ||
149 | realValue=Real; | ||
150 | |||
151 | StringConstant: | ||
152 | stringValue=STRING; | ||
153 | |||
154 | ScopeDeclaration: | ||
155 | "scope" typeScopes+=TypeScope ("," typeScopes+=TypeScope)* "."; | ||
156 | |||
157 | TypeScope: | ||
158 | targetType=[ClassDeclaration|QualifiedName] | ||
159 | (increment?="+=" | "=") | ||
160 | multiplicity=DefiniteMultiplicity; | ||
161 | |||
162 | Multiplicity: | ||
163 | UnboundedMultiplicity | DefiniteMultiplicity; | ||
164 | |||
165 | DefiniteMultiplicity returns Multiplicity: | ||
166 | RangeMultiplicity | ExactMultiplicity; | ||
167 | |||
168 | UnboundedMultiplicity: | ||
169 | {UnboundedMultiplicity}; | ||
170 | |||
171 | RangeMultiplicity: | ||
172 | lowerBound=INT ".." upperBound=UpperBound; | ||
173 | |||
174 | ExactMultiplicity: | ||
175 | exactValue=INT; | ||
176 | |||
177 | IndividualDeclaration: | ||
178 | "indiv" nodes+=EnumLiteral ("," nodes+=EnumLiteral)* "."; | ||
179 | |||
180 | UpperBound returns ecore::EInt: | ||
181 | INT | "*"; | ||
182 | |||
183 | QualifiedName hidden(): | ||
184 | Identifier ("::" Identifier)*; | ||
185 | |||
186 | Identifier: | ||
187 | ID | "true" | "false" | "unknown" | "error" | "class" | "abstract" | "extends" | "enum" | "pred" | | ||
188 | "indiv" | "problem" | "new" | "delete" | "direct" | "rule"; | ||
189 | |||
190 | Integer returns ecore::EInt hidden(): | ||
191 | "-"? INT; | ||
192 | |||
193 | Real returns ecore::EDouble: | ||
194 | "-"? (EXPONENTIAL | INT "." (INT | EXPONENTIAL)); | ||
195 | |||
196 | @Override | ||
197 | terminal ID: | ||
198 | ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*; | ||
199 | |||
200 | terminal EXPONENTIAL: | ||
201 | INT ("e" | "E") ("+" | "-")? INT; | ||
202 | |||
203 | @Override | ||
204 | terminal 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 | */ | ||
4 | package tools.refinery.language; | ||
5 | |||
6 | import org.eclipse.xtext.conversion.IValueConverterService; | ||
7 | import org.eclipse.xtext.naming.IQualifiedNameConverter; | ||
8 | import org.eclipse.xtext.resource.DerivedStateAwareResource; | ||
9 | import org.eclipse.xtext.resource.DerivedStateAwareResourceDescriptionManager; | ||
10 | import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy; | ||
11 | import org.eclipse.xtext.resource.IDerivedStateComputer; | ||
12 | import org.eclipse.xtext.resource.ILocationInFileProvider; | ||
13 | import org.eclipse.xtext.resource.IResourceDescription; | ||
14 | import org.eclipse.xtext.resource.XtextResource; | ||
15 | import org.eclipse.xtext.scoping.IGlobalScopeProvider; | ||
16 | import org.eclipse.xtext.scoping.IScopeProvider; | ||
17 | import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; | ||
18 | import org.eclipse.xtext.validation.IResourceValidator; | ||
19 | import org.eclipse.xtext.xbase.annotations.validation.DerivedStateAwareResourceValidator; | ||
20 | |||
21 | import com.google.inject.Binder; | ||
22 | import com.google.inject.name.Names; | ||
23 | |||
24 | import tools.refinery.language.conversion.ProblemValueConverterService; | ||
25 | import tools.refinery.language.naming.ProblemQualifiedNameConverter; | ||
26 | import tools.refinery.language.resource.ProblemDerivedStateComputer; | ||
27 | import tools.refinery.language.resource.ProblemLocationInFileProvider; | ||
28 | import tools.refinery.language.resource.ProblemResourceDescriptionStrategy; | ||
29 | import tools.refinery.language.scoping.ProblemGlobalScopeProvider; | ||
30 | import 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 | */ | ||
36 | public 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 | */ | ||
4 | package tools.refinery.language; | ||
5 | |||
6 | import org.eclipse.emf.ecore.resource.Resource; | ||
7 | import org.eclipse.xtext.resource.IResourceFactory; | ||
8 | import org.eclipse.xtext.resource.IResourceServiceProvider; | ||
9 | |||
10 | import com.google.inject.Guice; | ||
11 | import com.google.inject.Injector; | ||
12 | |||
13 | import tools.refinery.language.model.ProblemEMFSetup; | ||
14 | |||
15 | /** | ||
16 | * Initialization support for running Xtext languages without Equinox extension | ||
17 | * registry. | ||
18 | */ | ||
19 | public 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 @@ | |||
1 | package tools.refinery.language; | ||
2 | |||
3 | import org.eclipse.xtext.naming.IQualifiedNameConverter; | ||
4 | import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy; | ||
5 | import org.eclipse.xtext.resource.IResourceFactory; | ||
6 | import org.eclipse.xtext.resource.generic.AbstractGenericResourceRuntimeModule; | ||
7 | |||
8 | import tools.refinery.language.model.ProblemEMFSetup; | ||
9 | import tools.refinery.language.naming.ProblemQualifiedNameConverter; | ||
10 | import tools.refinery.language.resource.ProblemResourceDescriptionStrategy; | ||
11 | import tools.refinery.language.resource.ProblemXmiResourceFactory; | ||
12 | |||
13 | public 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 @@ | |||
1 | package tools.refinery.language.conversion; | ||
2 | |||
3 | import org.eclipse.xtext.common.services.DefaultTerminalConverters; | ||
4 | import org.eclipse.xtext.conversion.IValueConverter; | ||
5 | import org.eclipse.xtext.conversion.ValueConverter; | ||
6 | |||
7 | import com.google.inject.Inject; | ||
8 | |||
9 | public 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 @@ | |||
1 | package tools.refinery.language.conversion; | ||
2 | |||
3 | import org.eclipse.xtext.conversion.ValueConverterException; | ||
4 | import org.eclipse.xtext.conversion.impl.AbstractValueConverter; | ||
5 | import org.eclipse.xtext.conversion.impl.INTValueConverter; | ||
6 | import org.eclipse.xtext.nodemodel.INode; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import com.google.inject.Singleton; | ||
10 | |||
11 | @Singleton | ||
12 | public 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 | */ | ||
4 | package tools.refinery.language.formatting2; | ||
5 | |||
6 | import org.eclipse.emf.ecore.EObject; | ||
7 | import org.eclipse.xtext.formatting2.AbstractJavaFormatter; | ||
8 | import org.eclipse.xtext.formatting2.IFormattableDocument; | ||
9 | import org.eclipse.xtext.formatting2.IHiddenRegionFormatter; | ||
10 | import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionsFinder; | ||
11 | import org.eclipse.xtext.formatting2.regionaccess.ISequentialRegion; | ||
12 | import org.eclipse.xtext.xbase.lib.Procedures.Procedure1; | ||
13 | |||
14 | import tools.refinery.language.model.problem.Assertion; | ||
15 | import tools.refinery.language.model.problem.Atom; | ||
16 | import tools.refinery.language.model.problem.ClassDeclaration; | ||
17 | import tools.refinery.language.model.problem.Conjunction; | ||
18 | import tools.refinery.language.model.problem.IndividualDeclaration; | ||
19 | import tools.refinery.language.model.problem.NegativeLiteral; | ||
20 | import tools.refinery.language.model.problem.Parameter; | ||
21 | import tools.refinery.language.model.problem.PredicateDefinition; | ||
22 | import tools.refinery.language.model.problem.Problem; | ||
23 | import tools.refinery.language.model.problem.ProblemPackage; | ||
24 | |||
25 | public 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 @@ | |||
1 | package tools.refinery.language.naming; | ||
2 | |||
3 | import java.util.regex.Pattern; | ||
4 | |||
5 | public 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 @@ | |||
1 | package tools.refinery.language.naming; | ||
2 | |||
3 | import org.eclipse.xtext.naming.IQualifiedNameConverter; | ||
4 | |||
5 | import com.google.inject.Singleton; | ||
6 | |||
7 | @Singleton | ||
8 | public 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 @@ | |||
1 | package tools.refinery.language.resource; | ||
2 | |||
3 | import java.util.HashSet; | ||
4 | import java.util.List; | ||
5 | import java.util.Set; | ||
6 | |||
7 | import org.eclipse.xtext.linking.impl.LinkingHelper; | ||
8 | import org.eclipse.xtext.naming.IQualifiedNameConverter; | ||
9 | import org.eclipse.xtext.nodemodel.INode; | ||
10 | import org.eclipse.xtext.nodemodel.util.NodeModelUtils; | ||
11 | import org.eclipse.xtext.scoping.IScope; | ||
12 | import org.eclipse.xtext.scoping.IScopeProvider; | ||
13 | import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; | ||
14 | |||
15 | import com.google.inject.Inject; | ||
16 | import com.google.inject.Singleton; | ||
17 | import com.google.inject.name.Named; | ||
18 | |||
19 | import tools.refinery.language.model.problem.Argument; | ||
20 | import tools.refinery.language.model.problem.Atom; | ||
21 | import tools.refinery.language.model.problem.Conjunction; | ||
22 | import tools.refinery.language.model.problem.ExistentialQuantifier; | ||
23 | import tools.refinery.language.model.problem.ImplicitVariable; | ||
24 | import tools.refinery.language.model.problem.Literal; | ||
25 | import tools.refinery.language.model.problem.NegativeLiteral; | ||
26 | import tools.refinery.language.model.problem.Parameter; | ||
27 | import tools.refinery.language.model.problem.ParametricDefinition; | ||
28 | import tools.refinery.language.model.problem.Problem; | ||
29 | import tools.refinery.language.model.problem.ProblemFactory; | ||
30 | import tools.refinery.language.model.problem.ProblemPackage; | ||
31 | import tools.refinery.language.model.problem.Statement; | ||
32 | import tools.refinery.language.model.problem.ValueLiteral; | ||
33 | import tools.refinery.language.model.problem.VariableOrNodeArgument; | ||
34 | import tools.refinery.language.naming.NamingUtil; | ||
35 | |||
36 | @Singleton | ||
37 | public 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 @@ | |||
1 | package tools.refinery.language.resource; | ||
2 | |||
3 | import java.util.List; | ||
4 | import java.util.Set; | ||
5 | |||
6 | import org.eclipse.emf.ecore.EObject; | ||
7 | import org.eclipse.emf.ecore.EStructuralFeature; | ||
8 | import org.eclipse.xtext.linking.impl.LinkingHelper; | ||
9 | import org.eclipse.xtext.naming.IQualifiedNameConverter; | ||
10 | import org.eclipse.xtext.nodemodel.INode; | ||
11 | import org.eclipse.xtext.nodemodel.util.NodeModelUtils; | ||
12 | import org.eclipse.xtext.scoping.IScope; | ||
13 | import org.eclipse.xtext.scoping.IScopeProvider; | ||
14 | import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; | ||
15 | |||
16 | import com.google.common.collect.ImmutableSet; | ||
17 | import com.google.inject.Inject; | ||
18 | import com.google.inject.name.Named; | ||
19 | |||
20 | import tools.refinery.language.model.problem.Assertion; | ||
21 | import tools.refinery.language.model.problem.AssertionArgument; | ||
22 | import tools.refinery.language.model.problem.NodeAssertionArgument; | ||
23 | import tools.refinery.language.model.problem.NodeValueAssertion; | ||
24 | import tools.refinery.language.model.problem.Problem; | ||
25 | import tools.refinery.language.model.problem.ProblemPackage; | ||
26 | import tools.refinery.language.model.problem.Statement; | ||
27 | import tools.refinery.language.naming.NamingUtil; | ||
28 | |||
29 | public 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 @@ | |||
1 | package tools.refinery.language.resource; | ||
2 | |||
3 | import java.util.Collection; | ||
4 | import java.util.HashMap; | ||
5 | import java.util.HashSet; | ||
6 | import java.util.List; | ||
7 | import java.util.Map; | ||
8 | import java.util.Set; | ||
9 | import java.util.function.Function; | ||
10 | |||
11 | import org.eclipse.emf.common.notify.impl.AdapterImpl; | ||
12 | import org.eclipse.emf.ecore.EObject; | ||
13 | import org.eclipse.emf.ecore.resource.Resource; | ||
14 | import org.eclipse.emf.ecore.util.EcoreUtil; | ||
15 | import org.eclipse.xtext.Constants; | ||
16 | import org.eclipse.xtext.resource.DerivedStateAwareResource; | ||
17 | import org.eclipse.xtext.resource.IDerivedStateComputer; | ||
18 | import org.eclipse.xtext.resource.XtextResource; | ||
19 | import org.eclipse.xtext.scoping.IScopeProvider; | ||
20 | import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; | ||
21 | |||
22 | import com.google.inject.Inject; | ||
23 | import com.google.inject.Provider; | ||
24 | import com.google.inject.Singleton; | ||
25 | import com.google.inject.name.Named; | ||
26 | |||
27 | import tools.refinery.language.model.problem.ClassDeclaration; | ||
28 | import tools.refinery.language.model.problem.Node; | ||
29 | import tools.refinery.language.model.problem.Problem; | ||
30 | import tools.refinery.language.model.problem.ProblemFactory; | ||
31 | import tools.refinery.language.model.problem.Statement; | ||
32 | |||
33 | @Singleton | ||
34 | public 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 @@ | |||
1 | package tools.refinery.language.resource; | ||
2 | |||
3 | import org.eclipse.emf.ecore.EObject; | ||
4 | import org.eclipse.xtext.resource.DefaultLocationInFileProvider; | ||
5 | import org.eclipse.xtext.util.ITextRegion; | ||
6 | |||
7 | import tools.refinery.language.model.ProblemUtil; | ||
8 | import tools.refinery.language.model.problem.ImplicitVariable; | ||
9 | import tools.refinery.language.model.problem.Node; | ||
10 | |||
11 | public 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 @@ | |||
1 | package tools.refinery.language.resource; | ||
2 | |||
3 | import org.eclipse.emf.ecore.EObject; | ||
4 | import org.eclipse.xtext.EcoreUtil2; | ||
5 | import org.eclipse.xtext.naming.IQualifiedNameConverter; | ||
6 | import org.eclipse.xtext.naming.QualifiedName; | ||
7 | import org.eclipse.xtext.resource.EObjectDescription; | ||
8 | import org.eclipse.xtext.resource.IEObjectDescription; | ||
9 | import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy; | ||
10 | import org.eclipse.xtext.util.IAcceptor; | ||
11 | |||
12 | import com.google.inject.Inject; | ||
13 | import com.google.inject.Singleton; | ||
14 | |||
15 | import tools.refinery.language.model.ProblemUtil; | ||
16 | import tools.refinery.language.model.problem.NamedElement; | ||
17 | import tools.refinery.language.model.problem.Node; | ||
18 | import tools.refinery.language.model.problem.Problem; | ||
19 | import tools.refinery.language.model.problem.Variable; | ||
20 | import tools.refinery.language.naming.NamingUtil; | ||
21 | |||
22 | @Singleton | ||
23 | public 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 @@ | |||
1 | package tools.refinery.language.resource; | ||
2 | |||
3 | import org.eclipse.emf.common.util.URI; | ||
4 | import org.eclipse.emf.ecore.resource.Resource; | ||
5 | import org.eclipse.xtext.resource.IResourceFactory; | ||
6 | |||
7 | import tools.refinery.language.model.problem.util.ProblemResourceFactoryImpl; | ||
8 | |||
9 | public 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 @@ | |||
1 | package tools.refinery.language.resource; | ||
2 | |||
3 | import java.util.HashMap; | ||
4 | import java.util.Map; | ||
5 | |||
6 | import org.eclipse.emf.ecore.EObject; | ||
7 | import org.eclipse.xtext.util.IResourceScopeCache; | ||
8 | |||
9 | import com.google.inject.Inject; | ||
10 | import com.google.inject.Singleton; | ||
11 | |||
12 | import tools.refinery.language.model.problem.Problem; | ||
13 | |||
14 | @Singleton | ||
15 | public 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 @@ | |||
1 | package tools.refinery.language.scoping; | ||
2 | |||
3 | import java.util.LinkedHashSet; | ||
4 | |||
5 | import org.eclipse.emf.common.util.URI; | ||
6 | import org.eclipse.emf.ecore.resource.Resource; | ||
7 | import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider; | ||
8 | |||
9 | import tools.refinery.language.model.ProblemUtil; | ||
10 | |||
11 | public 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 @@ | |||
1 | package tools.refinery.language.scoping; | ||
2 | |||
3 | import java.util.List; | ||
4 | |||
5 | import org.eclipse.emf.ecore.EObject; | ||
6 | import org.eclipse.emf.ecore.resource.Resource; | ||
7 | import org.eclipse.xtext.naming.QualifiedName; | ||
8 | import org.eclipse.xtext.resource.IResourceDescriptions; | ||
9 | import org.eclipse.xtext.resource.IResourceDescriptionsProvider; | ||
10 | import org.eclipse.xtext.resource.ISelectable; | ||
11 | import org.eclipse.xtext.scoping.impl.ImportNormalizer; | ||
12 | import org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider; | ||
13 | |||
14 | import com.google.inject.Inject; | ||
15 | |||
16 | import tools.refinery.language.model.ProblemUtil; | ||
17 | |||
18 | public 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 | */ | ||
4 | package tools.refinery.language.scoping; | ||
5 | |||
6 | import java.util.ArrayList; | ||
7 | import java.util.List; | ||
8 | |||
9 | import org.eclipse.emf.ecore.EObject; | ||
10 | import org.eclipse.emf.ecore.EReference; | ||
11 | import org.eclipse.xtext.EcoreUtil2; | ||
12 | import org.eclipse.xtext.scoping.IScope; | ||
13 | import org.eclipse.xtext.scoping.Scopes; | ||
14 | |||
15 | import tools.refinery.language.model.ProblemUtil; | ||
16 | import tools.refinery.language.model.problem.ClassDeclaration; | ||
17 | import tools.refinery.language.model.problem.ExistentialQuantifier; | ||
18 | import tools.refinery.language.model.problem.NewActionLiteral; | ||
19 | import tools.refinery.language.model.problem.ParametricDefinition; | ||
20 | import tools.refinery.language.model.problem.Action; | ||
21 | import tools.refinery.language.model.problem.Problem; | ||
22 | import tools.refinery.language.model.problem.ProblemPackage; | ||
23 | import tools.refinery.language.model.problem.ReferenceDeclaration; | ||
24 | import tools.refinery.language.model.problem.Variable; | ||
25 | import 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 | */ | ||
34 | public 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 | */ | ||
4 | package tools.refinery.language.validation; | ||
5 | |||
6 | import org.eclipse.xtext.EcoreUtil2; | ||
7 | import org.eclipse.xtext.validation.Check; | ||
8 | |||
9 | import com.google.inject.Inject; | ||
10 | |||
11 | import tools.refinery.language.model.ProblemUtil; | ||
12 | import tools.refinery.language.model.problem.Node; | ||
13 | import tools.refinery.language.model.problem.Problem; | ||
14 | import tools.refinery.language.model.problem.ProblemPackage; | ||
15 | import tools.refinery.language.model.problem.Variable; | ||
16 | import tools.refinery.language.model.problem.VariableOrNodeArgument; | ||
17 | import 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 | */ | ||
25 | public 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 | */ | ||
4 | package tools.refinery.language.tests | ||
5 | |||
6 | import com.google.inject.Inject | ||
7 | import org.eclipse.xtext.testing.InjectWith | ||
8 | import org.eclipse.xtext.testing.extensions.InjectionExtension | ||
9 | import org.eclipse.xtext.testing.util.ParseHelper | ||
10 | import org.junit.jupiter.api.Test | ||
11 | import org.junit.jupiter.api.^extension.ExtendWith | ||
12 | import tools.refinery.language.model.problem.Problem | ||
13 | import tools.refinery.language.model.tests.ProblemTestUtil | ||
14 | |||
15 | import static org.hamcrest.MatcherAssert.assertThat | ||
16 | import static org.hamcrest.Matchers.* | ||
17 | |||
18 | @ExtendWith(InjectionExtension) | ||
19 | @InjectWith(ProblemInjectorProvider) | ||
20 | class 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 @@ | |||
1 | package tools.refinery.language.tests.formatting2; | ||
2 | |||
3 | import static org.hamcrest.MatcherAssert.assertThat; | ||
4 | import static org.hamcrest.Matchers.equalTo; | ||
5 | |||
6 | import java.util.List; | ||
7 | |||
8 | import org.eclipse.xtext.formatting2.FormatterRequest; | ||
9 | import org.eclipse.xtext.formatting2.IFormatter2; | ||
10 | import org.eclipse.xtext.formatting2.regionaccess.ITextRegionAccess; | ||
11 | import org.eclipse.xtext.formatting2.regionaccess.ITextReplacement; | ||
12 | import org.eclipse.xtext.formatting2.regionaccess.TextRegionAccessBuilder; | ||
13 | import org.eclipse.xtext.resource.XtextResource; | ||
14 | import org.eclipse.xtext.testing.InjectWith; | ||
15 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
16 | import org.eclipse.xtext.testing.util.ParseHelper; | ||
17 | import org.junit.jupiter.api.Test; | ||
18 | import org.junit.jupiter.api.extension.ExtendWith; | ||
19 | |||
20 | import com.google.inject.Inject; | ||
21 | import com.google.inject.Provider; | ||
22 | |||
23 | import tools.refinery.language.model.problem.Problem; | ||
24 | import tools.refinery.language.tests.ProblemInjectorProvider; | ||
25 | |||
26 | @ExtendWith(InjectionExtension.class) | ||
27 | @InjectWith(ProblemInjectorProvider.class) | ||
28 | class 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 @@ | |||
1 | package tools.refinery.language.tests.rules | ||
2 | |||
3 | import com.google.inject.Inject | ||
4 | import org.eclipse.xtext.testing.InjectWith | ||
5 | import org.eclipse.xtext.testing.extensions.InjectionExtension | ||
6 | import org.eclipse.xtext.testing.util.ParseHelper | ||
7 | import org.junit.jupiter.api.Test | ||
8 | import org.junit.jupiter.api.^extension.ExtendWith | ||
9 | import tools.refinery.language.model.problem.Problem | ||
10 | import tools.refinery.language.tests.ProblemInjectorProvider | ||
11 | import tools.refinery.language.model.tests.ProblemTestUtil | ||
12 | |||
13 | import static org.hamcrest.MatcherAssert.assertThat | ||
14 | import static org.hamcrest.Matchers.* | ||
15 | |||
16 | @ExtendWith(InjectionExtension) | ||
17 | @InjectWith(ProblemInjectorProvider) | ||
18 | class 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 @@ | |||
1 | package tools.refinery.language.tests.scoping | ||
2 | |||
3 | import com.google.inject.Inject | ||
4 | import java.util.stream.Stream | ||
5 | import org.eclipse.xtext.testing.InjectWith | ||
6 | import org.eclipse.xtext.testing.extensions.InjectionExtension | ||
7 | import org.eclipse.xtext.testing.util.ParseHelper | ||
8 | import org.junit.jupiter.api.Test | ||
9 | import org.junit.jupiter.api.^extension.ExtendWith | ||
10 | import org.junit.jupiter.params.ParameterizedTest | ||
11 | import org.junit.jupiter.params.provider.Arguments | ||
12 | import org.junit.jupiter.params.provider.MethodSource | ||
13 | import org.junit.jupiter.params.provider.ValueSource | ||
14 | import tools.refinery.language.model.problem.Problem | ||
15 | import tools.refinery.language.model.tests.ProblemTestUtil | ||
16 | import tools.refinery.language.tests.ProblemInjectorProvider | ||
17 | |||
18 | import static org.hamcrest.MatcherAssert.assertThat | ||
19 | import static org.hamcrest.Matchers.* | ||
20 | |||
21 | @ExtendWith(InjectionExtension) | ||
22 | @InjectWith(ProblemInjectorProvider) | ||
23 | class 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 @@ | |||
1 | package tools.refinery.language.tests.serializer; | ||
2 | |||
3 | import static org.hamcrest.MatcherAssert.assertThat; | ||
4 | import static org.hamcrest.Matchers.equalTo; | ||
5 | |||
6 | import java.io.ByteArrayOutputStream; | ||
7 | import java.io.IOException; | ||
8 | import java.util.Map; | ||
9 | import java.util.stream.Stream; | ||
10 | |||
11 | import org.eclipse.emf.common.util.URI; | ||
12 | import org.eclipse.emf.ecore.resource.Resource; | ||
13 | import org.eclipse.emf.ecore.resource.ResourceSet; | ||
14 | import org.eclipse.xtext.testing.InjectWith; | ||
15 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
16 | import org.junit.jupiter.api.BeforeEach; | ||
17 | import org.junit.jupiter.api.Test; | ||
18 | import org.junit.jupiter.api.extension.ExtendWith; | ||
19 | import org.junit.jupiter.params.ParameterizedTest; | ||
20 | import org.junit.jupiter.params.provider.Arguments; | ||
21 | import org.junit.jupiter.params.provider.MethodSource; | ||
22 | |||
23 | import com.google.inject.Inject; | ||
24 | |||
25 | import tools.refinery.language.model.ProblemUtil; | ||
26 | import tools.refinery.language.model.problem.Atom; | ||
27 | import tools.refinery.language.model.problem.LogicValue; | ||
28 | import tools.refinery.language.model.problem.Node; | ||
29 | import tools.refinery.language.model.problem.PredicateDefinition; | ||
30 | import tools.refinery.language.model.problem.Problem; | ||
31 | import tools.refinery.language.model.problem.ProblemFactory; | ||
32 | import tools.refinery.language.model.problem.Relation; | ||
33 | import tools.refinery.language.model.problem.VariableOrNode; | ||
34 | import tools.refinery.language.model.tests.ProblemTestUtil; | ||
35 | import tools.refinery.language.tests.ProblemInjectorProvider; | ||
36 | |||
37 | @ExtendWith(InjectionExtension.class) | ||
38 | @InjectWith(ProblemInjectorProvider.class) | ||
39 | class 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 @@ | |||
1 | plugins { | ||
2 | id 'refinery-java-library' | ||
3 | id 'refinery-jmh' | ||
4 | } | ||
5 | |||
6 | dependencies { | ||
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 @@ | |||
1 | package tools.refinery.store.map.benchmarks; | ||
2 | |||
3 | import java.util.ArrayList; | ||
4 | import java.util.HashMap; | ||
5 | import java.util.concurrent.TimeUnit; | ||
6 | |||
7 | import org.openjdk.jmh.annotations.Benchmark; | ||
8 | import org.openjdk.jmh.annotations.BenchmarkMode; | ||
9 | import org.openjdk.jmh.annotations.Fork; | ||
10 | import org.openjdk.jmh.annotations.Measurement; | ||
11 | import org.openjdk.jmh.annotations.Mode; | ||
12 | import org.openjdk.jmh.annotations.OutputTimeUnit; | ||
13 | import org.openjdk.jmh.annotations.Warmup; | ||
14 | import 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) | ||
21 | public 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 @@ | |||
1 | package tools.refinery.store.map.benchmarks; | ||
2 | |||
3 | import java.util.Random; | ||
4 | |||
5 | import tools.refinery.store.map.ContinousHashProvider; | ||
6 | import tools.refinery.store.map.VersionedMapStore; | ||
7 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
8 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
9 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
10 | |||
11 | import org.openjdk.jmh.annotations.Level; | ||
12 | import org.openjdk.jmh.annotations.Param; | ||
13 | import org.openjdk.jmh.annotations.Scope; | ||
14 | import org.openjdk.jmh.annotations.Setup; | ||
15 | import org.openjdk.jmh.annotations.State; | ||
16 | |||
17 | @State(Scope.Benchmark) | ||
18 | public 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 @@ | |||
1 | package tools.refinery.store.map; | ||
2 | |||
3 | import 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 | */ | ||
13 | public 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 @@ | |||
1 | package tools.refinery.store.map; | ||
2 | |||
3 | import java.util.List; | ||
4 | |||
5 | public 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 @@ | |||
1 | package tools.refinery.store.map; | ||
2 | |||
3 | import java.util.Iterator; | ||
4 | import java.util.NoSuchElementException; | ||
5 | import java.util.function.BiFunction; | ||
6 | import java.util.function.BiPredicate; | ||
7 | |||
8 | public 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 @@ | |||
1 | package tools.refinery.store.map; | ||
2 | |||
3 | public 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 @@ | |||
1 | package tools.refinery.store.map; | ||
2 | |||
3 | import java.util.Iterator; | ||
4 | import java.util.function.BiFunction; | ||
5 | import java.util.function.BiPredicate; | ||
6 | |||
7 | public 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 @@ | |||
1 | package tools.refinery.store.map; | ||
2 | |||
3 | public 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 @@ | |||
1 | package tools.refinery.store.map; | ||
2 | |||
3 | public 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 @@ | |||
1 | package tools.refinery.store.map; | ||
2 | |||
3 | import java.util.Set; | ||
4 | |||
5 | public 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 @@ | |||
1 | package tools.refinery.store.map; | ||
2 | |||
3 | public 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 @@ | |||
1 | package tools.refinery.store.map; | ||
2 | |||
3 | import java.util.ArrayList; | ||
4 | import java.util.Arrays; | ||
5 | import java.util.Collections; | ||
6 | import java.util.HashMap; | ||
7 | import java.util.HashSet; | ||
8 | import java.util.List; | ||
9 | import java.util.Map; | ||
10 | import java.util.Set; | ||
11 | |||
12 | import tools.refinery.store.map.internal.ImmutableNode; | ||
13 | import tools.refinery.store.map.internal.MapDiffCursor; | ||
14 | import tools.refinery.store.map.internal.Node; | ||
15 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
16 | |||
17 | public 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 @@ | |||
1 | package tools.refinery.store.map.internal; | ||
2 | |||
3 | enum 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 @@ | |||
1 | package tools.refinery.store.map.internal; | ||
2 | |||
3 | import java.util.Arrays; | ||
4 | import java.util.Map; | ||
5 | |||
6 | import tools.refinery.store.map.ContinousHashProvider; | ||
7 | |||
8 | public 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 @@ | |||
1 | package tools.refinery.store.map.internal; | ||
2 | |||
3 | import java.util.ArrayDeque; | ||
4 | import java.util.ConcurrentModificationException; | ||
5 | import java.util.Iterator; | ||
6 | import java.util.List; | ||
7 | |||
8 | import tools.refinery.store.map.Cursor; | ||
9 | import tools.refinery.store.map.VersionedMap; | ||
10 | |||
11 | public 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 @@ | |||
1 | package tools.refinery.store.map.internal; | ||
2 | |||
3 | import java.util.List; | ||
4 | import java.util.stream.Stream; | ||
5 | |||
6 | import tools.refinery.store.map.ContinousHashProvider; | ||
7 | import tools.refinery.store.map.Cursor; | ||
8 | import tools.refinery.store.map.DiffCursor; | ||
9 | import 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 | */ | ||
17 | public 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 @@ | |||
1 | package tools.refinery.store.map.internal; | ||
2 | |||
3 | import java.util.Arrays; | ||
4 | import java.util.Map; | ||
5 | |||
6 | import tools.refinery.store.map.ContinousHashProvider; | ||
7 | |||
8 | public 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 @@ | |||
1 | package tools.refinery.store.map.internal; | ||
2 | |||
3 | import java.util.Map; | ||
4 | |||
5 | import tools.refinery.store.map.ContinousHashProvider; | ||
6 | |||
7 | public 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 @@ | |||
1 | package tools.refinery.store.map.internal; | ||
2 | |||
3 | public 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 @@ | |||
1 | package tools.refinery.store.map.internal; | ||
2 | |||
3 | import java.util.Iterator; | ||
4 | import java.util.LinkedList; | ||
5 | import java.util.List; | ||
6 | |||
7 | import tools.refinery.store.map.ContinousHashProvider; | ||
8 | import tools.refinery.store.map.Cursor; | ||
9 | import tools.refinery.store.map.DiffCursor; | ||
10 | import tools.refinery.store.map.VersionedMap; | ||
11 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
12 | |||
13 | /** | ||
14 | * Not threadSafe in itself | ||
15 | * @author Oszkar Semerath | ||
16 | * | ||
17 | * @param <K> | ||
18 | * @param <V> | ||
19 | */ | ||
20 | public 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 @@ | |||
1 | package tools.refinery.store.model; | ||
2 | |||
3 | import java.util.Set; | ||
4 | |||
5 | import tools.refinery.store.map.Cursor; | ||
6 | import tools.refinery.store.map.Versioned; | ||
7 | import tools.refinery.store.model.representation.DataRepresentation; | ||
8 | |||
9 | public 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 @@ | |||
1 | package tools.refinery.store.model; | ||
2 | |||
3 | import java.util.Map; | ||
4 | |||
5 | import tools.refinery.store.map.Cursor; | ||
6 | import tools.refinery.store.model.representation.DataRepresentation; | ||
7 | |||
8 | public 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 @@ | |||
1 | package tools.refinery.store.model; | ||
2 | |||
3 | import java.util.Map; | ||
4 | |||
5 | import tools.refinery.store.map.Cursor; | ||
6 | import tools.refinery.store.map.DiffCursor; | ||
7 | import tools.refinery.store.model.representation.DataRepresentation; | ||
8 | |||
9 | public 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 @@ | |||
1 | package tools.refinery.store.model; | ||
2 | |||
3 | import java.util.Set; | ||
4 | |||
5 | import tools.refinery.store.model.representation.DataRepresentation; | ||
6 | |||
7 | public 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 @@ | |||
1 | package tools.refinery.store.model; | ||
2 | |||
3 | import java.util.HashMap; | ||
4 | import java.util.LinkedList; | ||
5 | import java.util.List; | ||
6 | import java.util.Map; | ||
7 | import java.util.Map.Entry; | ||
8 | |||
9 | import tools.refinery.store.map.ContinousHashProvider; | ||
10 | import tools.refinery.store.map.DiffCursor; | ||
11 | import tools.refinery.store.map.VersionedMap; | ||
12 | import tools.refinery.store.map.VersionedMapStore; | ||
13 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
14 | import tools.refinery.store.model.internal.ModelImpl; | ||
15 | import tools.refinery.store.model.internal.SimilarRelationEquivalenceClass; | ||
16 | import tools.refinery.store.model.representation.AuxilaryData; | ||
17 | import tools.refinery.store.model.representation.DataRepresentation; | ||
18 | import tools.refinery.store.model.representation.Relation; | ||
19 | |||
20 | import java.util.Set; | ||
21 | |||
22 | public 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 @@ | |||
1 | package tools.refinery.store.model; | ||
2 | |||
3 | import java.util.ArrayList; | ||
4 | import java.util.Arrays; | ||
5 | import java.util.List; | ||
6 | |||
7 | public 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 @@ | |||
1 | package tools.refinery.store.model; | ||
2 | |||
3 | import tools.refinery.store.map.ContinousHashProvider; | ||
4 | |||
5 | public 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 @@ | |||
1 | package tools.refinery.store.model; | ||
2 | |||
3 | import tools.refinery.store.map.ContinousHashProvider; | ||
4 | |||
5 | public 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 @@ | |||
1 | package tools.refinery.store.model.internal; | ||
2 | |||
3 | import java.util.HashMap; | ||
4 | import java.util.Map; | ||
5 | import java.util.Set; | ||
6 | |||
7 | import tools.refinery.store.map.ContinousHashProvider; | ||
8 | import tools.refinery.store.map.Cursor; | ||
9 | import tools.refinery.store.map.DiffCursor; | ||
10 | import tools.refinery.store.map.VersionedMap; | ||
11 | import tools.refinery.store.map.internal.MapDiffCursor; | ||
12 | import tools.refinery.store.model.Model; | ||
13 | import tools.refinery.store.model.ModelDiffCursor; | ||
14 | import tools.refinery.store.model.ModelStore; | ||
15 | import tools.refinery.store.model.representation.DataRepresentation; | ||
16 | |||
17 | public 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 @@ | |||
1 | package tools.refinery.store.model.internal; | ||
2 | |||
3 | import java.util.Objects; | ||
4 | |||
5 | import tools.refinery.store.map.ContinousHashProvider; | ||
6 | import tools.refinery.store.model.Tuple; | ||
7 | import tools.refinery.store.model.representation.Relation; | ||
8 | |||
9 | public 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 @@ | |||
1 | package tools.refinery.store.model.representation; | ||
2 | |||
3 | import tools.refinery.store.map.ContinousHashProvider; | ||
4 | |||
5 | public 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 @@ | |||
1 | package tools.refinery.store.model.representation; | ||
2 | |||
3 | import tools.refinery.store.map.ContinousHashProvider; | ||
4 | |||
5 | public 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 @@ | |||
1 | package tools.refinery.store.model.representation; | ||
2 | |||
3 | import tools.refinery.store.model.Tuple; | ||
4 | import tools.refinery.store.model.TupleHashProvider; | ||
5 | |||
6 | public 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 @@ | |||
1 | package tools.refinery.store.model.representation; | ||
2 | |||
3 | public 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 @@ | |||
1 | package tools.refinery.store.query; | ||
2 | |||
3 | import java.util.Optional; | ||
4 | import java.util.Set; | ||
5 | import java.util.stream.Stream; | ||
6 | |||
7 | import tools.refinery.store.model.Model; | ||
8 | import tools.refinery.store.query.building.DNFPredicate; | ||
9 | |||
10 | public 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 @@ | |||
1 | package tools.refinery.store.query; | ||
2 | |||
3 | import java.util.Set; | ||
4 | |||
5 | import tools.refinery.store.model.ModelDiffCursor; | ||
6 | import tools.refinery.store.model.ModelStore; | ||
7 | import tools.refinery.store.model.representation.DataRepresentation; | ||
8 | import tools.refinery.store.query.building.DNFPredicate; | ||
9 | import tools.refinery.store.query.view.RelationView; | ||
10 | |||
11 | public 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 @@ | |||
1 | package tools.refinery.store.query; | ||
2 | |||
3 | import java.util.Collections; | ||
4 | import java.util.HashMap; | ||
5 | import java.util.Map; | ||
6 | import java.util.Set; | ||
7 | |||
8 | import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification; | ||
9 | |||
10 | import tools.refinery.store.model.ModelDiffCursor; | ||
11 | import tools.refinery.store.model.ModelStore; | ||
12 | import tools.refinery.store.model.ModelStoreImpl; | ||
13 | import tools.refinery.store.model.representation.DataRepresentation; | ||
14 | import tools.refinery.store.query.building.DNFAnd; | ||
15 | import tools.refinery.store.query.building.DNFAtom; | ||
16 | import tools.refinery.store.query.building.DNFPredicate; | ||
17 | import tools.refinery.store.query.building.PredicateAtom; | ||
18 | import tools.refinery.store.query.building.RelationAtom; | ||
19 | import tools.refinery.store.query.internal.DNF2PQuery; | ||
20 | import tools.refinery.store.query.internal.QueriableModelImpl; | ||
21 | import tools.refinery.store.query.internal.RawPatternMatcher; | ||
22 | import tools.refinery.store.query.internal.DNF2PQuery.SimplePQuery; | ||
23 | import tools.refinery.store.query.view.RelationView; | ||
24 | |||
25 | public 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 @@ | |||
1 | package tools.refinery.store.query.building; | ||
2 | |||
3 | import java.util.HashMap; | ||
4 | import java.util.HashSet; | ||
5 | import java.util.List; | ||
6 | import java.util.Map; | ||
7 | import java.util.Set; | ||
8 | |||
9 | public 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 @@ | |||
1 | package tools.refinery.store.query.building; | ||
2 | |||
3 | import java.util.Collection; | ||
4 | import java.util.Iterator; | ||
5 | import java.util.Map; | ||
6 | import java.util.Set; | ||
7 | |||
8 | public 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 @@ | |||
1 | package tools.refinery.store.query.building; | ||
2 | |||
3 | import java.util.HashMap; | ||
4 | import java.util.HashSet; | ||
5 | import java.util.List; | ||
6 | import java.util.Map; | ||
7 | import java.util.UUID; | ||
8 | |||
9 | public 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 @@ | |||
1 | package tools.refinery.store.query.building; | ||
2 | |||
3 | import java.util.Map; | ||
4 | import java.util.Set; | ||
5 | |||
6 | public 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 @@ | |||
1 | package tools.refinery.store.query.building; | ||
2 | |||
3 | import java.util.List; | ||
4 | import java.util.Map; | ||
5 | import java.util.Set; | ||
6 | |||
7 | public 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 @@ | |||
1 | package tools.refinery.store.query.building; | ||
2 | |||
3 | import java.util.List; | ||
4 | import java.util.Map; | ||
5 | import java.util.Set; | ||
6 | |||
7 | import tools.refinery.store.query.view.FilteredRelationView; | ||
8 | import tools.refinery.store.query.view.RelationView; | ||
9 | |||
10 | public 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 @@ | |||
1 | package tools.refinery.store.query.building; | ||
2 | |||
3 | public 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 @@ | |||
1 | package tools.refinery.store.query.internal; | ||
2 | |||
3 | import java.util.ArrayList; | ||
4 | import java.util.HashMap; | ||
5 | import java.util.InputMismatchException; | ||
6 | import java.util.LinkedHashSet; | ||
7 | import java.util.List; | ||
8 | import java.util.Map; | ||
9 | import java.util.Set; | ||
10 | |||
11 | import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification; | ||
12 | import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; | ||
13 | import org.eclipse.viatra.query.runtime.api.scope.QueryScope; | ||
14 | import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; | ||
15 | import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; | ||
16 | import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable; | ||
17 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality; | ||
18 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
19 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Inequality; | ||
20 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.NegativePatternCall; | ||
21 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure; | ||
22 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
23 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
24 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.BasePQuery; | ||
25 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; | ||
26 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; | ||
27 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; | ||
28 | |||
29 | import tools.refinery.store.query.building.DNFAnd; | ||
30 | import tools.refinery.store.query.building.DNFAtom; | ||
31 | import tools.refinery.store.query.building.DNFPredicate; | ||
32 | import tools.refinery.store.query.building.EquivalenceAtom; | ||
33 | import tools.refinery.store.query.building.PredicateAtom; | ||
34 | import tools.refinery.store.query.building.RelationAtom; | ||
35 | import tools.refinery.store.query.building.Variable; | ||
36 | |||
37 | public 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 @@ | |||
1 | package tools.refinery.store.query.internal; | ||
2 | |||
3 | import java.lang.reflect.InvocationTargetException; | ||
4 | import java.util.concurrent.Callable; | ||
5 | |||
6 | import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex; | ||
7 | import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener; | ||
8 | import org.eclipse.viatra.query.runtime.api.scope.IInstanceObserver; | ||
9 | import org.eclipse.viatra.query.runtime.api.scope.ViatraBaseIndexChangeListener; | ||
10 | |||
11 | /** | ||
12 | * copied from org.eclipse.viatra.query.runtime.tabular.TabularEngineContext; | ||
13 | */ | ||
14 | public 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 @@ | |||
1 | package tools.refinery.store.query.internal; | ||
2 | |||
3 | import java.util.HashMap; | ||
4 | import java.util.HashSet; | ||
5 | import java.util.Map; | ||
6 | import java.util.Set; | ||
7 | |||
8 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; | ||
9 | import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; | ||
10 | |||
11 | import tools.refinery.store.model.Tuple; | ||
12 | import tools.refinery.store.model.representation.Relation; | ||
13 | import tools.refinery.store.query.view.RelationView; | ||
14 | |||
15 | public 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 @@ | |||
1 | package tools.refinery.store.query.internal; | ||
2 | |||
3 | import java.util.Optional; | ||
4 | import java.util.stream.Stream; | ||
5 | |||
6 | public 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 @@ | |||
1 | package tools.refinery.store.query.internal; | ||
2 | |||
3 | import java.util.HashMap; | ||
4 | import java.util.Map; | ||
5 | import java.util.Optional; | ||
6 | import java.util.Set; | ||
7 | import java.util.stream.Stream; | ||
8 | |||
9 | import org.eclipse.viatra.query.runtime.api.AdvancedViatraQueryEngine; | ||
10 | import org.eclipse.viatra.query.runtime.api.GenericQueryGroup; | ||
11 | import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification; | ||
12 | import org.eclipse.viatra.query.runtime.api.IQueryGroup; | ||
13 | |||
14 | import tools.refinery.store.map.Cursor; | ||
15 | import tools.refinery.store.map.DiffCursor; | ||
16 | import tools.refinery.store.model.Model; | ||
17 | import tools.refinery.store.model.ModelDiffCursor; | ||
18 | import tools.refinery.store.model.Tuple; | ||
19 | import tools.refinery.store.model.representation.DataRepresentation; | ||
20 | import tools.refinery.store.model.representation.Relation; | ||
21 | import tools.refinery.store.query.QueriableModel; | ||
22 | import tools.refinery.store.query.QueriableModelStore; | ||
23 | import tools.refinery.store.query.building.DNFPredicate; | ||
24 | |||
25 | public 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 @@ | |||
1 | package tools.refinery.store.query.internal; | ||
2 | |||
3 | import java.util.Optional; | ||
4 | import java.util.stream.Stream; | ||
5 | |||
6 | import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher; | ||
7 | import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification; | ||
8 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; | ||
9 | import org.eclipse.viatra.query.runtime.matchers.tuple.AbstractTuple; | ||
10 | |||
11 | public 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 @@ | |||
1 | package tools.refinery.store.query.internal; | ||
2 | |||
3 | import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex; | ||
4 | import org.eclipse.viatra.query.runtime.api.scope.IEngineContext; | ||
5 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext; | ||
6 | |||
7 | import tools.refinery.store.model.Model; | ||
8 | |||
9 | public 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 @@ | |||
1 | package tools.refinery.store.query.internal; | ||
2 | |||
3 | import java.util.Collection; | ||
4 | import java.util.Collections; | ||
5 | import java.util.HashMap; | ||
6 | import java.util.HashSet; | ||
7 | import java.util.Map; | ||
8 | import java.util.Set; | ||
9 | |||
10 | import org.eclipse.viatra.query.runtime.matchers.context.AbstractQueryMetaContext; | ||
11 | import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; | ||
12 | import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication; | ||
13 | |||
14 | import tools.refinery.store.query.view.RelationView; | ||
15 | |||
16 | /** | ||
17 | * The meta context information for String scopes. | ||
18 | */ | ||
19 | public 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 @@ | |||
1 | package tools.refinery.store.query.internal; | ||
2 | |||
3 | import static tools.refinery.store.util.CollectionsUtil.filter; | ||
4 | import static tools.refinery.store.util.CollectionsUtil.map; | ||
5 | |||
6 | import java.lang.reflect.InvocationTargetException; | ||
7 | import java.util.Iterator; | ||
8 | import java.util.Optional; | ||
9 | import java.util.concurrent.Callable; | ||
10 | |||
11 | import org.eclipse.viatra.query.runtime.base.core.NavigationHelperImpl; | ||
12 | import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; | ||
13 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryMetaContext; | ||
14 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext; | ||
15 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; | ||
16 | import org.eclipse.viatra.query.runtime.matchers.context.IndexingService; | ||
17 | import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; | ||
18 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; | ||
19 | import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask; | ||
20 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; | ||
21 | import org.eclipse.viatra.query.runtime.matchers.util.Accuracy; | ||
22 | |||
23 | import tools.refinery.store.model.Model; | ||
24 | import tools.refinery.store.query.view.RelationView; | ||
25 | |||
26 | public 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 @@ | |||
1 | package tools.refinery.store.query.internal; | ||
2 | |||
3 | import java.util.Set; | ||
4 | |||
5 | import org.apache.log4j.Logger; | ||
6 | import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; | ||
7 | import org.eclipse.viatra.query.runtime.api.scope.IEngineContext; | ||
8 | import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener; | ||
9 | import org.eclipse.viatra.query.runtime.api.scope.QueryScope; | ||
10 | |||
11 | import tools.refinery.store.model.Model; | ||
12 | import tools.refinery.store.model.Tuple; | ||
13 | import tools.refinery.store.model.representation.Relation; | ||
14 | import tools.refinery.store.query.view.RelationView; | ||
15 | |||
16 | public 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 @@ | |||
1 | package tools.refinery.store.query.internal; | ||
2 | |||
3 | import java.util.Arrays; | ||
4 | import java.util.Objects; | ||
5 | |||
6 | record 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 @@ | |||
1 | package tools.refinery.store.query.internal; | ||
2 | |||
3 | import java.util.ArrayList; | ||
4 | import java.util.Arrays; | ||
5 | import java.util.List; | ||
6 | |||
7 | import tools.refinery.store.model.Tuple; | ||
8 | |||
9 | public 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 @@ | |||
1 | package tools.refinery.store.query.internal; | ||
2 | |||
3 | import java.util.Objects; | ||
4 | |||
5 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; | ||
6 | import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; | ||
7 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; | ||
8 | |||
9 | import tools.refinery.store.model.Tuple; | ||
10 | import tools.refinery.store.query.view.RelationView; | ||
11 | |||
12 | public 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 @@ | |||
1 | package tools.refinery.store.query.view; | ||
2 | |||
3 | import java.util.function.BiPredicate; | ||
4 | |||
5 | import tools.refinery.store.model.Model; | ||
6 | import tools.refinery.store.model.Tuple; | ||
7 | import tools.refinery.store.model.Tuple.Tuple1; | ||
8 | import tools.refinery.store.model.representation.Relation; | ||
9 | |||
10 | public 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 @@ | |||
1 | package tools.refinery.store.query.view; | ||
2 | |||
3 | import tools.refinery.store.model.Model; | ||
4 | import tools.refinery.store.model.Tuple; | ||
5 | import tools.refinery.store.model.Tuple.Tuple1; | ||
6 | import tools.refinery.store.model.representation.Relation; | ||
7 | |||
8 | public 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 @@ | |||
1 | package tools.refinery.store.query.view; | ||
2 | |||
3 | import tools.refinery.store.model.Tuple; | ||
4 | import tools.refinery.store.model.representation.Relation; | ||
5 | |||
6 | public 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 @@ | |||
1 | package tools.refinery.store.query.view; | ||
2 | |||
3 | import java.util.Objects; | ||
4 | |||
5 | import org.eclipse.viatra.query.runtime.matchers.context.common.BaseInputKeyWrapper; | ||
6 | |||
7 | import tools.refinery.store.map.CursorAsIterator; | ||
8 | import tools.refinery.store.model.Model; | ||
9 | import tools.refinery.store.model.Tuple; | ||
10 | import 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 | */ | ||
19 | public 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 @@ | |||
1 | package tools.refinery.store.util; | ||
2 | |||
3 | import java.util.Iterator; | ||
4 | import java.util.NoSuchElementException; | ||
5 | import java.util.function.Function; | ||
6 | import java.util.function.Predicate; | ||
7 | |||
8 | public 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 @@ | |||
1 | package tools.refinery.store.map.tests; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | |||
5 | import org.junit.jupiter.api.Test; | ||
6 | |||
7 | import tools.refinery.store.map.VersionedMapStore; | ||
8 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
9 | import tools.refinery.store.model.Tuple; | ||
10 | import tools.refinery.store.model.TupleHashProvider; | ||
11 | |||
12 | class 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 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.fail; | ||
4 | |||
5 | import java.util.Random; | ||
6 | import java.util.stream.Stream; | ||
7 | |||
8 | import org.junit.jupiter.api.Tag; | ||
9 | import org.junit.jupiter.api.Timeout; | ||
10 | import org.junit.jupiter.params.ParameterizedTest; | ||
11 | import org.junit.jupiter.params.provider.Arguments; | ||
12 | import org.junit.jupiter.params.provider.MethodSource; | ||
13 | |||
14 | import tools.refinery.store.map.ContinousHashProvider; | ||
15 | import tools.refinery.store.map.VersionedMapStore; | ||
16 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
17 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
18 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
19 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
20 | |||
21 | class 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 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | import static org.junit.jupiter.api.Assertions.fail; | ||
5 | |||
6 | import java.util.AbstractMap.SimpleEntry; | ||
7 | import java.util.Collections; | ||
8 | import java.util.LinkedList; | ||
9 | import java.util.List; | ||
10 | import java.util.Random; | ||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import org.junit.jupiter.api.Tag; | ||
14 | import org.junit.jupiter.api.Timeout; | ||
15 | import org.junit.jupiter.params.ParameterizedTest; | ||
16 | import org.junit.jupiter.params.provider.Arguments; | ||
17 | import org.junit.jupiter.params.provider.MethodSource; | ||
18 | |||
19 | import tools.refinery.store.map.ContinousHashProvider; | ||
20 | import tools.refinery.store.map.Cursor; | ||
21 | import tools.refinery.store.map.VersionedMap; | ||
22 | import tools.refinery.store.map.VersionedMapStore; | ||
23 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
24 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
25 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
26 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
27 | |||
28 | class 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 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.fail; | ||
4 | |||
5 | import java.util.Random; | ||
6 | import java.util.stream.Stream; | ||
7 | |||
8 | import org.junit.jupiter.api.Tag; | ||
9 | import org.junit.jupiter.api.Timeout; | ||
10 | import org.junit.jupiter.params.ParameterizedTest; | ||
11 | import org.junit.jupiter.params.provider.Arguments; | ||
12 | import org.junit.jupiter.params.provider.MethodSource; | ||
13 | |||
14 | import tools.refinery.store.map.ContinousHashProvider; | ||
15 | import tools.refinery.store.map.DiffCursor; | ||
16 | import tools.refinery.store.map.VersionedMapStore; | ||
17 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
18 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
19 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
20 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
21 | |||
22 | class 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 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | import static org.junit.jupiter.api.Assertions.fail; | ||
5 | |||
6 | import java.util.Collections; | ||
7 | import java.util.LinkedList; | ||
8 | import java.util.List; | ||
9 | import java.util.stream.Stream; | ||
10 | |||
11 | import org.junit.jupiter.api.Tag; | ||
12 | import org.junit.jupiter.api.Timeout; | ||
13 | import org.junit.jupiter.params.ParameterizedTest; | ||
14 | import org.junit.jupiter.params.provider.Arguments; | ||
15 | import org.junit.jupiter.params.provider.MethodSource; | ||
16 | |||
17 | import tools.refinery.store.map.ContinousHashProvider; | ||
18 | import tools.refinery.store.map.VersionedMapStore; | ||
19 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
20 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
21 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
22 | |||
23 | class 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 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import java.util.ArrayList; | ||
4 | import java.util.Collections; | ||
5 | import java.util.HashMap; | ||
6 | import java.util.LinkedList; | ||
7 | import java.util.List; | ||
8 | import java.util.Map; | ||
9 | import java.util.Random; | ||
10 | |||
11 | import tools.refinery.store.map.VersionedMapStore; | ||
12 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
13 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
14 | |||
15 | public 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 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.fail; | ||
4 | |||
5 | import java.util.Random; | ||
6 | import java.util.stream.Stream; | ||
7 | |||
8 | import org.junit.jupiter.api.Tag; | ||
9 | import org.junit.jupiter.api.Timeout; | ||
10 | import org.junit.jupiter.params.ParameterizedTest; | ||
11 | import org.junit.jupiter.params.provider.Arguments; | ||
12 | import org.junit.jupiter.params.provider.MethodSource; | ||
13 | |||
14 | import tools.refinery.store.map.ContinousHashProvider; | ||
15 | import tools.refinery.store.map.VersionedMapStore; | ||
16 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
17 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
18 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
19 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
20 | |||
21 | class 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 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.fail; | ||
4 | |||
5 | import java.util.Random; | ||
6 | import java.util.stream.Stream; | ||
7 | |||
8 | import org.junit.jupiter.api.Tag; | ||
9 | import org.junit.jupiter.api.Timeout; | ||
10 | import org.junit.jupiter.params.ParameterizedTest; | ||
11 | import org.junit.jupiter.params.provider.Arguments; | ||
12 | import org.junit.jupiter.params.provider.MethodSource; | ||
13 | |||
14 | import tools.refinery.store.map.ContinousHashProvider; | ||
15 | import tools.refinery.store.map.VersionedMapStore; | ||
16 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
17 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
18 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
19 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
20 | |||
21 | class 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 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.fail; | ||
4 | |||
5 | import java.util.HashMap; | ||
6 | import java.util.Map; | ||
7 | import java.util.Random; | ||
8 | import java.util.stream.Stream; | ||
9 | |||
10 | import org.junit.jupiter.api.Tag; | ||
11 | import org.junit.jupiter.api.Timeout; | ||
12 | import org.junit.jupiter.params.ParameterizedTest; | ||
13 | import org.junit.jupiter.params.provider.Arguments; | ||
14 | import org.junit.jupiter.params.provider.MethodSource; | ||
15 | |||
16 | import tools.refinery.store.map.ContinousHashProvider; | ||
17 | import tools.refinery.store.map.VersionedMapStore; | ||
18 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
19 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
20 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
21 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
22 | |||
23 | class 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 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import java.util.HashMap; | ||
4 | import java.util.LinkedList; | ||
5 | import java.util.List; | ||
6 | import java.util.Map; | ||
7 | import java.util.Random; | ||
8 | import java.util.stream.Stream; | ||
9 | |||
10 | import org.junit.jupiter.api.Tag; | ||
11 | import org.junit.jupiter.api.Timeout; | ||
12 | import org.junit.jupiter.params.ParameterizedTest; | ||
13 | import org.junit.jupiter.params.provider.Arguments; | ||
14 | import org.junit.jupiter.params.provider.MethodSource; | ||
15 | |||
16 | import tools.refinery.store.map.ContinousHashProvider; | ||
17 | import tools.refinery.store.map.VersionedMapStore; | ||
18 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
19 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
20 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
21 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
22 | |||
23 | class 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 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz.utils; | ||
2 | |||
3 | import java.util.Arrays; | ||
4 | import java.util.LinkedList; | ||
5 | import java.util.List; | ||
6 | import java.util.stream.Stream; | ||
7 | |||
8 | import org.junit.jupiter.params.provider.Arguments; | ||
9 | |||
10 | public 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 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz.utils; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | |||
5 | import java.util.List; | ||
6 | |||
7 | import org.junit.jupiter.api.Test; | ||
8 | |||
9 | class 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 @@ | |||
1 | package tools.refinery.store.map.tests.utils; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | import static org.junit.jupiter.api.Assertions.assertTrue; | ||
5 | import static org.junit.jupiter.api.Assertions.fail; | ||
6 | |||
7 | import java.util.HashMap; | ||
8 | import java.util.Iterator; | ||
9 | import java.util.List; | ||
10 | import java.util.Map; | ||
11 | import java.util.Map.Entry; | ||
12 | |||
13 | import tools.refinery.store.map.ContinousHashProvider; | ||
14 | import tools.refinery.store.map.Cursor; | ||
15 | import tools.refinery.store.map.VersionedMap; | ||
16 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
17 | |||
18 | import java.util.TreeMap; | ||
19 | |||
20 | public 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 @@ | |||
1 | package tools.refinery.store.model.hashTests; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | |||
5 | import java.util.ArrayList; | ||
6 | import java.util.LinkedList; | ||
7 | import java.util.List; | ||
8 | import java.util.Random; | ||
9 | |||
10 | import org.junit.jupiter.api.Test; | ||
11 | |||
12 | import tools.refinery.store.map.ContinousHashProvider; | ||
13 | import tools.refinery.store.model.Tuple; | ||
14 | import tools.refinery.store.model.TupleHashProvider; | ||
15 | import tools.refinery.store.model.TupleHashProviderBitMagic; | ||
16 | |||
17 | class 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 @@ | |||
1 | package tools.refinery.store.model.tests; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | import static org.junit.jupiter.api.Assertions.assertFalse; | ||
5 | import static org.junit.jupiter.api.Assertions.assertTrue; | ||
6 | |||
7 | import java.util.Set; | ||
8 | |||
9 | import org.junit.jupiter.api.Assertions; | ||
10 | import org.junit.jupiter.api.Test; | ||
11 | |||
12 | import tools.refinery.store.model.Model; | ||
13 | import tools.refinery.store.model.ModelStore; | ||
14 | import tools.refinery.store.model.ModelStoreImpl; | ||
15 | import tools.refinery.store.model.Tuple; | ||
16 | import tools.refinery.store.model.representation.Relation; | ||
17 | |||
18 | class 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 @@ | |||
1 | package tools.refinery.store.query.test; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | |||
5 | import java.util.ArrayList; | ||
6 | import java.util.Arrays; | ||
7 | import java.util.Collections; | ||
8 | import java.util.HashSet; | ||
9 | import java.util.List; | ||
10 | import java.util.Set; | ||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import org.junit.jupiter.api.Test; | ||
14 | |||
15 | import tools.refinery.store.model.Tuple; | ||
16 | import tools.refinery.store.model.representation.Relation; | ||
17 | import tools.refinery.store.model.representation.TruthValue; | ||
18 | import tools.refinery.store.query.QueriableModel; | ||
19 | import tools.refinery.store.query.QueriableModelStore; | ||
20 | import tools.refinery.store.query.QueriableModelStoreImpl; | ||
21 | import tools.refinery.store.query.building.DNFAnd; | ||
22 | import tools.refinery.store.query.building.DNFPredicate; | ||
23 | import tools.refinery.store.query.building.EquivalenceAtom; | ||
24 | import tools.refinery.store.query.building.PredicateAtom; | ||
25 | import tools.refinery.store.query.building.RelationAtom; | ||
26 | import tools.refinery.store.query.building.Variable; | ||
27 | import tools.refinery.store.query.view.FilteredRelationView; | ||
28 | import tools.refinery.store.query.view.KeyOnlyRelationView; | ||
29 | import tools.refinery.store.query.view.RelationView; | ||
30 | |||
31 | class 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 @@ | |||
1 | package tools.refinery.store.query.test; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | |||
5 | import java.util.Arrays; | ||
6 | import java.util.Collections; | ||
7 | import java.util.List; | ||
8 | import java.util.Set; | ||
9 | |||
10 | import org.junit.jupiter.api.Test; | ||
11 | |||
12 | import tools.refinery.store.model.Tuple; | ||
13 | import tools.refinery.store.model.representation.Relation; | ||
14 | import tools.refinery.store.query.QueriableModel; | ||
15 | import tools.refinery.store.query.QueriableModelStore; | ||
16 | import tools.refinery.store.query.QueriableModelStoreImpl; | ||
17 | import tools.refinery.store.query.building.DNFAnd; | ||
18 | import tools.refinery.store.query.building.DNFPredicate; | ||
19 | import tools.refinery.store.query.building.RelationAtom; | ||
20 | import tools.refinery.store.query.building.Variable; | ||
21 | import tools.refinery.store.query.view.KeyOnlyRelationView; | ||
22 | import tools.refinery.store.query.view.RelationView; | ||
23 | |||
24 | class 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 @@ | |||
1 | package tools.refinery.store.util; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | import static tools.refinery.store.util.CollectionsUtil.filter; | ||
5 | import static tools.refinery.store.util.CollectionsUtil.map; | ||
6 | |||
7 | import java.util.ArrayList; | ||
8 | import java.util.Iterator; | ||
9 | import java.util.List; | ||
10 | import java.util.NoSuchElementException; | ||
11 | |||
12 | import org.junit.jupiter.api.Assertions; | ||
13 | import org.junit.jupiter.api.Test; | ||
14 | |||
15 | class 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 | } | ||