From 48c9f84f10d5fbcbe399a0c3335707167f4e7f0d Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Mon, 5 Feb 2024 15:18:02 +0100 Subject: Revert "refactor: simplify module name inference" This reverts commit c6e70f5a01c877b560d4561f22a830c1ce1c6dbe. --- .../language/library/ClasspathBasedLibrary.java | 48 +++++++++++++ .../refinery/language/library/PathLibrary.java | 34 +++++++++ .../refinery/language/library/RefineryLibrary.java | 2 + .../language/scoping/imports/ImportAdapter.java | 84 ++++++++++++++++++++-- 4 files changed, 164 insertions(+), 4 deletions(-) (limited to 'subprojects') 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 bc9ae5ca..4b748c64 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,10 +14,21 @@ import java.util.*; public abstract class ClasspathBasedLibrary implements RefineryLibrary { private final QualifiedName prefix; private final List automaticImports; + private final URI rootUri; protected ClasspathBasedLibrary(QualifiedName prefix, List automaticImports) { this.prefix = prefix; this.automaticImports = List.copyOf(automaticImports); + var context = this.getClass(); + var contextPath = context.getCanonicalName().replace('.', '/') + ".class"; + var contextResource = context.getClassLoader().getResource(contextPath); + if (contextResource == null) { + throw new IllegalStateException("Failed to find library context"); + } + var contextUri = URI.createURI(contextResource.toString()); + var segments = Arrays.copyOf(contextUri.segments(), contextUri.segmentCount() - 1); + rootUri = URI.createHierarchicalURI(contextUri.scheme(), contextUri.authority(), contextUri.device(), + segments, null, null); } protected ClasspathBasedLibrary(QualifiedName prefix) { @@ -37,6 +48,43 @@ public abstract class ClasspathBasedLibrary implements RefineryLibrary { return Optional.empty(); } + @Override + public Optional getQualifiedName(URI uri, List libraryPaths) { + if (!uri.isHierarchical() || + !Objects.equals(rootUri.scheme(), uri.scheme()) || + !Objects.equals(rootUri.authority(), uri.authority()) || + !Objects.equals(rootUri.device(), uri.device()) || + rootUri.segmentCount() >= uri.segmentCount()) { + return Optional.empty(); + } + int rootSegmentCount = rootUri.segmentCount(); + int uriSegmentCount = uri.segmentCount(); + if (!uri.segment(uriSegmentCount - 1).endsWith(RefineryLibrary.EXTENSION)) { + return Optional.empty(); + } + var segments = new ArrayList(); + int i = 0; + while (i < rootSegmentCount) { + if (!rootUri.segment(i).equals(uri.segment(i))) { + return Optional.empty(); + } + i++; + } + while (i < uriSegmentCount) { + var segment = uri.segment(i); + if (i == uriSegmentCount - 1) { + segment = segment.substring(0, segment.length() - RefineryLibrary.EXTENSION.length()); + } + segments.add(segment); + i++; + } + var qualifiedName = QualifiedName.create(segments); + if (!qualifiedName.startsWith(prefix)) { + return Optional.empty(); + } + return Optional.of(qualifiedName); + } + public static Optional getLibraryUri(Class context, QualifiedName qualifiedName) { var packagePath = context.getPackageName().replace('.', '/'); 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 6f418492..c6f994df 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,6 +10,7 @@ import org.eclipse.xtext.naming.QualifiedName; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -53,4 +54,37 @@ public final class PathLibrary implements RefineryLibrary { } return Path.of(first, rest); } + + @Override + public Optional getQualifiedName(URI uri, List libraryPaths) { + if (libraryPaths.isEmpty()) { + return Optional.empty(); + } + if (!uri.isFile() || !uri.hasAbsolutePath()) { + return Optional.empty(); + } + var path = Path.of(uri.toFileString()); + for (var library : libraryPaths) { + if (path.startsWith(library)) { + return getRelativeQualifiedName(library, path); + } + } + return Optional.empty(); + } + + private static Optional getRelativeQualifiedName(Path library, Path path) { + var relativePath = path.relativize(library); + var segments = new ArrayList(); + for (Path value : relativePath) { + segments.add(value.toString()); + } + int lastIndex = segments.size() - 1; + var lastSegment = segments.get(lastIndex); + if (!lastSegment.endsWith(EXTENSION)) { + return Optional.empty(); + } + lastSegment = lastSegment.substring(0, lastSegment.length() - RefineryLibrary.EXTENSION.length()); + segments.set(lastIndex, lastSegment); + return Optional.of(QualifiedName.create(segments)); + } } 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 9c0d72f9..e1f8d7bc 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,4 +20,6 @@ public interface RefineryLibrary { } Optional resolveQualifiedName(QualifiedName qualifiedName, List libraryPaths); + + Optional getQualifiedName(URI uri, List libraryPaths); } 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 a34c09cc..5a8f7fd7 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,9 +8,12 @@ package tools.refinery.language.scoping.imports; import com.google.common.base.Splitter; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; +import org.apache.log4j.Logger; +import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtext.naming.QualifiedName; @@ -21,6 +24,7 @@ import java.nio.file.Path; import java.util.*; public class ImportAdapter extends AdapterImpl { + private static final Logger LOG = Logger.getLogger(ImportAdapter.class); private static final List DEFAULT_LIBRARIES; private static final List DEFAULT_PATHS; @@ -49,9 +53,12 @@ public class ImportAdapter extends AdapterImpl { private final Map qualifiedNameToUriMap = new LinkedHashMap<>(); private final Map uriToQualifiedNameMap = new LinkedHashMap<>(); - private ImportAdapter() { + private ImportAdapter(ResourceSet resourceSet) { libraries = new ArrayList<>(DEFAULT_LIBRARIES); libraryPaths = new ArrayList<>(DEFAULT_PATHS); + for (var resource : resourceSet.getResources()) { + resourceAdded(resource); + } } @Override @@ -113,10 +120,81 @@ public class ImportAdapter extends AdapterImpl { return uriToQualifiedNameMap.get(uri); } + @Override + public void notifyChanged(Notification msg) { + switch (msg.getEventType()) { + case Notification.ADD -> { + if (msg.getNewValue() instanceof Resource resource) { + resourceAdded(resource); + } + } + case Notification.ADD_MANY -> { + if (msg.getNewValue() instanceof List list) { + manyResourcesAdded(list); + } + } + case Notification.REMOVE -> { + if (msg.getOldValue() instanceof Resource resource) { + resourceRemoved(resource); + } + } + case Notification.REMOVE_MANY -> { + if (msg.getOldValue() instanceof List list) { + manyResourcesRemoved(list); + } + } + default -> { + // Nothing to update. + } + } + } + + private void manyResourcesAdded(List list) { + for (var element : list) { + if (element instanceof Resource resource) { + resourceAdded(resource); + } + } + } + + private void manyResourcesRemoved(List list) { + for (var element : list) { + if (element instanceof Resource resource) { + resourceRemoved(resource); + } + } + } + + private void resourceAdded(Resource resource) { + var uri = resource.getURI(); + for (var library : libraries) { + var result = library.getQualifiedName(uri, libraryPaths); + if (result.isPresent()) { + var qualifiedName = result.get(); + var previousQualifiedName = uriToQualifiedNameMap.putIfAbsent(uri, qualifiedName); + if (previousQualifiedName == null) { + if (qualifiedNameToUriMap.put(qualifiedName, uri) != null) { + throw new IllegalArgumentException("Duplicate resource for" + qualifiedName); + } + } else if (!previousQualifiedName.equals(qualifiedName)) { + LOG.warn("Expected %s to have qualified name %s, got %s instead".formatted( + uri, previousQualifiedName, qualifiedName)); + } + } + } + } + + private void resourceRemoved(Resource resource) { + var qualifiedName = uriToQualifiedNameMap.remove(resource.getURI()); + if (qualifiedName != null) { + qualifiedNameToUriMap.remove(qualifiedName); + } + } + public static ImportAdapter getOrInstall(ResourceSet resourceSet) { var adapter = getAdapter(resourceSet); if (adapter == null) { - adapter = new ImportAdapter(); + adapter = new ImportAdapter(resourceSet); resourceSet.eAdapters().add(adapter); } return adapter; @@ -148,7 +226,5 @@ public class ImportAdapter extends AdapterImpl { newAdapter.libraries.addAll(originalAdapter.libraries); newAdapter.libraryPaths.clear(); newAdapter.libraryPaths.addAll(originalAdapter.libraryPaths); - newAdapter.uriToQualifiedNameMap.putAll(originalAdapter.uriToQualifiedNameMap); - newAdapter.qualifiedNameToUriMap.putAll(originalAdapter.qualifiedNameToUriMap); } } -- cgit v1.2.3-70-g09d2