aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language/src/main/java/tools/refinery/language/library
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/language/src/main/java/tools/refinery/language/library')
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/library/BuiltinLibrary.java27
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/library/ClasspathBasedLibrary.java92
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/library/PathLibrary.java90
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibrary.java26
4 files changed, 235 insertions, 0 deletions
diff --git a/subprojects/language/src/main/java/tools/refinery/language/library/BuiltinLibrary.java b/subprojects/language/src/main/java/tools/refinery/language/library/BuiltinLibrary.java
new file mode 100644
index 00000000..ce80504e
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/library/BuiltinLibrary.java
@@ -0,0 +1,27 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.library;
7
8import org.eclipse.emf.common.util.URI;
9import org.eclipse.xtext.naming.QualifiedName;
10
11import java.util.List;
12
13public class BuiltinLibrary extends ClasspathBasedLibrary {
14 public static final QualifiedName BUILTIN_LIBRARY_NAME = QualifiedName.create("builtin");
15 public static final URI BUILTIN_LIBRARY_URI = ClasspathBasedLibrary.getLibraryUri(
16 BuiltinLibrary.class, BUILTIN_LIBRARY_NAME).orElseThrow(
17 () -> new IllegalStateException("Builtin library was not found"));
18
19 public BuiltinLibrary() {
20 super(BUILTIN_LIBRARY_NAME);
21 }
22
23 @Override
24 public List<QualifiedName> getAutomaticImports() {
25 return List.of(BUILTIN_LIBRARY_NAME);
26 }
27}
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
new file mode 100644
index 00000000..56bb8b96
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/library/ClasspathBasedLibrary.java
@@ -0,0 +1,92 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.library;
7
8import org.eclipse.emf.common.util.URI;
9import org.eclipse.xtext.naming.QualifiedName;
10
11import java.nio.file.Path;
12import java.util.*;
13
14public abstract class ClasspathBasedLibrary implements RefineryLibrary {
15 private final Class<?> context;
16 private final QualifiedName prefix;
17 private final URI rootUri;
18
19 protected ClasspathBasedLibrary(Class<?> context, QualifiedName prefix) {
20 this.context = context == null ? getClass() : context;
21 this.prefix = prefix;
22 var contextPath = this.context.getCanonicalName().replace('.', '/') + ".class";
23 var contextResource = this.context.getClassLoader().getResource(contextPath);
24 if (contextResource == null) {
25 throw new IllegalStateException("Failed to find library context");
26 }
27 var contextUri = URI.createURI(contextResource.toString());
28 var segments = Arrays.copyOf(contextUri.segments(), contextUri.segmentCount() - 1);
29 rootUri = URI.createHierarchicalURI(contextUri.scheme(), contextUri.authority(), contextUri.device(),
30 segments, null, null);
31 }
32
33 protected ClasspathBasedLibrary(QualifiedName prefix) {
34 this(null, prefix);
35 }
36
37 @Override
38 public Optional<URI> resolveQualifiedName(QualifiedName qualifiedName, List<Path> libraryPaths) {
39 if (qualifiedName.startsWith(prefix)) {
40 return getLibraryUri(context, qualifiedName);
41 }
42 return Optional.empty();
43 }
44
45 @Override
46 public Optional<QualifiedName> getQualifiedName(URI uri, List<Path> libraryPaths) {
47 if (!uri.isHierarchical() ||
48 !Objects.equals(rootUri.scheme(), uri.scheme()) ||
49 !Objects.equals(rootUri.authority(), uri.authority()) ||
50 !Objects.equals(rootUri.device(), uri.device()) ||
51 rootUri.segmentCount() >= uri.segmentCount()) {
52 return Optional.empty();
53 }
54 int rootSegmentCount = rootUri.segmentCount();
55 int uriSegmentCount = uri.segmentCount();
56 if (!uri.segment(uriSegmentCount - 1).endsWith(RefineryLibrary.FILE_NAME_SUFFIX)) {
57 return Optional.empty();
58 }
59 var segments = new ArrayList<String>();
60 int i = 0;
61 while (i < rootSegmentCount) {
62 if (!rootUri.segment(i).equals(uri.segment(i))) {
63 return Optional.empty();
64 }
65 i++;
66 }
67 while (i < uriSegmentCount) {
68 var segment = uri.segment(i);
69 if (i == uriSegmentCount - 1) {
70 segment = segment.substring(0, segment.length() - RefineryLibrary.FILE_NAME_SUFFIX.length());
71 }
72 segments.add(segment);
73 i++;
74 }
75 var qualifiedName = QualifiedName.create(segments);
76 if (!qualifiedName.startsWith(prefix)) {
77 return Optional.empty();
78 }
79 return Optional.of(qualifiedName);
80 }
81
82 public static Optional<URI> getLibraryUri(Class<?> context, QualifiedName qualifiedName) {
83 var packagePath = context.getPackageName().replace('.', '/');
84 var libraryPath = String.join("/", qualifiedName.getSegments());
85 var resourceName = "%s/%s%s".formatted(packagePath, libraryPath, RefineryLibrary.FILE_NAME_SUFFIX);
86 var resource = context.getClassLoader().getResource(resourceName);
87 if (resource == null) {
88 return Optional.empty();
89 }
90 return Optional.of(URI.createURI(resource.toString()));
91 }
92}
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
new file mode 100644
index 00000000..8eaf8458
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/library/PathLibrary.java
@@ -0,0 +1,90 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.library;
7
8import org.eclipse.emf.common.util.URI;
9import org.eclipse.xtext.naming.QualifiedName;
10
11import java.nio.file.Files;
12import java.nio.file.Path;
13import java.util.ArrayList;
14import java.util.List;
15import java.util.Optional;
16
17public final class PathLibrary implements RefineryLibrary {
18 @Override
19 public Optional<URI> resolveQualifiedName(QualifiedName qualifiedName, List<Path> libraryPaths) {
20 if (libraryPaths.isEmpty()) {
21 return Optional.empty();
22 }
23 if (qualifiedName.getSegmentCount() == 0) {
24 return Optional.empty();
25 }
26 var relativePath = qualifiedNameToRelativePath(qualifiedName);
27 for (var library : libraryPaths) {
28 var absoluteResolvedPath = library.resolve(relativePath).toAbsolutePath().normalize();
29 if (absoluteResolvedPath.startsWith(library) && Files.exists(absoluteResolvedPath)) {
30 var uri = URI.createFileURI(absoluteResolvedPath.toString());
31 return Optional.of(uri);
32 }
33 }
34 return Optional.empty();
35 }
36
37 private static Path qualifiedNameToRelativePath(QualifiedName qualifiedName) {
38 int segmentCount = qualifiedName.getSegmentCount();
39 String first = null;
40 var rest = new String[segmentCount - 1];
41 for (var i = 0; i < segmentCount; i++) {
42 var segment = qualifiedName.getSegment(i);
43 if (i == segmentCount - 1) {
44 segment = segment + RefineryLibrary.FILE_NAME_SUFFIX;
45 }
46 if (i == 0) {
47 first = segment;
48 } else {
49 rest[i - 1] = segment;
50 }
51 }
52 if (first == null) {
53 throw new AssertionError("Expected qualified name to have non-null segments");
54 }
55 return Path.of(first, rest);
56 }
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(FILE_NAME_SUFFIX)) {
84 return Optional.empty();
85 }
86 lastSegment = lastSegment.substring(0, lastSegment.length() - RefineryLibrary.FILE_NAME_SUFFIX.length());
87 segments.set(lastIndex, lastSegment);
88 return Optional.of(QualifiedName.create(segments));
89 }
90}
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
new file mode 100644
index 00000000..2559749a
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibrary.java
@@ -0,0 +1,26 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.library;
7
8import org.eclipse.emf.common.util.URI;
9import org.eclipse.xtext.naming.QualifiedName;
10import tools.refinery.language.utils.ProblemUtil;
11
12import java.nio.file.Path;
13import java.util.List;
14import java.util.Optional;
15
16public interface RefineryLibrary {
17 String FILE_NAME_SUFFIX = "." + ProblemUtil.MODULE_EXTENSION;
18
19 default List<QualifiedName> getAutomaticImports() {
20 return List.of();
21 }
22
23 Optional<URI> resolveQualifiedName(QualifiedName qualifiedName, List<Path> libraryPaths);
24
25 Optional<QualifiedName> getQualifiedName(URI uri, List<Path> libraryPaths);
26}