aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/generator
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2024-02-18 22:19:44 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2024-02-18 22:19:44 +0100
commitb9bb6816a8806fe4e918c8a2074364676737cc0c (patch)
tree4b9c822d4996854adc89ca24935a6964af0d5f7d /subprojects/generator
parentrefactor(language): no fully qualified self import (diff)
downloadrefinery-b9bb6816a8806fe4e918c8a2074364676737cc0c.tar.gz
refinery-b9bb6816a8806fe4e918c8a2074364676737cc0c.tar.zst
refinery-b9bb6816a8806fe4e918c8a2074364676737cc0c.zip
feat(language): import validation
Validate imports and imported resources. Also fixes a linking error in imported resources by ensuring that imported resources are always fully resolved with all of their derived state.
Diffstat (limited to 'subprojects/generator')
-rw-r--r--subprojects/generator/src/main/java/tools/refinery/generator/ProblemLoader.java99
1 files changed, 85 insertions, 14 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 e44dddc0..580a87b6 100644
--- a/subprojects/generator/src/main/java/tools/refinery/generator/ProblemLoader.java
+++ b/subprojects/generator/src/main/java/tools/refinery/generator/ProblemLoader.java
@@ -9,17 +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;
22import tools.refinery.language.scoping.imports.ImportAdapter; 28import tools.refinery.language.scoping.imports.ImportAdapter;
29import tools.refinery.language.scoping.imports.ImportCollector;
23import tools.refinery.store.util.CancellationToken; 30import tools.refinery.store.util.CancellationToken;
24 31
25import java.io.ByteArrayOutputStream; 32import java.io.ByteArrayOutputStream;
@@ -28,10 +35,8 @@ import java.io.IOException;
28import java.io.InputStream; 35import java.io.InputStream;
29import java.nio.charset.StandardCharsets; 36import java.nio.charset.StandardCharsets;
30import java.nio.file.Path; 37import java.nio.file.Path;
31import java.util.ArrayList; 38import java.util.*;
32import java.util.HashSet; 39import java.util.stream.Collectors;
33import java.util.List;
34import java.util.Map;
35 40
36// This class is used as a fluent builder. 41// This class is used as a fluent builder.
37@SuppressWarnings("UnusedReturnValue") 42@SuppressWarnings("UnusedReturnValue")
@@ -47,6 +52,15 @@ public class ProblemLoader {
47 @Inject 52 @Inject
48 private IResourceValidator resourceValidator; 53 private IResourceValidator resourceValidator;
49 54
55 @Inject
56 private ImportCollector importCollector;
57
58 @Inject
59 private GlobalResourceDescriptionProvider globalResourceDescriptionProvider;
60
61 @Inject
62 private IQualifiedNameConverter qualifiedNameConverter;
63
50 private CancellationToken cancellationToken = CancellationToken.NONE; 64 private CancellationToken cancellationToken = CancellationToken.NONE;
51 65
52 private final List<Path> extraPaths = new ArrayList<>(); 66 private final List<Path> extraPaths = new ArrayList<>();
@@ -117,14 +131,30 @@ public class ProblemLoader {
117 } 131 }
118 132
119 public Problem loadResource(Resource resource) { 133 public Problem loadResource(Resource resource) {
120 var issues = resourceValidator.validate(resource, CheckMode.ALL, () -> { 134 EcoreUtil.resolveAll(resource);
135 CancelIndicator cancelIndicator = () -> {
121 cancellationToken.checkCancelled(); 136 cancellationToken.checkCancelled();
122 return Thread.interrupted(); 137 return Thread.interrupted();
123 }); 138 };
139 var shadowedNames = new LinkedHashMap<ShadowingKey, Set<IEObjectDescription>>();
140 var issues = new ArrayList<Issue>();
141 validateResource(resource, issues, cancelIndicator);
124 cancellationToken.checkCancelled(); 142 cancellationToken.checkCancelled();
125 var errors = issues.stream() 143 var resourceSet = resource.getResourceSet();
126 .filter(issue -> issue.getSeverity() == Severity.ERROR) 144 if (resourceSet != null) {
127 .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();
128 if (!errors.isEmpty()) { 158 if (!errors.isEmpty()) {
129 throw new ValidationErrorsException(resource.getURI(), errors); 159 throw new ValidationErrorsException(resource.getURI(), errors);
130 } 160 }
@@ -134,8 +164,49 @@ public class ProblemLoader {
134 return problem; 164 return problem;
135 } 165 }
136 166
137 public Problem loadScopeConstraints(Problem problem, List<String> extraScopes, 167 private void findShadowedNames(Resource importedResource,
138 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 {
139 var allScopes = new ArrayList<>(extraScopes); 210 var allScopes = new ArrayList<>(extraScopes);
140 allScopes.addAll(overrideScopes); 211 allScopes.addAll(overrideScopes);
141 if (allScopes.isEmpty()) { 212 if (allScopes.isEmpty()) {