aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/generator/src/main/java/tools/refinery/generator/ProblemLoader.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/generator/src/main/java/tools/refinery/generator/ProblemLoader.java')
-rw-r--r--subprojects/generator/src/main/java/tools/refinery/generator/ProblemLoader.java129
1 files changed, 111 insertions, 18 deletions
diff --git a/subprojects/generator/src/main/java/tools/refinery/generator/ProblemLoader.java b/subprojects/generator/src/main/java/tools/refinery/generator/ProblemLoader.java
index 20ea8132..580a87b6 100644
--- a/subprojects/generator/src/main/java/tools/refinery/generator/ProblemLoader.java
+++ b/subprojects/generator/src/main/java/tools/refinery/generator/ProblemLoader.java
@@ -1,5 +1,5 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2023-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
@@ -9,16 +9,24 @@ import com.google.inject.Inject;
9import com.google.inject.Provider; 9import com.google.inject.Provider;
10import org.eclipse.emf.common.util.URI; 10import org.eclipse.emf.common.util.URI;
11import org.eclipse.emf.ecore.resource.Resource; 11import org.eclipse.emf.ecore.resource.Resource;
12import org.eclipse.emf.ecore.util.EcoreUtil;
12import org.eclipse.xtext.diagnostics.Severity; 13import org.eclipse.xtext.diagnostics.Severity;
13import org.eclipse.xtext.resource.FileExtensionProvider; 14import org.eclipse.xtext.naming.IQualifiedNameConverter;
14import org.eclipse.xtext.resource.IResourceFactory; 15import org.eclipse.xtext.resource.*;
15import org.eclipse.xtext.resource.XtextResourceSet; 16import org.eclipse.xtext.scoping.impl.GlobalResourceDescriptionProvider;
17import org.eclipse.xtext.util.CancelIndicator;
16import org.eclipse.xtext.util.LazyStringInputStream; 18import org.eclipse.xtext.util.LazyStringInputStream;
17import org.eclipse.xtext.validation.CheckMode; 19import org.eclipse.xtext.validation.CheckMode;
18import org.eclipse.xtext.validation.IResourceValidator; 20import org.eclipse.xtext.validation.IResourceValidator;
21import org.eclipse.xtext.validation.Issue;
19import tools.refinery.language.model.problem.Problem; 22import tools.refinery.language.model.problem.Problem;
20import tools.refinery.language.model.problem.Relation; 23import tools.refinery.language.model.problem.Relation;
21import tools.refinery.language.model.problem.ScopeDeclaration; 24import tools.refinery.language.model.problem.ScopeDeclaration;
25import tools.refinery.language.naming.NamingUtil;
26import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
27import tools.refinery.language.resource.ProblemResourceDescriptionStrategy.ShadowingKey;
28import tools.refinery.language.scoping.imports.ImportAdapter;
29import tools.refinery.language.scoping.imports.ImportCollector;
22import tools.refinery.store.util.CancellationToken; 30import tools.refinery.store.util.CancellationToken;
23 31
24import java.io.ByteArrayOutputStream; 32import java.io.ByteArrayOutputStream;
@@ -26,11 +34,12 @@ import java.io.File;
26import java.io.IOException; 34import java.io.IOException;
27import java.io.InputStream; 35import java.io.InputStream;
28import java.nio.charset.StandardCharsets; 36import java.nio.charset.StandardCharsets;
29import java.util.ArrayList; 37import java.nio.file.Path;
30import java.util.HashSet; 38import java.util.*;
31import java.util.List; 39import java.util.stream.Collectors;
32import java.util.Map;
33 40
41// This class is used as a fluent builder.
42@SuppressWarnings("UnusedReturnValue")
34public class ProblemLoader { 43public class ProblemLoader {
35 private String fileExtension; 44 private String fileExtension;
36 45
@@ -43,8 +52,19 @@ public class ProblemLoader {
43 @Inject 52 @Inject
44 private IResourceValidator resourceValidator; 53 private IResourceValidator resourceValidator;
45 54
55 @Inject
56 private ImportCollector importCollector;
57
58 @Inject
59 private GlobalResourceDescriptionProvider globalResourceDescriptionProvider;
60
61 @Inject
62 private IQualifiedNameConverter qualifiedNameConverter;
63
46 private CancellationToken cancellationToken = CancellationToken.NONE; 64 private CancellationToken cancellationToken = CancellationToken.NONE;
47 65
66 private final List<Path> extraPaths = new ArrayList<>();
67
48 @Inject 68 @Inject
49 public void setFileExtensionProvider(FileExtensionProvider fileExtensionProvider) { 69 public void setFileExtensionProvider(FileExtensionProvider fileExtensionProvider) {
50 this.fileExtension = fileExtensionProvider.getPrimaryFileExtension(); 70 this.fileExtension = fileExtensionProvider.getPrimaryFileExtension();
@@ -55,6 +75,15 @@ public class ProblemLoader {
55 return this; 75 return this;
56 } 76 }
57 77
78 public ProblemLoader extraPath(String path) {
79 return extraPath(Path.of(path));
80 }
81
82 public ProblemLoader extraPath(Path path) {
83 extraPaths.add(path.toAbsolutePath().normalize());
84 return this;
85 }
86
58 public Problem loadString(String problemString, URI uri) throws IOException { 87 public Problem loadString(String problemString, URI uri) throws IOException {
59 try (var stream = new LazyStringInputStream(problemString)) { 88 try (var stream = new LazyStringInputStream(problemString)) {
60 return loadStream(stream, uri); 89 return loadStream(stream, uri);
@@ -66,8 +95,8 @@ public class ProblemLoader {
66 } 95 }
67 96
68 public Problem loadStream(InputStream inputStream, URI uri) throws IOException { 97 public Problem loadStream(InputStream inputStream, URI uri) throws IOException {
69 var resourceSet = resourceSetProvider.get(); 98 var resourceSet = createResourceSet();
70 var resourceUri = uri == null ? URI.createFileURI("__synthetic." + fileExtension) : uri; 99 var resourceUri = uri == null ? URI.createURI("__synthetic." + fileExtension) : uri;
71 var resource = resourceFactory.createResource(resourceUri); 100 var resource = resourceFactory.createResource(resourceUri);
72 resourceSet.getResources().add(resource); 101 resourceSet.getResources().add(resource);
73 resource.load(inputStream, Map.of()); 102 resource.load(inputStream, Map.of());
@@ -87,22 +116,45 @@ public class ProblemLoader {
87 } 116 }
88 117
89 public Problem loadUri(URI uri) throws IOException { 118 public Problem loadUri(URI uri) throws IOException {
90 var resourceSet = resourceSetProvider.get(); 119 var resourceSet = createResourceSet();
91 var resource = resourceFactory.createResource(uri); 120 var resource = resourceFactory.createResource(uri);
92 resourceSet.getResources().add(resource); 121 resourceSet.getResources().add(resource);
93 resource.load(Map.of()); 122 resource.load(Map.of());
94 return loadResource(resource); 123 return loadResource(resource);
95 } 124 }
96 125
126 private XtextResourceSet createResourceSet() {
127 var resourceSet = resourceSetProvider.get();
128 var adapter = ImportAdapter.getOrInstall(resourceSet);
129 adapter.getLibraryPaths().addAll(0, extraPaths);
130 return resourceSet;
131 }
132
97 public Problem loadResource(Resource resource) { 133 public Problem loadResource(Resource resource) {
98 var issues = resourceValidator.validate(resource, CheckMode.ALL, () -> { 134 EcoreUtil.resolveAll(resource);
135 CancelIndicator cancelIndicator = () -> {
99 cancellationToken.checkCancelled(); 136 cancellationToken.checkCancelled();
100 return Thread.interrupted(); 137 return Thread.interrupted();
101 }); 138 };
139 var shadowedNames = new LinkedHashMap<ShadowingKey, Set<IEObjectDescription>>();
140 var issues = new ArrayList<Issue>();
141 validateResource(resource, issues, cancelIndicator);
102 cancellationToken.checkCancelled(); 142 cancellationToken.checkCancelled();
103 var errors = issues.stream() 143 var resourceSet = resource.getResourceSet();
104 .filter(issue -> issue.getSeverity() == Severity.ERROR) 144 if (resourceSet != null) {
105 .toList(); 145 var imports = importCollector.getAllImports(resource).toUriSet();
146 cancellationToken.checkCancelled();
147 for (var importedUri : imports) {
148 var importedResource = resourceSet.getResource(importedUri, false);
149 if (importedResource == null) {
150 throw new IllegalStateException("Unknown imported resource: " + importedUri);
151 }
152 findShadowedNames(importedResource, shadowedNames);
153 validateResource(importedResource, issues, cancelIndicator);
154 }
155 }
156 addNameClashIssues(issues, shadowedNames);
157 var errors = issues.stream().filter(issue -> issue.getSeverity() == Severity.ERROR).toList();
106 if (!errors.isEmpty()) { 158 if (!errors.isEmpty()) {
107 throw new ValidationErrorsException(resource.getURI(), errors); 159 throw new ValidationErrorsException(resource.getURI(), errors);
108 } 160 }
@@ -112,8 +164,49 @@ public class ProblemLoader {
112 return problem; 164 return problem;
113 } 165 }
114 166
115 public Problem loadScopeConstraints(Problem problem, List<String> extraScopes, 167 private void findShadowedNames(Resource importedResource,
116 List<String> overrideScopes) throws IOException { 168 LinkedHashMap<ShadowingKey, Set<IEObjectDescription>> shadowedNames) {
169 var resourceDescription = globalResourceDescriptionProvider.getResourceDescription(importedResource);
170 for (var eObjectDescription : resourceDescription.getExportedObjects()) {
171 var name = eObjectDescription.getName();
172 if (NamingUtil.isFullyQualified(name)) {
173 var shadowingKey = ProblemResourceDescriptionStrategy.getShadowingKey(eObjectDescription);
174 var entries = shadowedNames.computeIfAbsent(shadowingKey, ignored -> new LinkedHashSet<>());
175 entries.add(eObjectDescription);
176 }
177 }
178 cancellationToken.checkCancelled();
179 }
180
181 private void validateResource(Resource importedResource, ArrayList<Issue> issues,
182 CancelIndicator cancelIndicator) {
183 issues.addAll(resourceValidator.validate(importedResource, CheckMode.ALL, cancelIndicator));
184 cancellationToken.checkCancelled();
185 }
186
187 private void addNameClashIssues(ArrayList<Issue> issues,
188 LinkedHashMap<ShadowingKey, Set<IEObjectDescription>> shadowedNames) {
189 for (var entry : shadowedNames.entrySet()) {
190 var eObjectDescriptions = entry.getValue();
191 if (eObjectDescriptions.size() <= 1) {
192 continue;
193 }
194 var qualifiedName = qualifiedNameConverter.toString(NamingUtil.stripRootPrefix(entry.getKey().name()));
195 var uris = eObjectDescriptions.stream()
196 .map(eObjectDescription -> eObjectDescription.getEObjectURI().trimFragment().toString())
197 .collect(Collectors.joining(", "));
198 var message = "Object with qualified name %s is also defined in %s".formatted(qualifiedName, uris);
199 for (var eObjectDescription : eObjectDescriptions) {
200 var issue = new Issue.IssueImpl();
201 issue.setSeverity(Severity.ERROR);
202 issue.setMessage(message);
203 issue.setUriToProblem(eObjectDescription.getEObjectURI());
204 issues.add(issue);
205 }
206 }
207 }
208
209 public Problem loadScopeConstraints(Problem problem, List<String> extraScopes, List<String> overrideScopes) throws IOException {
117 var allScopes = new ArrayList<>(extraScopes); 210 var allScopes = new ArrayList<>(extraScopes);
118 allScopes.addAll(overrideScopes); 211 allScopes.addAll(overrideScopes);
119 if (allScopes.isEmpty()) { 212 if (allScopes.isEmpty()) {