1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
/*
* SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
*
* SPDX-License-Identifier: EPL-2.0
*/
package tools.refinery.language.web.semantics;
import com.google.gson.JsonObject;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.eclipse.xtext.ide.ExecutorServiceProvider;
import org.eclipse.xtext.service.OperationCanceledManager;
import org.eclipse.xtext.util.CancelIndicator;
import org.eclipse.xtext.web.server.model.AbstractCachedService;
import org.eclipse.xtext.web.server.model.IXtextWebDocument;
import org.eclipse.xtext.web.server.validation.ValidationService;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tools.refinery.language.model.problem.Problem;
import tools.refinery.language.web.xtext.server.push.PushWebDocument;
import java.util.List;
import java.util.concurrent.*;
@Singleton
public class SemanticsService extends AbstractCachedService<SemanticsResult> {
private static final Logger LOG = LoggerFactory.getLogger(SemanticsService.class);
@Inject
private Provider<SemanticsWorker> workerProvider;
@Inject
private OperationCanceledManager operationCanceledManager;
@Inject
private ValidationService validationService;
private ExecutorService executorService;
@Inject
public void setExecutorServiceProvider(ExecutorServiceProvider provider) {
executorService = provider.get(this.getClass().getName());
}
@Override
public SemanticsResult compute(IXtextWebDocument doc, CancelIndicator cancelIndicator) {
long start = 0;
if (LOG.isTraceEnabled()) {
start = System.currentTimeMillis();
}
if (hasError(doc, cancelIndicator)) {
return null;
}
var problem = getProblem(doc);
if (problem == null) {
return new SemanticsSuccessResult(List.of(), List.of(), new JsonObject());
}
var worker = workerProvider.get();
worker.setProblem(problem, cancelIndicator);
var future = executorService.submit(worker);
SemanticsResult result = null;
try {
result = future.get(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
future.cancel(true);
LOG.error("Semantics service interrupted", e);
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
operationCanceledManager.propagateAsErrorIfCancelException(e.getCause());
LOG.debug("Error while computing semantics", e);
if (e.getCause() instanceof Error error) {
throw error;
}
String message = e.getMessage();
if (message == null) {
message = "Partial interpretation error";
}
return new SemanticsInternalErrorResult(message);
} catch (TimeoutException e) {
future.cancel(true);
LOG.trace("Semantics service timeout", e);
return new SemanticsInternalErrorResult("Partial interpretation timed out");
}
if (LOG.isTraceEnabled()) {
long end = System.currentTimeMillis();
LOG.trace("Computed semantics for {} ({}) in {}ms", doc.getResourceId(), doc.getStateId(),
end - start);
}
return result;
}
private boolean hasError(IXtextWebDocument doc, CancelIndicator cancelIndicator) {
if (!(doc instanceof PushWebDocument pushDoc)) {
throw new IllegalArgumentException("Unexpected IXtextWebDocument: " + doc);
}
var validationResult = pushDoc.getCachedServiceResult(validationService, cancelIndicator, true);
return validationResult.getIssues().stream()
.anyMatch(issue -> "error".equals(issue.getSeverity()));
}
@Nullable
private Problem getProblem(IXtextWebDocument doc) {
var contents = doc.getResource().getContents();
if (contents.isEmpty()) {
return null;
}
var model = contents.get(0);
if (!(model instanceof Problem problem)) {
return null;
}
return problem;
}
}
|