diff options
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.java | 160 |
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 | */ | ||
6 | package tools.refinery.language.scoping.imports; | ||
7 | |||
8 | import com.google.common.base.Splitter; | ||
9 | import com.google.common.base.Strings; | ||
10 | import com.google.inject.Inject; | ||
11 | import com.google.inject.Provider; | ||
12 | import com.google.inject.Singleton; | ||
13 | import org.eclipse.emf.common.util.URI; | ||
14 | import org.eclipse.emf.ecore.EObject; | ||
15 | import org.eclipse.emf.ecore.resource.Resource; | ||
16 | import org.eclipse.xtext.linking.impl.LinkingHelper; | ||
17 | import org.eclipse.xtext.naming.IQualifiedNameConverter; | ||
18 | import org.eclipse.xtext.naming.QualifiedName; | ||
19 | import org.eclipse.xtext.nodemodel.util.NodeModelUtils; | ||
20 | import org.eclipse.xtext.resource.IEObjectDescription; | ||
21 | import org.eclipse.xtext.util.IResourceScopeCache; | ||
22 | import tools.refinery.language.model.problem.ImportStatement; | ||
23 | import tools.refinery.language.model.problem.Problem; | ||
24 | import tools.refinery.language.model.problem.ProblemPackage; | ||
25 | import tools.refinery.language.naming.NamingUtil; | ||
26 | import tools.refinery.language.resource.LoadOnDemandResourceDescriptionProvider; | ||
27 | import tools.refinery.language.resource.ProblemResourceDescriptionStrategy; | ||
28 | |||
29 | import java.util.*; | ||
30 | |||
31 | @Singleton | ||
32 | public 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 | } | ||