aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java')
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java222
1 files changed, 222 insertions, 0 deletions
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java
new file mode 100644
index 00000000..d905aa9a
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java
@@ -0,0 +1,222 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.resource.state;
7
8import com.google.inject.Inject;
9import com.google.inject.Provider;
10import com.google.inject.Singleton;
11import com.google.inject.name.Named;
12import org.eclipse.emf.common.notify.impl.AdapterImpl;
13import org.eclipse.emf.ecore.EObject;
14import org.eclipse.emf.ecore.resource.Resource;
15import org.eclipse.emf.ecore.util.EcoreUtil;
16import org.eclipse.xtext.Constants;
17import org.eclipse.xtext.resource.DerivedStateAwareResource;
18import org.eclipse.xtext.resource.IDerivedStateComputer;
19import org.eclipse.xtext.resource.XtextResource;
20import tools.refinery.language.model.problem.*;
21import tools.refinery.language.utils.ProblemUtil;
22
23import java.util.*;
24import java.util.function.Function;
25
26@Singleton
27public class ProblemDerivedStateComputer implements IDerivedStateComputer {
28 public static final String NEW_NODE = "new";
29
30 @Inject
31 @Named(Constants.LANGUAGE_NAME)
32 private String languageName;
33
34 @Inject
35 private Provider<NodeNameCollector> nodeNameCollectorProvider;
36
37 @Inject
38 private DerivedVariableComputer derivedVariableComputer;
39
40 @Override
41 public void installDerivedState(DerivedStateAwareResource resource, boolean preLinkingPhase) {
42 var problem = getProblem(resource);
43 if (problem != null) {
44 var adapter = getOrInstallAdapter(resource);
45 installDerivedProblemState(problem, adapter, preLinkingPhase);
46 }
47 }
48
49 protected Problem getProblem(Resource resource) {
50 List<EObject> contents = resource.getContents();
51 if (contents.isEmpty()) {
52 return null;
53 }
54 EObject object = contents.get(0);
55 if (object instanceof Problem problem) {
56 return problem;
57 }
58 return null;
59 }
60
61 protected void installDerivedProblemState(Problem problem, Adapter adapter, boolean preLinkingPhase) {
62 installDerivedClassDeclarationState(problem, adapter);
63 if (preLinkingPhase) {
64 return;
65 }
66 Set<String> nodeNames = installDerivedNodes(problem);
67 derivedVariableComputer.installDerivedVariables(problem, nodeNames);
68 }
69
70 protected void installDerivedClassDeclarationState(Problem problem, Adapter adapter) {
71 for (var statement : problem.getStatements()) {
72 if (statement instanceof ClassDeclaration classDeclaration) {
73 installOrRemoveNewNode(adapter, classDeclaration);
74 for (var featureDeclaration : classDeclaration.getFeatureDeclarations()) {
75 if (featureDeclaration instanceof ReferenceDeclaration referenceDeclaration) {
76 installOrRemoveInvalidMultiplicityPredicate(adapter, classDeclaration, referenceDeclaration);
77 }
78 }
79 }
80 }
81 }
82
83 protected void installOrRemoveNewNode(Adapter adapter, ClassDeclaration declaration) {
84 if (declaration.isAbstract()) {
85 var newNode = declaration.getNewNode();
86 if (newNode != null) {
87 declaration.setNewNode(null);
88 adapter.removeNewNode(declaration);
89 }
90 } else {
91 if (declaration.getNewNode() == null) {
92 var newNode = adapter.createNewNodeIfAbsent(declaration, key -> createNode(NEW_NODE));
93 declaration.setNewNode(newNode);
94 }
95 }
96 }
97
98 protected void installOrRemoveInvalidMultiplicityPredicate(
99 Adapter adapter, ClassDeclaration containingClassDeclaration, ReferenceDeclaration declaration) {
100 if (ProblemUtil.hasMultiplicityConstraint(declaration)) {
101 if (declaration.getInvalidMultiplicity() == null) {
102 var invalidMultiplicity = adapter.createInvalidMultiplicityPredicateIfAbsent(declaration, key -> {
103 var predicate = ProblemFactory.eINSTANCE.createPredicateDefinition();
104 predicate.setError(true);
105 predicate.setName("invalidMultiplicity");
106 var parameter = ProblemFactory.eINSTANCE.createParameter();
107 parameter.setParameterType(containingClassDeclaration);
108 parameter.setName("node");
109 predicate.getParameters().add(parameter);
110 return predicate;
111 });
112 declaration.setInvalidMultiplicity(invalidMultiplicity);
113 }
114 } else {
115 var invalidMultiplicity = declaration.getInvalidMultiplicity();
116 if (invalidMultiplicity != null) {
117 declaration.setInvalidMultiplicity(null);
118 adapter.removeInvalidMultiplicityPredicate(declaration);
119 }
120 }
121 }
122
123 protected Set<String> installDerivedNodes(Problem problem) {
124 var collector = nodeNameCollectorProvider.get();
125 collector.collectNodeNames(problem);
126 Set<String> nodeNames = collector.getNodeNames();
127 List<Node> graphNodes = problem.getNodes();
128 for (String nodeName : nodeNames) {
129 var graphNode = createNode(nodeName);
130 graphNodes.add(graphNode);
131 }
132 return nodeNames;
133 }
134
135 protected Node createNode(String name) {
136 var node = ProblemFactory.eINSTANCE.createNode();
137 node.setName(name);
138 return node;
139 }
140
141 @Override
142 public void discardDerivedState(DerivedStateAwareResource resource) {
143 var problem = getProblem(resource);
144 if (problem != null) {
145 var adapter = getOrInstallAdapter(resource);
146 discardDerivedProblemState(problem, adapter);
147 }
148 }
149
150 protected void discardDerivedProblemState(Problem problem, Adapter adapter) {
151 var abstractClassDeclarations = new HashSet<ClassDeclaration>();
152 var referenceDeclarationsWithMultiplicity = new HashSet<ReferenceDeclaration>();
153 problem.getNodes().clear();
154 for (var statement : problem.getStatements()) {
155 if (statement instanceof ClassDeclaration classDeclaration) {
156 classDeclaration.setNewNode(null);
157 if (classDeclaration.isAbstract()) {
158 abstractClassDeclarations.add(classDeclaration);
159 }
160 for (var featureDeclaration : classDeclaration.getFeatureDeclarations()) {
161 if (featureDeclaration instanceof ReferenceDeclaration referenceDeclaration &&
162 ProblemUtil.hasMultiplicityConstraint(referenceDeclaration)) {
163 referenceDeclarationsWithMultiplicity.add(referenceDeclaration);
164 }
165 }
166 }
167 }
168 adapter.retainAll(abstractClassDeclarations, referenceDeclarationsWithMultiplicity);
169 derivedVariableComputer.discardDerivedVariables(problem);
170 }
171
172 protected Adapter getOrInstallAdapter(Resource resource) {
173 if (!(resource instanceof XtextResource)) {
174 return new Adapter();
175 }
176 String resourceLanguageName = ((XtextResource) resource).getLanguageName();
177 if (!languageName.equals(resourceLanguageName)) {
178 return new Adapter();
179 }
180 var adapter = (Adapter) EcoreUtil.getAdapter(resource.eAdapters(), Adapter.class);
181 if (adapter == null) {
182 adapter = new Adapter();
183 resource.eAdapters().add(adapter);
184 }
185 return adapter;
186 }
187
188 protected static class Adapter extends AdapterImpl {
189 private final Map<ClassDeclaration, Node> newNodes = new HashMap<>();
190 private final Map<ReferenceDeclaration, PredicateDefinition> invalidMultiplicityPredicates = new HashMap<>();
191
192 public Node createNewNodeIfAbsent(ClassDeclaration classDeclaration,
193 Function<ClassDeclaration, Node> createNode) {
194 return newNodes.computeIfAbsent(classDeclaration, createNode);
195 }
196
197 public void removeNewNode(ClassDeclaration classDeclaration) {
198 newNodes.remove(classDeclaration);
199 }
200
201 public PredicateDefinition createInvalidMultiplicityPredicateIfAbsent(
202 ReferenceDeclaration referenceDeclaration,
203 Function<ReferenceDeclaration, PredicateDefinition> createPredicate) {
204 return invalidMultiplicityPredicates.computeIfAbsent(referenceDeclaration, createPredicate);
205 }
206
207 public void removeInvalidMultiplicityPredicate(ReferenceDeclaration referenceDeclaration) {
208 invalidMultiplicityPredicates.remove(referenceDeclaration);
209 }
210
211 public void retainAll(Collection<ClassDeclaration> abstractClassDeclarations,
212 Collection<ReferenceDeclaration> referenceDeclarationsWithMultiplicity) {
213 newNodes.keySet().retainAll(abstractClassDeclarations);
214 invalidMultiplicityPredicates.keySet().retainAll(referenceDeclarationsWithMultiplicity);
215 }
216
217 @Override
218 public boolean isAdapterForType(Object type) {
219 return Adapter.class == type;
220 }
221 }
222}