aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java')
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java160
1 files changed, 160 insertions, 0 deletions
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java
new file mode 100644
index 00000000..ac5a92ba
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java
@@ -0,0 +1,160 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.scoping.imports;
7
8import com.google.common.base.Splitter;
9import com.google.common.base.Strings;
10import com.google.inject.Inject;
11import com.google.inject.Provider;
12import com.google.inject.Singleton;
13import org.eclipse.emf.common.util.URI;
14import org.eclipse.emf.ecore.EObject;
15import org.eclipse.emf.ecore.resource.Resource;
16import org.eclipse.xtext.linking.impl.LinkingHelper;
17import org.eclipse.xtext.naming.IQualifiedNameConverter;
18import org.eclipse.xtext.naming.QualifiedName;
19import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
20import org.eclipse.xtext.resource.IEObjectDescription;
21import org.eclipse.xtext.util.IResourceScopeCache;
22import tools.refinery.language.model.problem.ImportStatement;
23import tools.refinery.language.model.problem.Problem;
24import tools.refinery.language.model.problem.ProblemPackage;
25import tools.refinery.language.naming.NamingUtil;
26import tools.refinery.language.resource.LoadOnDemandResourceDescriptionProvider;
27import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
28
29import java.util.*;
30
31@Singleton
32public class ImportCollector {
33 private static final String PREFIX = "tools.refinery.language.imports.";
34 private static final String DIRECT_IMPORTS_KEY = PREFIX + "DIRECT_IMPORTS";
35 private static final String ALL_IMPORTS_KEY = PREFIX + "ALL_IMPORTS";
36
37 @Inject
38 private IResourceScopeCache cache;
39
40 @Inject
41 private LinkingHelper linkingHelper;
42
43 @Inject
44 private IQualifiedNameConverter qualifiedNameConverter;
45
46 @Inject
47 private Provider<LoadOnDemandResourceDescriptionProvider> loadOnDemandProvider;
48
49 public ImportCollection getDirectImports(Resource resource) {
50 return cache.get(DIRECT_IMPORTS_KEY, resource, () -> this.computeDirectImports(resource));
51 }
52
53 protected ImportCollection computeDirectImports(Resource resource) {
54 if (resource.getContents().isEmpty() || !(resource.getContents().getFirst() instanceof Problem problem)) {
55 return ImportCollection.EMPTY;
56 }
57 var resourceSet = resource.getResourceSet();
58 if (resourceSet == null) {
59 return ImportCollection.EMPTY;
60 }
61 var adapter = ImportAdapter.getOrInstall(resourceSet);
62 var collection = new ImportCollection();
63 collectAutomaticImports(collection, adapter);
64 collectExplicitImports(problem, collection, adapter);
65 collection.remove(resource.getURI());
66 return collection;
67 }
68
69 private void collectAutomaticImports(ImportCollection importCollection, ImportAdapter adapter) {
70 for (var library : adapter.getLibraries()) {
71 for (var qualifiedName : library.getAutomaticImports()) {
72 var uri = adapter.resolveQualifiedName(qualifiedName);
73 if (uri != null) {
74 importCollection.add(NamedImport.implicit(uri, qualifiedName));
75 }
76 }
77 }
78 }
79
80 private void collectExplicitImports(Problem problem, ImportCollection collection, ImportAdapter adapter) {
81 for (var statement : problem.getStatements()) {
82 if (statement instanceof ImportStatement importStatement) {
83 collectImportStatement(importStatement, collection, adapter);
84 }
85 }
86 }
87
88 private void collectImportStatement(ImportStatement importStatement, ImportCollection collection,
89 ImportAdapter adapter) {
90 var nodes = NodeModelUtils.findNodesForFeature(importStatement,
91 ProblemPackage.Literals.IMPORT_STATEMENT__IMPORTED_MODULE);
92 var aliasString = importStatement.getAlias();
93 var alias = Strings.isNullOrEmpty(aliasString) ? QualifiedName.EMPTY :
94 NamingUtil.stripRootPrefix(qualifiedNameConverter.toQualifiedName(aliasString));
95 var referredProblem = (EObject) importStatement.eGet(ProblemPackage.Literals.IMPORT_STATEMENT__IMPORTED_MODULE,
96 false);
97 URI referencedUri = null;
98 if (referredProblem != null && !referredProblem.eIsProxy()) {
99 var resource = referredProblem.eResource();
100 if (resource != null) {
101 referencedUri = resource.getURI();
102 }
103 }
104 for (var node : nodes) {
105 var qualifiedNameString = linkingHelper.getCrossRefNodeAsString(node, true);
106 if (Strings.isNullOrEmpty(qualifiedNameString)) {
107 continue;
108 }
109 var qualifiedName = NamingUtil.stripRootPrefix(
110 qualifiedNameConverter.toQualifiedName(qualifiedNameString));
111 var uri = referencedUri == null ? adapter.resolveQualifiedName(qualifiedName) : referencedUri;
112 if (uri != null) {
113 collection.add(NamedImport.explicit(uri, qualifiedName, List.of(alias)));
114 }
115 }
116 }
117
118 public ImportCollection getAllImports(Resource resource) {
119 return cache.get(ALL_IMPORTS_KEY, resource, () -> this.computeAllImports(resource));
120 }
121
122 protected ImportCollection computeAllImports(Resource resource) {
123 var collection = new ImportCollection();
124 collection.addAll(getDirectImports(resource).toList());
125 var loadOnDemand = loadOnDemandProvider.get();
126 loadOnDemand.setContext(resource);
127 var seen = new HashSet<URI>();
128 seen.add(resource.getURI());
129 var queue = new ArrayDeque<>(collection.toUriSet());
130 while (!queue.isEmpty()) {
131 var uri = queue.removeFirst();
132 seen.add(uri);
133 collection.add(new TransitiveImport(uri));
134 var resourceDescription = loadOnDemand.getResourceDescription(uri);
135 if (resourceDescription == null) {
136 continue;
137 }
138 var problemDescriptions = resourceDescription.getExportedObjectsByType(ProblemPackage.Literals.PROBLEM);
139 for (var eObjectDescription : problemDescriptions) {
140 for (var importedUri : getImports(eObjectDescription)) {
141 if (!seen.contains(importedUri)) {
142 queue.addLast(importedUri);
143 }
144 }
145 }
146 }
147 collection.remove(resource.getURI());
148 return collection;
149 }
150
151 protected List<URI> getImports(IEObjectDescription eObjectDescription) {
152 var importString = eObjectDescription.getUserData(ProblemResourceDescriptionStrategy.IMPORTS);
153 if (importString == null || importString.isEmpty()) {
154 return List.of();
155 }
156 return Splitter.on(ProblemResourceDescriptionStrategy.IMPORTS_SEPARATOR).splitToStream(importString)
157 .map(URI::createURI)
158 .toList();
159 }
160}