aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsService.java
blob: ba55dc7765299e56e7c752f29e12201a654ccef1 (plain) (blame)
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;
	}
}