aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language/src/main/java
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2024-02-04 22:34:18 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2024-02-04 22:34:18 +0100
commitc6e70f5a01c877b560d4561f22a830c1ce1c6dbe (patch)
treeed1ea2f4ab522570d52f102ce5f90281cd17268b /subprojects/language/src/main/java
parentfeat: filesystem-level import resolution (diff)
downloadrefinery-c6e70f5a01c877b560d4561f22a830c1ce1c6dbe.tar.gz
refinery-c6e70f5a01c877b560d4561f22a830c1ce1c6dbe.tar.zst
refinery-c6e70f5a01c877b560d4561f22a830c1ce1c6dbe.zip
refactor: simplify module name inference
We never need to infer names for modules not added by import, because importing a problem (i.e., a Resource manually added into the ResourceSet) is not allowed.
Diffstat (limited to 'subprojects/language/src/main/java')
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/library/ClasspathBasedLibrary.java48
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/library/PathLibrary.java34
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibrary.java2
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapter.java84
4 files changed, 4 insertions, 164 deletions
diff --git a/subprojects/language/src/main/java/tools/refinery/language/library/ClasspathBasedLibrary.java b/subprojects/language/src/main/java/tools/refinery/language/library/ClasspathBasedLibrary.java
index 4b748c64..bc9ae5ca 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/library/ClasspathBasedLibrary.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/library/ClasspathBasedLibrary.java
@@ -14,21 +14,10 @@ import java.util.*;
14public abstract class ClasspathBasedLibrary implements RefineryLibrary { 14public abstract class ClasspathBasedLibrary implements RefineryLibrary {
15 private final QualifiedName prefix; 15 private final QualifiedName prefix;
16 private final List<QualifiedName> automaticImports; 16 private final List<QualifiedName> automaticImports;
17 private final URI rootUri;
18 17
19 protected ClasspathBasedLibrary(QualifiedName prefix, List<QualifiedName> automaticImports) { 18 protected ClasspathBasedLibrary(QualifiedName prefix, List<QualifiedName> automaticImports) {
20 this.prefix = prefix; 19 this.prefix = prefix;
21 this.automaticImports = List.copyOf(automaticImports); 20 this.automaticImports = List.copyOf(automaticImports);
22 var context = this.getClass();
23 var contextPath = context.getCanonicalName().replace('.', '/') + ".class";
24 var contextResource = context.getClassLoader().getResource(contextPath);
25 if (contextResource == null) {
26 throw new IllegalStateException("Failed to find library context");
27 }
28 var contextUri = URI.createURI(contextResource.toString());
29 var segments = Arrays.copyOf(contextUri.segments(), contextUri.segmentCount() - 1);
30 rootUri = URI.createHierarchicalURI(contextUri.scheme(), contextUri.authority(), contextUri.device(),
31 segments, null, null);
32 } 21 }
33 22
34 protected ClasspathBasedLibrary(QualifiedName prefix) { 23 protected ClasspathBasedLibrary(QualifiedName prefix) {
@@ -48,43 +37,6 @@ public abstract class ClasspathBasedLibrary implements RefineryLibrary {
48 return Optional.empty(); 37 return Optional.empty();
49 } 38 }
50 39
51 @Override
52 public Optional<QualifiedName> getQualifiedName(URI uri, List<Path> libraryPaths) {
53 if (!uri.isHierarchical() ||
54 !Objects.equals(rootUri.scheme(), uri.scheme()) ||
55 !Objects.equals(rootUri.authority(), uri.authority()) ||
56 !Objects.equals(rootUri.device(), uri.device()) ||
57 rootUri.segmentCount() >= uri.segmentCount()) {
58 return Optional.empty();
59 }
60 int rootSegmentCount = rootUri.segmentCount();
61 int uriSegmentCount = uri.segmentCount();
62 if (!uri.segment(uriSegmentCount - 1).endsWith(RefineryLibrary.EXTENSION)) {
63 return Optional.empty();
64 }
65 var segments = new ArrayList<String>();
66 int i = 0;
67 while (i < rootSegmentCount) {
68 if (!rootUri.segment(i).equals(uri.segment(i))) {
69 return Optional.empty();
70 }
71 i++;
72 }
73 while (i < uriSegmentCount) {
74 var segment = uri.segment(i);
75 if (i == uriSegmentCount - 1) {
76 segment = segment.substring(0, segment.length() - RefineryLibrary.EXTENSION.length());
77 }
78 segments.add(segment);
79 i++;
80 }
81 var qualifiedName = QualifiedName.create(segments);
82 if (!qualifiedName.startsWith(prefix)) {
83 return Optional.empty();
84 }
85 return Optional.of(qualifiedName);
86 }
87
88 public static Optional<URI> getLibraryUri(Class<?> context, QualifiedName qualifiedName) { 40 public static Optional<URI> getLibraryUri(Class<?> context, QualifiedName qualifiedName) {
89 var packagePath = context.getPackageName().replace('.', '/'); 41 var packagePath = context.getPackageName().replace('.', '/');
90 var libraryPath = String.join("/", qualifiedName.getSegments()); 42 var libraryPath = String.join("/", qualifiedName.getSegments());
diff --git a/subprojects/language/src/main/java/tools/refinery/language/library/PathLibrary.java b/subprojects/language/src/main/java/tools/refinery/language/library/PathLibrary.java
index c6f994df..6f418492 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/library/PathLibrary.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/library/PathLibrary.java
@@ -10,7 +10,6 @@ import org.eclipse.xtext.naming.QualifiedName;
10 10
11import java.nio.file.Files; 11import java.nio.file.Files;
12import java.nio.file.Path; 12import java.nio.file.Path;
13import java.util.ArrayList;
14import java.util.List; 13import java.util.List;
15import java.util.Optional; 14import java.util.Optional;
16 15
@@ -54,37 +53,4 @@ public final class PathLibrary implements RefineryLibrary {
54 } 53 }
55 return Path.of(first, rest); 54 return Path.of(first, rest);
56 } 55 }
57
58 @Override
59 public Optional<QualifiedName> getQualifiedName(URI uri, List<Path> libraryPaths) {
60 if (libraryPaths.isEmpty()) {
61 return Optional.empty();
62 }
63 if (!uri.isFile() || !uri.hasAbsolutePath()) {
64 return Optional.empty();
65 }
66 var path = Path.of(uri.toFileString());
67 for (var library : libraryPaths) {
68 if (path.startsWith(library)) {
69 return getRelativeQualifiedName(library, path);
70 }
71 }
72 return Optional.empty();
73 }
74
75 private static Optional<QualifiedName> getRelativeQualifiedName(Path library, Path path) {
76 var relativePath = path.relativize(library);
77 var segments = new ArrayList<String>();
78 for (Path value : relativePath) {
79 segments.add(value.toString());
80 }
81 int lastIndex = segments.size() - 1;
82 var lastSegment = segments.get(lastIndex);
83 if (!lastSegment.endsWith(EXTENSION)) {
84 return Optional.empty();
85 }
86 lastSegment = lastSegment.substring(0, lastSegment.length() - RefineryLibrary.EXTENSION.length());
87 segments.set(lastIndex, lastSegment);
88 return Optional.of(QualifiedName.create(segments));
89 }
90} 56}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibrary.java b/subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibrary.java
index e1f8d7bc..9c0d72f9 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibrary.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibrary.java
@@ -20,6 +20,4 @@ public interface RefineryLibrary {
20 } 20 }
21 21
22 Optional<URI> resolveQualifiedName(QualifiedName qualifiedName, List<Path> libraryPaths); 22 Optional<URI> resolveQualifiedName(QualifiedName qualifiedName, List<Path> libraryPaths);
23
24 Optional<QualifiedName> getQualifiedName(URI uri, List<Path> libraryPaths);
25} 23}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapter.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapter.java
index 5a8f7fd7..a34c09cc 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapter.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapter.java
@@ -8,12 +8,9 @@ package tools.refinery.language.scoping.imports;
8import com.google.common.base.Splitter; 8import com.google.common.base.Splitter;
9import com.google.common.cache.Cache; 9import com.google.common.cache.Cache;
10import com.google.common.cache.CacheBuilder; 10import com.google.common.cache.CacheBuilder;
11import org.apache.log4j.Logger;
12import org.eclipse.emf.common.notify.Notification;
13import org.eclipse.emf.common.notify.impl.AdapterImpl; 11import org.eclipse.emf.common.notify.impl.AdapterImpl;
14import org.eclipse.emf.common.util.URI; 12import org.eclipse.emf.common.util.URI;
15import org.eclipse.emf.ecore.EObject; 13import org.eclipse.emf.ecore.EObject;
16import org.eclipse.emf.ecore.resource.Resource;
17import org.eclipse.emf.ecore.resource.ResourceSet; 14import org.eclipse.emf.ecore.resource.ResourceSet;
18import org.eclipse.emf.ecore.util.EcoreUtil; 15import org.eclipse.emf.ecore.util.EcoreUtil;
19import org.eclipse.xtext.naming.QualifiedName; 16import org.eclipse.xtext.naming.QualifiedName;
@@ -24,7 +21,6 @@ import java.nio.file.Path;
24import java.util.*; 21import java.util.*;
25 22
26public class ImportAdapter extends AdapterImpl { 23public class ImportAdapter extends AdapterImpl {
27 private static final Logger LOG = Logger.getLogger(ImportAdapter.class);
28 private static final List<RefineryLibrary> DEFAULT_LIBRARIES; 24 private static final List<RefineryLibrary> DEFAULT_LIBRARIES;
29 private static final List<Path> DEFAULT_PATHS; 25 private static final List<Path> DEFAULT_PATHS;
30 26
@@ -53,12 +49,9 @@ public class ImportAdapter extends AdapterImpl {
53 private final Map<QualifiedName, URI> qualifiedNameToUriMap = new LinkedHashMap<>(); 49 private final Map<QualifiedName, URI> qualifiedNameToUriMap = new LinkedHashMap<>();
54 private final Map<URI, QualifiedName> uriToQualifiedNameMap = new LinkedHashMap<>(); 50 private final Map<URI, QualifiedName> uriToQualifiedNameMap = new LinkedHashMap<>();
55 51
56 private ImportAdapter(ResourceSet resourceSet) { 52 private ImportAdapter() {
57 libraries = new ArrayList<>(DEFAULT_LIBRARIES); 53 libraries = new ArrayList<>(DEFAULT_LIBRARIES);
58 libraryPaths = new ArrayList<>(DEFAULT_PATHS); 54 libraryPaths = new ArrayList<>(DEFAULT_PATHS);
59 for (var resource : resourceSet.getResources()) {
60 resourceAdded(resource);
61 }
62 } 55 }
63 56
64 @Override 57 @Override
@@ -120,81 +113,10 @@ public class ImportAdapter extends AdapterImpl {
120 return uriToQualifiedNameMap.get(uri); 113 return uriToQualifiedNameMap.get(uri);
121 } 114 }
122 115
123 @Override
124 public void notifyChanged(Notification msg) {
125 switch (msg.getEventType()) {
126 case Notification.ADD -> {
127 if (msg.getNewValue() instanceof Resource resource) {
128 resourceAdded(resource);
129 }
130 }
131 case Notification.ADD_MANY -> {
132 if (msg.getNewValue() instanceof List<?> list) {
133 manyResourcesAdded(list);
134 }
135 }
136 case Notification.REMOVE -> {
137 if (msg.getOldValue() instanceof Resource resource) {
138 resourceRemoved(resource);
139 }
140 }
141 case Notification.REMOVE_MANY -> {
142 if (msg.getOldValue() instanceof List<?> list) {
143 manyResourcesRemoved(list);
144 }
145 }
146 default -> {
147 // Nothing to update.
148 }
149 }
150 }
151
152 private void manyResourcesAdded(List<?> list) {
153 for (var element : list) {
154 if (element instanceof Resource resource) {
155 resourceAdded(resource);
156 }
157 }
158 }
159
160 private void manyResourcesRemoved(List<?> list) {
161 for (var element : list) {
162 if (element instanceof Resource resource) {
163 resourceRemoved(resource);
164 }
165 }
166 }
167
168 private void resourceAdded(Resource resource) {
169 var uri = resource.getURI();
170 for (var library : libraries) {
171 var result = library.getQualifiedName(uri, libraryPaths);
172 if (result.isPresent()) {
173 var qualifiedName = result.get();
174 var previousQualifiedName = uriToQualifiedNameMap.putIfAbsent(uri, qualifiedName);
175 if (previousQualifiedName == null) {
176 if (qualifiedNameToUriMap.put(qualifiedName, uri) != null) {
177 throw new IllegalArgumentException("Duplicate resource for" + qualifiedName);
178 }
179 } else if (!previousQualifiedName.equals(qualifiedName)) {
180 LOG.warn("Expected %s to have qualified name %s, got %s instead".formatted(
181 uri, previousQualifiedName, qualifiedName));
182 }
183 }
184 }
185 }
186
187 private void resourceRemoved(Resource resource) {
188 var qualifiedName = uriToQualifiedNameMap.remove(resource.getURI());
189 if (qualifiedName != null) {
190 qualifiedNameToUriMap.remove(qualifiedName);
191 }
192 }
193
194 public static ImportAdapter getOrInstall(ResourceSet resourceSet) { 116 public static ImportAdapter getOrInstall(ResourceSet resourceSet) {
195 var adapter = getAdapter(resourceSet); 117 var adapter = getAdapter(resourceSet);
196 if (adapter == null) { 118 if (adapter == null) {
197 adapter = new ImportAdapter(resourceSet); 119 adapter = new ImportAdapter();
198 resourceSet.eAdapters().add(adapter); 120 resourceSet.eAdapters().add(adapter);
199 } 121 }
200 return adapter; 122 return adapter;
@@ -226,5 +148,7 @@ public class ImportAdapter extends AdapterImpl {
226 newAdapter.libraries.addAll(originalAdapter.libraries); 148 newAdapter.libraries.addAll(originalAdapter.libraries);
227 newAdapter.libraryPaths.clear(); 149 newAdapter.libraryPaths.clear();
228 newAdapter.libraryPaths.addAll(originalAdapter.libraryPaths); 150 newAdapter.libraryPaths.addAll(originalAdapter.libraryPaths);
151 newAdapter.uriToQualifiedNameMap.putAll(originalAdapter.uriToQualifiedNameMap);
152 newAdapter.qualifiedNameToUriMap.putAll(originalAdapter.qualifiedNameToUriMap);
229 } 153 }
230} 154}