aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects')
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java230
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java1
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/library/BuiltinLibrary.java41
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibraries.java54
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibrary.java20
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/LoadOnDemandResourceDescriptionProvider.java49
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java12
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/state/DerivedVariableComputer.java (renamed from subprojects/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java)2
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/state/ImplicitVariableScope.java (renamed from subprojects/language/src/main/java/tools/refinery/language/resource/ImplicitVariableScope.java)2
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/state/NodeNameCollector.java (renamed from subprojects/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java)2
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java (renamed from subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java)2
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/CompositeSelectable.java62
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/NormalizedSelectable.java37
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java84
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java65
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/imports/Import.java12
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollection.java76
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java138
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/imports/NamedImport.java22
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/imports/TransitiveImport.java11
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java7
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java16
-rw-r--r--subprojects/language/src/main/resources/META-INF/services/tools.refinery.language.library.RefineryLibrary4
-rw-r--r--subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery (renamed from subprojects/language/src/main/resources/tools/refinery/language/builtin.problem)0
24 files changed, 779 insertions, 170 deletions
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java
index 7c649232..c2bca2a5 100644
--- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java
@@ -6,7 +6,9 @@
6package tools.refinery.language.semantics; 6package tools.refinery.language.semantics;
7 7
8import com.google.inject.Inject; 8import com.google.inject.Inject;
9import tools.refinery.language.library.BuiltinLibrary;
9import tools.refinery.language.model.problem.*; 10import tools.refinery.language.model.problem.*;
11import tools.refinery.language.scoping.imports.ImportCollector;
10import tools.refinery.language.semantics.internal.MutableSeed; 12import tools.refinery.language.semantics.internal.MutableSeed;
11import tools.refinery.language.utils.BuiltinSymbols; 13import tools.refinery.language.utils.BuiltinSymbols;
12import tools.refinery.language.utils.ProblemDesugarer; 14import tools.refinery.language.utils.ProblemDesugarer;
@@ -51,8 +53,13 @@ public class ModelInitializer {
51 @Inject 53 @Inject
52 private ProblemTraceImpl problemTrace; 54 private ProblemTraceImpl problemTrace;
53 55
56 @Inject
57 private ImportCollector importCollector;
58
54 private Problem problem; 59 private Problem problem;
55 60
61 private final Set<Problem> importedProblems = new HashSet<>();
62
56 private BuiltinSymbols builtinSymbols; 63 private BuiltinSymbols builtinSymbols;
57 64
58 private PartialRelation nodeRelation; 65 private PartialRelation nodeRelation;
@@ -82,6 +89,8 @@ public class ModelInitializer {
82 throw new IllegalArgumentException("Problem was already set"); 89 throw new IllegalArgumentException("Problem was already set");
83 } 90 }
84 this.problem = problem; 91 this.problem = problem;
92 loadImportedProblems();
93 importedProblems.add(problem);
85 problemTrace.setProblem(problem); 94 problemTrace.setProblem(problem);
86 try { 95 try {
87 builtinSymbols = desugarer.getBuiltinSymbols(problem).orElseThrow(() -> new IllegalArgumentException( 96 builtinSymbols = desugarer.getBuiltinSymbols(problem).orElseThrow(() -> new IllegalArgumentException(
@@ -128,6 +137,29 @@ public class ModelInitializer {
128 } 137 }
129 } 138 }
130 139
140 private void loadImportedProblems() {
141 var resource = problem.eResource();
142 if (resource == null) {
143 return;
144 }
145 var resourceSet = resource.getResourceSet();
146 if (resourceSet == null) {
147 return;
148 }
149 var importedUris = importCollector.getAllImports(resource).toUriSet();
150 for (var uri : importedUris) {
151 if (BuiltinLibrary.BUILTIN_LIBRARY_URI.equals(uri)) {
152 // We hard-code the behavior of the builtin library.
153 continue;
154 }
155 var importedResource = resourceSet.getResource(uri, false);
156 if (importedResource != null && !importedResource.getContents().isEmpty() &&
157 importedResource.getContents().getFirst() instanceof Problem importedProblem) {
158 importedProblems.add(importedProblem);
159 }
160 }
161 }
162
131 public void configureStoreBuilder(ModelStoreBuilder storeBuilder) { 163 public void configureStoreBuilder(ModelStoreBuilder storeBuilder) {
132 checkProblem(); 164 checkProblem();
133 try { 165 try {
@@ -168,19 +200,21 @@ public class ModelInitializer {
168 } 200 }
169 201
170 private void collectNodes() { 202 private void collectNodes() {
171 for (var statement : problem.getStatements()) { 203 for (var importedProblem : importedProblems) {
172 if (statement instanceof NodeDeclaration nodeDeclaration) { 204 for (var statement : importedProblem.getStatements()) {
173 for (var node : nodeDeclaration.getNodes()) { 205 if (statement instanceof NodeDeclaration nodeDeclaration) {
174 collectNode(node); 206 for (var node : nodeDeclaration.getNodes()) {
175 } 207 collectNode(node);
176 } else if (statement instanceof ClassDeclaration classDeclaration) { 208 }
177 var newNode = classDeclaration.getNewNode(); 209 } else if (statement instanceof ClassDeclaration classDeclaration) {
178 if (newNode != null) { 210 var newNode = classDeclaration.getNewNode();
179 collectNode(newNode); 211 if (newNode != null) {
180 } 212 collectNode(newNode);
181 } else if (statement instanceof EnumDeclaration enumDeclaration) { 213 }
182 for (var literal : enumDeclaration.getLiterals()) { 214 } else if (statement instanceof EnumDeclaration enumDeclaration) {
183 collectNode(literal); 215 for (var literal : enumDeclaration.getLiterals()) {
216 collectNode(literal);
217 }
184 } 218 }
185 } 219 }
186 } 220 }
@@ -194,13 +228,15 @@ public class ModelInitializer {
194 } 228 }
195 229
196 private void collectPartialSymbols() { 230 private void collectPartialSymbols() {
197 for (var statement : problem.getStatements()) { 231 for (var importedProblem : importedProblems) {
198 if (statement instanceof ClassDeclaration classDeclaration) { 232 for (var statement : importedProblem.getStatements()) {
199 collectClassDeclarationSymbols(classDeclaration); 233 if (statement instanceof ClassDeclaration classDeclaration) {
200 } else if (statement instanceof EnumDeclaration enumDeclaration) { 234 collectClassDeclarationSymbols(classDeclaration);
201 collectPartialRelation(enumDeclaration, 1, TruthValue.FALSE, TruthValue.FALSE); 235 } else if (statement instanceof EnumDeclaration enumDeclaration) {
202 } else if (statement instanceof PredicateDefinition predicateDefinition) { 236 collectPartialRelation(enumDeclaration, 1, TruthValue.FALSE, TruthValue.FALSE);
203 collectPredicateDefinitionSymbol(predicateDefinition); 237 } else if (statement instanceof PredicateDefinition predicateDefinition) {
238 collectPredicateDefinitionSymbol(predicateDefinition);
239 }
204 } 240 }
205 } 241 }
206 } 242 }
@@ -251,11 +287,13 @@ public class ModelInitializer {
251 } 287 }
252 288
253 private void collectMetamodel() { 289 private void collectMetamodel() {
254 for (var statement : problem.getStatements()) { 290 for (var importedProblem : importedProblems) {
255 if (statement instanceof ClassDeclaration classDeclaration) { 291 for (var statement : importedProblem.getStatements()) {
256 collectClassDeclarationMetamodel(classDeclaration); 292 if (statement instanceof ClassDeclaration classDeclaration) {
257 } else if (statement instanceof EnumDeclaration enumDeclaration) { 293 collectClassDeclarationMetamodel(classDeclaration);
258 collectEnumMetamodel(enumDeclaration); 294 } else if (statement instanceof EnumDeclaration enumDeclaration) {
295 collectEnumMetamodel(enumDeclaration);
296 }
259 } 297 }
260 } 298 }
261 } 299 }
@@ -350,15 +388,17 @@ public class ModelInitializer {
350 } 388 }
351 389
352 private void collectAssertions() { 390 private void collectAssertions() {
353 for (var statement : problem.getStatements()) { 391 for (var importedProblem : importedProblems) {
354 if (statement instanceof ClassDeclaration classDeclaration) { 392 for (var statement : importedProblem.getStatements()) {
355 collectClassDeclarationAssertions(classDeclaration); 393 if (statement instanceof ClassDeclaration classDeclaration) {
356 } else if (statement instanceof EnumDeclaration enumDeclaration) { 394 collectClassDeclarationAssertions(classDeclaration);
357 collectEnumAssertions(enumDeclaration); 395 } else if (statement instanceof EnumDeclaration enumDeclaration) {
358 } else if (statement instanceof NodeDeclaration nodeDeclaration) { 396 collectEnumAssertions(enumDeclaration);
359 collectNodeDeclarationAssertions(nodeDeclaration); 397 } else if (statement instanceof NodeDeclaration nodeDeclaration) {
360 } else if (statement instanceof Assertion assertion) { 398 collectNodeDeclarationAssertions(nodeDeclaration);
361 collectAssertion(assertion); 399 } else if (statement instanceof Assertion assertion) {
400 collectAssertion(assertion);
401 }
362 } 402 }
363 } 403 }
364 } 404 }
@@ -429,9 +469,11 @@ public class ModelInitializer {
429 } 469 }
430 470
431 private void fixClassDeclarationAssertions() { 471 private void fixClassDeclarationAssertions() {
432 for (var statement : problem.getStatements()) { 472 for (var importedProblem : importedProblems) {
433 if (statement instanceof ClassDeclaration classDeclaration) { 473 for (var statement : importedProblem.getStatements()) {
434 fixClassDeclarationAssertions(classDeclaration); 474 if (statement instanceof ClassDeclaration classDeclaration) {
475 fixClassDeclarationAssertions(classDeclaration);
476 }
435 } 477 }
436 } 478 }
437 } 479 }
@@ -499,9 +541,11 @@ public class ModelInitializer {
499 } 541 }
500 542
501 private void collectPredicates(ModelStoreBuilder storeBuilder) { 543 private void collectPredicates(ModelStoreBuilder storeBuilder) {
502 for (var statement : problem.getStatements()) { 544 for (var importedProblem : importedProblems) {
503 if (statement instanceof PredicateDefinition predicateDefinition) { 545 for (var statement : importedProblem.getStatements()) {
504 collectPredicateDefinitionTraced(predicateDefinition, storeBuilder); 546 if (statement instanceof PredicateDefinition predicateDefinition) {
547 collectPredicateDefinitionTraced(predicateDefinition, storeBuilder);
548 }
505 } 549 }
506 } 550 }
507 } 551 }
@@ -601,51 +645,51 @@ public class ModelInitializer {
601 645
602 private void toLiterals(Expr expr, Map<tools.refinery.language.model.problem.Variable, Variable> localScope, 646 private void toLiterals(Expr expr, Map<tools.refinery.language.model.problem.Variable, Variable> localScope,
603 List<Literal> literals) { 647 List<Literal> literals) {
604 switch (expr) { 648 switch (expr) {
605 case LogicConstant logicConstant -> { 649 case LogicConstant logicConstant -> {
606 switch (logicConstant.getLogicValue()) { 650 switch (logicConstant.getLogicValue()) {
607 case TRUE -> literals.add(BooleanLiteral.TRUE); 651 case TRUE -> literals.add(BooleanLiteral.TRUE);
608 case FALSE -> literals.add(BooleanLiteral.FALSE); 652 case FALSE -> literals.add(BooleanLiteral.FALSE);
609 default -> throw new TracedException(logicConstant, "Unsupported literal"); 653 default -> throw new TracedException(logicConstant, "Unsupported literal");
610 } 654 }
611 } 655 }
612 case Atom atom -> { 656 case Atom atom -> {
613 var target = getPartialRelation(atom.getRelation()); 657 var target = getPartialRelation(atom.getRelation());
614 var polarity = atom.isTransitiveClosure() ? CallPolarity.TRANSITIVE : CallPolarity.POSITIVE; 658 var polarity = atom.isTransitiveClosure() ? CallPolarity.TRANSITIVE : CallPolarity.POSITIVE;
615 var argumentList = toArgumentList(atom.getArguments(), localScope, literals); 659 var argumentList = toArgumentList(atom.getArguments(), localScope, literals);
616 literals.add(target.call(polarity, argumentList)); 660 literals.add(target.call(polarity, argumentList));
617 } 661 }
618 case NegationExpr negationExpr -> { 662 case NegationExpr negationExpr -> {
619 var body = negationExpr.getBody(); 663 var body = negationExpr.getBody();
620 if (!(body instanceof Atom atom)) { 664 if (!(body instanceof Atom atom)) {
621 throw new TracedException(body, "Cannot negate literal"); 665 throw new TracedException(body, "Cannot negate literal");
622 } 666 }
623 var target = getPartialRelation(atom.getRelation()); 667 var target = getPartialRelation(atom.getRelation());
624 Constraint constraint; 668 Constraint constraint;
625 if (atom.isTransitiveClosure()) { 669 if (atom.isTransitiveClosure()) {
626 constraint = Query.of(target.name() + "#transitive", (builder, p1, p2) -> builder.clause( 670 constraint = Query.of(target.name() + "#transitive", (builder, p1, p2) -> builder.clause(
627 target.callTransitive(p1, p2) 671 target.callTransitive(p1, p2)
628 )).getDnf(); 672 )).getDnf();
629 } else { 673 } else {
630 constraint = target; 674 constraint = target;
631 } 675 }
632 var negatedScope = extendScope(localScope, negationExpr.getImplicitVariables()); 676 var negatedScope = extendScope(localScope, negationExpr.getImplicitVariables());
633 var argumentList = toArgumentList(atom.getArguments(), negatedScope, literals); 677 var argumentList = toArgumentList(atom.getArguments(), negatedScope, literals);
634 literals.add(constraint.call(CallPolarity.NEGATIVE, argumentList)); 678 literals.add(constraint.call(CallPolarity.NEGATIVE, argumentList));
635 } 679 }
636 case ComparisonExpr comparisonExpr -> { 680 case ComparisonExpr comparisonExpr -> {
637 var argumentList = toArgumentList(List.of(comparisonExpr.getLeft(), comparisonExpr.getRight()), 681 var argumentList = toArgumentList(List.of(comparisonExpr.getLeft(), comparisonExpr.getRight()),
638 localScope, literals); 682 localScope, literals);
639 boolean positive = switch (comparisonExpr.getOp()) { 683 boolean positive = switch (comparisonExpr.getOp()) {
640 case EQ -> true; 684 case EQ -> true;
641 case NOT_EQ -> false; 685 case NOT_EQ -> false;
642 default -> throw new TracedException( 686 default -> throw new TracedException(
643 comparisonExpr, "Unsupported operator"); 687 comparisonExpr, "Unsupported operator");
644 }; 688 };
645 literals.add(new EquivalenceLiteral(positive, argumentList.get(0), argumentList.get(1))); 689 literals.add(new EquivalenceLiteral(positive, argumentList.get(0), argumentList.get(1)));
646 } 690 }
647 default -> throw new TracedException(expr, "Unsupported literal"); 691 default -> throw new TracedException(expr, "Unsupported literal");
648 } 692 }
649 } 693 }
650 694
651 private List<Variable> toArgumentList( 695 private List<Variable> toArgumentList(
@@ -680,13 +724,15 @@ public class ModelInitializer {
680 } 724 }
681 725
682 private void collectScopes() { 726 private void collectScopes() {
683 for (var statement : problem.getStatements()) { 727 for (var importedProblem : importedProblems) {
684 if (statement instanceof ScopeDeclaration scopeDeclaration) { 728 for (var statement : importedProblem.getStatements()) {
685 for (var typeScope : scopeDeclaration.getTypeScopes()) { 729 if (statement instanceof ScopeDeclaration scopeDeclaration) {
686 if (typeScope.isIncrement()) { 730 for (var typeScope : scopeDeclaration.getTypeScopes()) {
687 collectTypeScopeIncrement(typeScope); 731 if (typeScope.isIncrement()) {
688 } else { 732 collectTypeScopeIncrement(typeScope);
689 collectTypeScope(typeScope); 733 } else {
734 collectTypeScope(typeScope);
735 }
690 } 736 }
691 } 737 }
692 } 738 }
diff --git a/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java b/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java
index 19816da4..a846e265 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java
@@ -32,6 +32,7 @@ import tools.refinery.language.naming.ProblemQualifiedNameConverter;
32import tools.refinery.language.naming.ProblemQualifiedNameProvider; 32import tools.refinery.language.naming.ProblemQualifiedNameProvider;
33import tools.refinery.language.parser.antlr.TokenSourceInjectingProblemParser; 33import tools.refinery.language.parser.antlr.TokenSourceInjectingProblemParser;
34import tools.refinery.language.resource.*; 34import tools.refinery.language.resource.*;
35import tools.refinery.language.resource.state.ProblemDerivedStateComputer;
35import tools.refinery.language.scoping.ProblemGlobalScopeProvider; 36import tools.refinery.language.scoping.ProblemGlobalScopeProvider;
36import tools.refinery.language.scoping.ProblemLocalScopeProvider; 37import tools.refinery.language.scoping.ProblemLocalScopeProvider;
37import tools.refinery.language.serializer.PreferShortAssertionsProblemSemanticSequencer; 38import tools.refinery.language.serializer.PreferShortAssertionsProblemSemanticSequencer;
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..0fb4cd2c
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/library/BuiltinLibrary.java
@@ -0,0 +1,41 @@
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;
12import java.util.Optional;
13
14public class BuiltinLibrary implements RefineryLibrary {
15 public static final QualifiedName BUILTIN_LIBRARY_NAME = QualifiedName.create("builtin");
16 public static final URI BUILTIN_LIBRARY_URI = getLibraryUri(BUILTIN_LIBRARY_NAME).orElseThrow(
17 () -> new IllegalStateException("Builtin library was not found"));
18
19 @Override
20 public List<QualifiedName> getAutomaticImports() {
21 return List.of(BUILTIN_LIBRARY_NAME);
22 }
23
24 @Override
25 public Optional<URI> resolveQualifiedName(QualifiedName qualifiedName) {
26 if (qualifiedName.startsWith(BUILTIN_LIBRARY_NAME)) {
27 return getLibraryUri(qualifiedName);
28 }
29 return Optional.empty();
30 }
31
32 private static Optional<URI> getLibraryUri(QualifiedName qualifiedName) {
33 var libraryPath = String.join("/", qualifiedName.getSegments());
34 var libraryResource = BuiltinLibrary.class.getClassLoader()
35 .getResource("tools/refinery/language/library/%s.refinery".formatted(libraryPath));
36 if (libraryResource == null) {
37 return Optional.empty();
38 }
39 return Optional.of(URI.createURI(libraryResource.toString()));
40 }
41}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibraries.java b/subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibraries.java
new file mode 100644
index 00000000..0efca199
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibraries.java
@@ -0,0 +1,54 @@
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.scoping.imports.NamedImport;
11
12import java.util.LinkedHashMap;
13import java.util.List;
14import java.util.Optional;
15import java.util.ServiceLoader;
16
17public final class RefineryLibraries {
18 private static final ServiceLoader<RefineryLibrary> SERVICE_LOADER = ServiceLoader.load(RefineryLibrary.class);
19 private static final List<NamedImport> AUTOMATIC_IMPORTS;
20
21 static {
22 var imports = new LinkedHashMap<QualifiedName, URI>();
23 for (var service : SERVICE_LOADER) {
24 for (var qualifiedName : service.getAutomaticImports()) {
25 var uri = service.resolveQualifiedName(qualifiedName).orElseThrow(
26 () -> new IllegalStateException("Automatic import %s was not found".formatted(qualifiedName)));
27 if (imports.put(qualifiedName, uri) != null) {
28 throw new IllegalStateException("Duplicate automatic import " + qualifiedName);
29 }
30 }
31 }
32 AUTOMATIC_IMPORTS = imports.entrySet().stream()
33 .map(entry -> NamedImport.implicit(entry.getValue(), entry.getKey()))
34 .toList();
35 }
36
37 private RefineryLibraries() {
38 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
39 }
40
41 public static List<NamedImport> getAutomaticImports() {
42 return AUTOMATIC_IMPORTS;
43 }
44
45 public static Optional<URI> resolveQualifiedName(QualifiedName qualifiedName) {
46 for (var service : SERVICE_LOADER) {
47 var result = service.resolveQualifiedName(qualifiedName);
48 if (result.isPresent()) {
49 return result;
50 }
51 }
52 return Optional.empty();
53 }
54}
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..9db2900e
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibrary.java
@@ -0,0 +1,20 @@
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;
12import java.util.Optional;
13
14public interface RefineryLibrary {
15 default List<QualifiedName> getAutomaticImports() {
16 return List.of();
17 }
18
19 Optional<URI> resolveQualifiedName(QualifiedName qualifiedName);
20}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/LoadOnDemandResourceDescriptionProvider.java b/subprojects/language/src/main/java/tools/refinery/language/resource/LoadOnDemandResourceDescriptionProvider.java
new file mode 100644
index 00000000..373a32f2
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/LoadOnDemandResourceDescriptionProvider.java
@@ -0,0 +1,49 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.resource;
7
8import com.google.inject.Inject;
9import org.eclipse.emf.common.util.URI;
10import org.eclipse.emf.ecore.resource.Resource;
11import org.eclipse.xtext.EcoreUtil2;
12import org.eclipse.xtext.resource.IResourceDescription;
13import org.eclipse.xtext.resource.IResourceDescriptions;
14import org.eclipse.xtext.resource.IResourceDescriptionsProvider;
15import org.eclipse.xtext.scoping.impl.GlobalResourceDescriptionProvider;
16
17public class LoadOnDemandResourceDescriptionProvider {
18 @Inject
19 private IResourceDescriptionsProvider resourceDescriptionsProvider;
20
21 @Inject
22 private GlobalResourceDescriptionProvider globalResourceDescriptionProvider;
23
24 private Resource context;
25 private IResourceDescriptions resourceDescriptions;
26
27 public void setContext(Resource context) {
28 if (this.context != null) {
29 throw new IllegalStateException("Context was already set");
30 }
31 this.context = context;
32 resourceDescriptions = resourceDescriptionsProvider.getResourceDescriptions(context.getResourceSet());
33 }
34
35 public IResourceDescription getResourceDescription(URI uri) {
36 if (this.context == null) {
37 throw new IllegalStateException("Context was not set");
38 }
39 var resourceDescription = resourceDescriptions.getResourceDescription(uri);
40 if (resourceDescription != null) {
41 return resourceDescription;
42 }
43 var importedResource = EcoreUtil2.getResource(context, uri.toString());
44 if (importedResource == null) {
45 return null;
46 }
47 return globalResourceDescriptionProvider.getResourceDescription(importedResource);
48 }
49}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java
index 76fd5852..a2ec7ed0 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java
@@ -16,11 +16,13 @@ import org.eclipse.xtext.resource.EObjectDescription;
16import org.eclipse.xtext.resource.IEObjectDescription; 16import org.eclipse.xtext.resource.IEObjectDescription;
17import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy; 17import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy;
18import org.eclipse.xtext.util.IAcceptor; 18import org.eclipse.xtext.util.IAcceptor;
19import tools.refinery.language.scoping.imports.ImportCollector;
19import tools.refinery.language.model.problem.*; 20import tools.refinery.language.model.problem.*;
20import tools.refinery.language.naming.NamingUtil; 21import tools.refinery.language.naming.NamingUtil;
21import tools.refinery.language.utils.ProblemUtil; 22import tools.refinery.language.utils.ProblemUtil;
22 23
23import java.util.Map; 24import java.util.Map;
25import java.util.stream.Collectors;
24 26
25@Singleton 27@Singleton
26public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy { 28public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy {
@@ -35,12 +37,17 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
35 public static final String SHADOWING_KEY_RELATION = "relation"; 37 public static final String SHADOWING_KEY_RELATION = "relation";
36 public static final String PREFERRED_NAME = DATA_PREFIX + "PREFERRED_NAME"; 38 public static final String PREFERRED_NAME = DATA_PREFIX + "PREFERRED_NAME";
37 public static final String PREFERRED_NAME_TRUE = "true"; 39 public static final String PREFERRED_NAME_TRUE = "true";
40 public static final String IMPORTS = DATA_PREFIX + "IMPORTS";
41 public static final String IMPORTS_SEPARATOR = "|";
38 public static final String COLOR_RELATION = DATA_PREFIX + "COLOR_RELATION"; 42 public static final String COLOR_RELATION = DATA_PREFIX + "COLOR_RELATION";
39 public static final String COLOR_RELATION_TRUE = "true"; 43 public static final String COLOR_RELATION_TRUE = "true";
40 44
41 @Inject 45 @Inject
42 private IQualifiedNameConverter qualifiedNameConverter; 46 private IQualifiedNameConverter qualifiedNameConverter;
43 47
48 @Inject
49 private ImportCollector importCollector;
50
44 @Override 51 @Override
45 public boolean createEObjectDescriptions(EObject eObject, IAcceptor<IEObjectDescription> acceptor) { 52 public boolean createEObjectDescriptions(EObject eObject, IAcceptor<IEObjectDescription> acceptor) {
46 if (!shouldExport(eObject)) { 53 if (!shouldExport(eObject)) {
@@ -115,6 +122,11 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
115 var builder = ImmutableMap.<String, String>builder(); 122 var builder = ImmutableMap.<String, String>builder();
116 if (eObject instanceof Problem) { 123 if (eObject instanceof Problem) {
117 builder.put(SHADOWING_KEY, SHADOWING_KEY_PROBLEM); 124 builder.put(SHADOWING_KEY, SHADOWING_KEY_PROBLEM);
125 var explicitImports = importCollector.getDirectImports(eObject.eResource());
126 var importsString = explicitImports.toList().stream()
127 .map(importEntry -> importEntry.uri().toString())
128 .collect(Collectors.joining(IMPORTS_SEPARATOR));
129 builder.put(IMPORTS, importsString);
118 } else if (eObject instanceof Node) { 130 } else if (eObject instanceof Node) {
119 builder.put(SHADOWING_KEY, SHADOWING_KEY_NODE); 131 builder.put(SHADOWING_KEY, SHADOWING_KEY_NODE);
120 } else if (eObject instanceof Relation relation) { 132 } else if (eObject instanceof Relation relation) {
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java b/subprojects/language/src/main/java/tools/refinery/language/resource/state/DerivedVariableComputer.java
index 07c5da41..f0baf35f 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/state/DerivedVariableComputer.java
@@ -3,7 +3,7 @@
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
6package tools.refinery.language.resource; 6package tools.refinery.language.resource.state;
7 7
8import com.google.inject.Inject; 8import com.google.inject.Inject;
9import com.google.inject.Singleton; 9import com.google.inject.Singleton;
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ImplicitVariableScope.java b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ImplicitVariableScope.java
index e97c8287..e25887ad 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/resource/ImplicitVariableScope.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ImplicitVariableScope.java
@@ -3,7 +3,7 @@
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
6package tools.refinery.language.resource; 6package tools.refinery.language.resource.state;
7 7
8import org.eclipse.emf.ecore.EObject; 8import org.eclipse.emf.ecore.EObject;
9import org.eclipse.xtext.linking.impl.LinkingHelper; 9import org.eclipse.xtext.linking.impl.LinkingHelper;
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java b/subprojects/language/src/main/java/tools/refinery/language/resource/state/NodeNameCollector.java
index e5deca4d..de4a607c 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/state/NodeNameCollector.java
@@ -3,7 +3,7 @@
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
6package tools.refinery.language.resource; 6package tools.refinery.language.resource.state;
7 7
8import com.google.common.collect.ImmutableSet; 8import com.google.common.collect.ImmutableSet;
9import com.google.inject.Inject; 9import com.google.inject.Inject;
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java
index 31eb55a6..d905aa9a 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java
@@ -3,7 +3,7 @@
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
6package tools.refinery.language.resource; 6package tools.refinery.language.resource.state;
7 7
8import com.google.inject.Inject; 8import com.google.inject.Inject;
9import com.google.inject.Provider; 9import com.google.inject.Provider;
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/CompositeSelectable.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/CompositeSelectable.java
new file mode 100644
index 00000000..0fddcaf9
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/CompositeSelectable.java
@@ -0,0 +1,62 @@
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;
7
8import com.google.common.collect.Iterables;
9import org.eclipse.emf.ecore.EClass;
10import org.eclipse.emf.ecore.EObject;
11import org.eclipse.xtext.naming.QualifiedName;
12import org.eclipse.xtext.resource.IEObjectDescription;
13import org.eclipse.xtext.resource.ISelectable;
14
15import java.util.Collection;
16import java.util.List;
17
18class CompositeSelectable implements ISelectable {
19 private static final CompositeSelectable EMPTY = new CompositeSelectable(List.of());
20
21 private final List<? extends ISelectable> children;
22
23 private CompositeSelectable(List<? extends ISelectable> children) {
24
25 this.children = children;
26 }
27
28 @Override
29 public boolean isEmpty() {
30 return children.isEmpty();
31 }
32
33 @Override
34 public Iterable<IEObjectDescription> getExportedObjects() {
35 return Iterables.concat(Iterables.transform(children, ISelectable::getExportedObjects));
36 }
37
38 @Override
39 public Iterable<IEObjectDescription> getExportedObjects(EClass type, QualifiedName name, boolean ignoreCase) {
40 return Iterables.concat(Iterables.transform(children, child -> child.getExportedObjects(type, name,
41 ignoreCase)));
42 }
43
44 @Override
45 public Iterable<IEObjectDescription> getExportedObjectsByType(EClass type) {
46 return Iterables.concat(Iterables.transform(children, child -> child.getExportedObjectsByType(type)));
47 }
48
49 @Override
50 public Iterable<IEObjectDescription> getExportedObjectsByObject(EObject object) {
51 return Iterables.concat(Iterables.transform(children, child -> child.getExportedObjectsByObject(object)));
52 }
53
54 public static ISelectable of(Collection<? extends ISelectable> children) {
55 var filteredChildren = children.stream().filter(selectable -> !selectable.isEmpty()).toList();
56 return switch (filteredChildren.size()) {
57 case 0 -> EMPTY;
58 case 1 -> filteredChildren.getFirst();
59 default -> new CompositeSelectable(filteredChildren);
60 };
61 }
62}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/NormalizedSelectable.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/NormalizedSelectable.java
index 0c7828d8..09fe4716 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/scoping/NormalizedSelectable.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/NormalizedSelectable.java
@@ -12,18 +12,21 @@ import org.eclipse.xtext.naming.QualifiedName;
12import org.eclipse.xtext.resource.IEObjectDescription; 12import org.eclipse.xtext.resource.IEObjectDescription;
13import org.eclipse.xtext.resource.ISelectable; 13import org.eclipse.xtext.resource.ISelectable;
14import org.eclipse.xtext.resource.impl.AliasedEObjectDescription; 14import org.eclipse.xtext.resource.impl.AliasedEObjectDescription;
15import org.jetbrains.annotations.NotNull;
16 15
17import java.util.Iterator; 16import java.util.Iterator;
17import java.util.List;
18import java.util.NoSuchElementException; 18import java.util.NoSuchElementException;
19 19
20public class NormalizedSelectable implements ISelectable { 20class NormalizedSelectable implements ISelectable {
21 private final ISelectable delegateSelectable; 21 private final ISelectable delegateSelectable;
22 private final QualifiedName originalPrefix; 22 private final QualifiedName originalPrefix;
23 private final QualifiedName normalizedPrefix; 23 private final QualifiedName normalizedPrefix;
24 24
25 private NormalizedSelectable(ISelectable delegateSelectable, QualifiedName originalPrefix, 25 public NormalizedSelectable(ISelectable delegateSelectable, QualifiedName originalPrefix,
26 QualifiedName normalizedPrefix) { 26 QualifiedName normalizedPrefix) {
27 if (originalPrefix.equals(QualifiedName.EMPTY)) {
28 throw new IllegalArgumentException("Cannot normalize empty qualified name prefix");
29 }
27 this.delegateSelectable = delegateSelectable; 30 this.delegateSelectable = delegateSelectable;
28 this.originalPrefix = originalPrefix; 31 this.originalPrefix = originalPrefix;
29 this.normalizedPrefix = normalizedPrefix; 32 this.normalizedPrefix = normalizedPrefix;
@@ -37,41 +40,36 @@ public class NormalizedSelectable implements ISelectable {
37 @Override 40 @Override
38 public Iterable<IEObjectDescription> getExportedObjects() { 41 public Iterable<IEObjectDescription> getExportedObjects() {
39 var delegateIterable = delegateSelectable.getExportedObjects(); 42 var delegateIterable = delegateSelectable.getExportedObjects();
40 var aliasedIterable = getAliasedElements(delegateIterable); 43 return getAliasedElements(delegateIterable);
41 return Iterables.concat(delegateIterable, aliasedIterable);
42 } 44 }
43 45
44 @Override 46 @Override
45 public Iterable<IEObjectDescription> getExportedObjects(EClass type, QualifiedName name, boolean ignoreCase) { 47 public Iterable<IEObjectDescription> getExportedObjects(EClass type, QualifiedName name, boolean ignoreCase) {
46 var delegateIterable = delegateSelectable.getExportedObjects(type, name, ignoreCase);
47 boolean startsWith = ignoreCase ? name.startsWithIgnoreCase(normalizedPrefix) : 48 boolean startsWith = ignoreCase ? name.startsWithIgnoreCase(normalizedPrefix) :
48 name.startsWith(normalizedPrefix); 49 name.startsWith(normalizedPrefix);
49 if (startsWith && name.getSegmentCount() > normalizedPrefix.getSegmentCount()) { 50 if (startsWith && name.getSegmentCount() > normalizedPrefix.getSegmentCount()) {
50 var originalName = originalPrefix.append(name.skipFirst(normalizedPrefix.getSegmentCount())); 51 var originalName = originalPrefix.append(name.skipFirst(normalizedPrefix.getSegmentCount()));
51 var originalIterable = Iterables.transform( 52 return Iterables.transform(
52 delegateSelectable.getExportedObjects(type, originalName, ignoreCase), 53 delegateSelectable.getExportedObjects(type, originalName, ignoreCase),
53 description -> { 54 description -> {
54 var normalizedName = normalizedPrefix.append( 55 var normalizedName = normalizedPrefix.append(
55 description.getName().skipFirst(originalPrefix.getSegmentCount())); 56 description.getName().skipFirst(originalPrefix.getSegmentCount()));
56 return new AliasedEObjectDescription(normalizedName, description); 57 return new AliasedEObjectDescription(normalizedName, description);
57 }); 58 });
58 return Iterables.concat(originalIterable, delegateIterable);
59 } 59 }
60 return delegateIterable; 60 return List.of();
61 } 61 }
62 62
63 @Override 63 @Override
64 public Iterable<IEObjectDescription> getExportedObjectsByType(EClass type) { 64 public Iterable<IEObjectDescription> getExportedObjectsByType(EClass type) {
65 var delegateIterable = delegateSelectable.getExportedObjectsByType(type); 65 var delegateIterable = delegateSelectable.getExportedObjectsByType(type);
66 var aliasedIterable = getAliasedElements(delegateIterable); 66 return getAliasedElements(delegateIterable);
67 return Iterables.concat(delegateIterable, aliasedIterable);
68 } 67 }
69 68
70 @Override 69 @Override
71 public Iterable<IEObjectDescription> getExportedObjectsByObject(EObject object) { 70 public Iterable<IEObjectDescription> getExportedObjectsByObject(EObject object) {
72 var delegateIterable = delegateSelectable.getExportedObjectsByObject(object); 71 var delegateIterable = delegateSelectable.getExportedObjectsByObject(object);
73 var aliasedIterable = getAliasedElements(delegateIterable); 72 return getAliasedElements(delegateIterable);
74 return Iterables.concat(delegateIterable, aliasedIterable);
75 } 73 }
76 74
77 private Iterable<IEObjectDescription> getAliasedElements(Iterable<IEObjectDescription> delegateIterable) { 75 private Iterable<IEObjectDescription> getAliasedElements(Iterable<IEObjectDescription> delegateIterable) {
@@ -108,15 +106,4 @@ public class NormalizedSelectable implements ISelectable {
108 } 106 }
109 }; 107 };
110 } 108 }
111
112 public static ISelectable of(@NotNull ISelectable delegateSelectable, @NotNull QualifiedName originalPrefix,
113 @NotNull QualifiedName normalizedPrefix) {
114 if (originalPrefix.equals(normalizedPrefix)) {
115 return delegateSelectable;
116 }
117 if (originalPrefix.equals(QualifiedName.EMPTY)) {
118 throw new IllegalArgumentException("Cannot normalize empty qualified name prefix");
119 }
120 return new NormalizedSelectable(delegateSelectable, originalPrefix, normalizedPrefix);
121 }
122} 109}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java
index 37a67c0c..dad4e5d0 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java
@@ -6,35 +6,85 @@
6package tools.refinery.language.scoping; 6package tools.refinery.language.scoping;
7 7
8import com.google.common.base.Predicate; 8import com.google.common.base.Predicate;
9import org.eclipse.emf.common.util.URI; 9import com.google.inject.Inject;
10import com.google.inject.Provider;
10import org.eclipse.emf.ecore.EClass; 11import org.eclipse.emf.ecore.EClass;
11import org.eclipse.emf.ecore.resource.Resource; 12import org.eclipse.emf.ecore.resource.Resource;
12import org.eclipse.xtext.naming.QualifiedName; 13import org.eclipse.xtext.naming.QualifiedName;
13import org.eclipse.xtext.resource.IEObjectDescription; 14import org.eclipse.xtext.resource.IEObjectDescription;
14import org.eclipse.xtext.resource.IResourceDescriptions;
15import org.eclipse.xtext.resource.ISelectable; 15import org.eclipse.xtext.resource.ISelectable;
16import org.eclipse.xtext.scoping.IScope; 16import org.eclipse.xtext.scoping.IScope;
17import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider; 17import org.eclipse.xtext.scoping.impl.AbstractGlobalScopeProvider;
18import org.eclipse.xtext.scoping.impl.SelectableBasedScope; 18import org.eclipse.xtext.scoping.impl.SelectableBasedScope;
19import tools.refinery.language.utils.ProblemUtil; 19import org.eclipse.xtext.util.IResourceScopeCache;
20import tools.refinery.language.scoping.imports.ImportCollector;
21import tools.refinery.language.resource.LoadOnDemandResourceDescriptionProvider;
22import tools.refinery.language.scoping.imports.NamedImport;
20 23
21import java.util.LinkedHashSet; 24import java.util.ArrayList;
25import java.util.Collection;
26import java.util.List;
22 27
23public class ProblemGlobalScopeProvider extends ImportUriGlobalScopeProvider { 28public class ProblemGlobalScopeProvider extends AbstractGlobalScopeProvider {
29 private static final String CACHE_KEY = "tools.refinery.language.scoping.ProblemGlobalScopeProvider.CACHE_KEY";
30
31 @Inject
32 private ImportCollector importCollector;
33
34 @Inject
35 private Provider<LoadOnDemandResourceDescriptionProvider> loadOnDemandProvider;
36
37 @Inject
38 private IResourceScopeCache cache;
39
40 // {@link com.google.common.base.Predicate} required by Xtext API.
41 @SuppressWarnings("squid:S4738")
24 @Override 42 @Override
25 protected LinkedHashSet<URI> getImportedUris(Resource resource) { 43 protected IScope getScope(Resource resource, boolean ignoreCase, EClass type,
26 LinkedHashSet<URI> importedUris = new LinkedHashSet<>(); 44 Predicate<IEObjectDescription> filter) {
27 importedUris.add(ProblemUtil.BUILTIN_LIBRARY_URI); 45 var loadedImports = cache.get(CACHE_KEY, resource, () -> computeLoadedImports(resource));
28 return importedUris; 46 var qualifiedScope = createScope(IScope.NULLSCOPE, loadedImports.qualifiedImports(), type, filter, ignoreCase);
47 var implicitScope = createScope(qualifiedScope, loadedImports.implicitImports(), type, filter, ignoreCase);
48 return createScope(implicitScope, loadedImports.explicitImports(), type, filter, ignoreCase);
29 } 49 }
30 50
31 @Override 51 protected LoadedImports computeLoadedImports(Resource resource) {
32 protected IScope createLazyResourceScope(IScope parent, URI uri, IResourceDescriptions descriptions, EClass type, 52 var imports = importCollector.getAllImports(resource);
33 Predicate<IEObjectDescription> filter, boolean ignoreCase) { 53 var loadOnDemand = loadOnDemandProvider.get();
34 ISelectable description = descriptions.getResourceDescription(uri); 54 loadOnDemand.setContext(resource);
35 if (description != null && ProblemUtil.BUILTIN_LIBRARY_URI.equals(uri)) { 55 var qualifiedImports = new ArrayList<ISelectable>();
36 description = NormalizedSelectable.of(description, QualifiedName.create("builtin"), QualifiedName.EMPTY); 56 var implicitImports = new ArrayList<ISelectable>();
57 var explicitImports = new ArrayList<ISelectable>();
58 for (var importEntry : imports.toList()) {
59 var uri = importEntry.uri();
60 var resourceDescription = loadOnDemand.getResourceDescription(uri);
61 if (resourceDescription == null) {
62 continue;
63 }
64 qualifiedImports.add(resourceDescription);
65 if (importEntry instanceof NamedImport namedImport) {
66 var qualifiedName = namedImport.qualifiedName();
67 if (namedImport.alsoImplicit()) {
68 implicitImports.add(new NormalizedSelectable(resourceDescription, qualifiedName,
69 QualifiedName.EMPTY));
70 }
71 for (var alias : namedImport.aliases()) {
72 explicitImports.add(new NormalizedSelectable(resourceDescription, qualifiedName, alias));
73 }
74 }
37 } 75 }
38 return SelectableBasedScope.createScope(parent, description, filter, type, ignoreCase); 76 return new LoadedImports(qualifiedImports, implicitImports, explicitImports);
77 }
78
79 // {@link com.google.common.base.Predicate} required by Xtext API.
80 @SuppressWarnings("squid:S4738")
81 protected IScope createScope(IScope parent, Collection<? extends ISelectable> children, EClass type,
82 Predicate<IEObjectDescription> filter, boolean ignoreCase) {
83 var selectable = CompositeSelectable.of(children);
84 return SelectableBasedScope.createScope(parent, selectable, filter, type, ignoreCase);
85 }
86
87 protected record LoadedImports(List<ISelectable> qualifiedImports, List<ISelectable> implicitImports,
88 List<ISelectable> explicitImports) {
39 } 89 }
40} 90}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java
index 9be32636..3e00b87e 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java
@@ -6,37 +6,72 @@
6package tools.refinery.language.scoping; 6package tools.refinery.language.scoping;
7 7
8import com.google.inject.Inject; 8import com.google.inject.Inject;
9import org.eclipse.emf.ecore.EObject;
10import org.eclipse.emf.ecore.EReference;
9import org.eclipse.emf.ecore.resource.Resource; 11import org.eclipse.emf.ecore.resource.Resource;
10import org.eclipse.xtext.naming.IQualifiedNameProvider; 12import org.eclipse.xtext.naming.IQualifiedNameProvider;
11import org.eclipse.xtext.naming.QualifiedName; 13import org.eclipse.xtext.naming.QualifiedName;
14import org.eclipse.xtext.resource.IResourceDescription;
12import org.eclipse.xtext.resource.IResourceDescriptionsProvider; 15import org.eclipse.xtext.resource.IResourceDescriptionsProvider;
13import org.eclipse.xtext.resource.ISelectable; 16import org.eclipse.xtext.resource.ISelectable;
14import org.eclipse.xtext.scoping.impl.SimpleLocalScopeProvider; 17import org.eclipse.xtext.scoping.IScope;
18import org.eclipse.xtext.scoping.impl.AbstractGlobalScopeDelegatingScopeProvider;
19import org.eclipse.xtext.scoping.impl.SelectableBasedScope;
20import org.eclipse.xtext.util.IResourceScopeCache;
15import tools.refinery.language.naming.NamingUtil; 21import tools.refinery.language.naming.NamingUtil;
16 22
17public class ProblemLocalScopeProvider extends SimpleLocalScopeProvider { 23public class ProblemLocalScopeProvider extends AbstractGlobalScopeDelegatingScopeProvider {
24 private static final String CACHE_KEY = "tools.refinery.language.scoping.ProblemLocalScopeProvider.CACHE_KEY";
25
18 @Inject 26 @Inject
19 private IQualifiedNameProvider qualifiedNameProvider; 27 private IQualifiedNameProvider qualifiedNameProvider;
20 28
21 @Inject 29 @Inject
22 private IResourceDescriptionsProvider resourceDescriptionsProvider; 30 private IResourceDescriptionsProvider resourceDescriptionsProvider;
23 31
32 @Inject
33 private IResourceScopeCache cache;
34
24 @Override 35 @Override
25 protected ISelectable getAllDescriptions(Resource resource) { 36 public IScope getScope(EObject context, EReference reference) {
37 var resource = context.eResource();
38 if (resource == null) {
39 return IScope.NULLSCOPE;
40 }
41 var localImports = cache.get(CACHE_KEY, resource, () -> computeLocalImports(resource));
42 if (localImports.resourceDescription() == null) {
43 return IScope.NULLSCOPE;
44 }
45 var globalScope = getGlobalScope(resource, reference);
46 var type = reference.getEReferenceType();
47 boolean ignoreCase = isIgnoreCase(reference);
48 var scope = SelectableBasedScope.createScope(globalScope, localImports.resourceDescription(), type,
49 ignoreCase);
50 if (localImports.normalizedSelectable() == null) {
51 return scope;
52 }
53 return SelectableBasedScope.createScope(scope, localImports.normalizedSelectable(), type, ignoreCase);
54 }
55
56 protected LocalImports computeLocalImports(Resource resource) {
26 // Force the use of ProblemResourceDescriptionStrategy to include all QualifiedNames of objects. 57 // Force the use of ProblemResourceDescriptionStrategy to include all QualifiedNames of objects.
27 var resourceDescriptions = resourceDescriptionsProvider 58 var resourceDescriptions = resourceDescriptionsProvider.getResourceDescriptions(resource.getResourceSet());
28 .getResourceDescriptions(resource.getResourceSet());
29 var resourceDescription = resourceDescriptions.getResourceDescription(resource.getURI()); 59 var resourceDescription = resourceDescriptions.getResourceDescription(resource.getURI());
30 if (resourceDescription != null && !resource.getContents().isEmpty()) { 60 if (resourceDescription == null) {
31 var rootElement = resource.getContents().getFirst(); 61 return new LocalImports(null, null);
32 if (rootElement != null) { 62 }
33 var rootName = NamingUtil.stripRootPrefix(qualifiedNameProvider.getFullyQualifiedName(rootElement)); 63 var rootElement = resource.getContents().getFirst();
34 if (rootName == null) { 64 if (rootElement == null) {
35 return resourceDescription; 65 return new LocalImports(resourceDescription, null);
36 }
37 return NormalizedSelectable.of(resourceDescription, rootName, QualifiedName.EMPTY);
38 }
39 } 66 }
40 return resourceDescription; 67 var rootName = NamingUtil.stripRootPrefix(qualifiedNameProvider.getFullyQualifiedName(rootElement));
68 if (rootName == null) {
69 return new LocalImports(resourceDescription, null);
70 }
71 var normalizedSelectable = new NormalizedSelectable(resourceDescription, rootName, QualifiedName.EMPTY);
72 return new LocalImports(resourceDescription, normalizedSelectable);
73 }
74
75 protected record LocalImports(IResourceDescription resourceDescription, ISelectable normalizedSelectable) {
41 } 76 }
42} 77}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/Import.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/Import.java
new file mode 100644
index 00000000..e2f0d3d8
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/Import.java
@@ -0,0 +1,12 @@
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 org.eclipse.emf.common.util.URI;
9
10public sealed interface Import permits NamedImport, TransitiveImport {
11 URI uri();
12}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollection.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollection.java
new file mode 100644
index 00000000..63171138
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollection.java
@@ -0,0 +1,76 @@
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 org.eclipse.emf.common.util.URI;
9import org.jetbrains.annotations.NotNull;
10import org.jetbrains.annotations.Nullable;
11
12import java.util.*;
13
14public class ImportCollection {
15 public static ImportCollection EMPTY = new ImportCollection() {
16 @Override
17 public void add(Import importEntry) {
18 throw new UnsupportedOperationException("Read-only collection");
19 }
20
21 @Override
22 public void remove(URI uri) {
23 throw new UnsupportedOperationException("Read-only collection");
24 }
25 };
26
27 private final Map<URI, Import> importMap = new HashMap<>();
28
29 public void add(Import importEntry) {
30 importMap.compute(importEntry.uri(), (ignored, originalEntry) -> merge(originalEntry, importEntry));
31 }
32
33 public void addAll(Iterable<? extends Import> imports) {
34 imports.forEach(this::add);
35 }
36
37 public void remove(URI uri) {
38 importMap.remove(uri);
39 }
40
41 public List<Import> toList() {
42 return List.copyOf(importMap.values());
43 }
44
45 public Set<URI> toUriSet() {
46 return new LinkedHashSet<>(importMap.keySet());
47 }
48
49 @NotNull
50 private static Import merge(@Nullable Import left, @NotNull Import right) {
51 if (left == null) {
52 return right;
53 }
54 if (!left.uri().equals(right.uri())) {
55 throw new IllegalArgumentException("Expected URIs '%s' and '%s' to be equal".formatted(
56 left.uri(), right.uri()));
57 }
58 return switch (left) {
59 case TransitiveImport transitiveLeft ->
60 right instanceof TransitiveImport ? left : merge(right, transitiveLeft);
61 case NamedImport namedLeft -> switch (right) {
62 case TransitiveImport ignored -> namedLeft;
63 case NamedImport namedRight -> {
64 if (!namedLeft.qualifiedName().equals(namedRight.qualifiedName())) {
65 throw new IllegalArgumentException("Expected qualified names '%s' and '%s' to be equal"
66 .formatted(namedLeft.qualifiedName(), namedRight.qualifiedName()));
67 }
68 var mergedAliases = new LinkedHashSet<>(namedLeft.aliases());
69 mergedAliases.addAll(namedRight.aliases());
70 yield new NamedImport(namedLeft.uri(), namedLeft.qualifiedName(),
71 List.copyOf(mergedAliases), namedLeft.alsoImplicit() || namedRight.alsoImplicit());
72 }
73 };
74 };
75 }
76}
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..cea99f0a
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java
@@ -0,0 +1,138 @@
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.resource.Resource;
15import org.eclipse.xtext.linking.impl.LinkingHelper;
16import org.eclipse.xtext.naming.IQualifiedNameConverter;
17import org.eclipse.xtext.naming.QualifiedName;
18import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
19import org.eclipse.xtext.resource.IEObjectDescription;
20import org.eclipse.xtext.util.IResourceScopeCache;
21import tools.refinery.language.library.RefineryLibraries;
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 Map<QualifiedName, Set<QualifiedName>> aliasesMap = new LinkedHashMap<>();
58 for (var statement : problem.getStatements()) {
59 if (statement instanceof ImportStatement importStatement) {
60 collectImportStatement(importStatement, aliasesMap);
61 }
62 }
63 var collection = new ImportCollection();
64 collection.addAll(RefineryLibraries.getAutomaticImports());
65 for (var entry : aliasesMap.entrySet()) {
66 var qualifiedName = entry.getKey();
67 RefineryLibraries.resolveQualifiedName(qualifiedName).ifPresent(uri -> {
68 if (!uri.equals(resource.getURI())) {
69 var aliases = entry.getValue();
70 collection.add(NamedImport.explicit(uri, qualifiedName, List.copyOf(aliases)));
71 }
72 });
73 }
74 collection.remove(resource.getURI());
75 return collection;
76 }
77
78 private void collectImportStatement(ImportStatement importStatement, Map<QualifiedName, Set<QualifiedName>> aliasesMap) {
79 var nodes = NodeModelUtils.findNodesForFeature(importStatement,
80 ProblemPackage.Literals.IMPORT_STATEMENT__IMPORTED_MODULE);
81 var aliasString = importStatement.getAlias();
82 var alias = Strings.isNullOrEmpty(aliasString) ? QualifiedName.EMPTY :
83 NamingUtil.stripRootPrefix(qualifiedNameConverter.toQualifiedName(aliasString));
84 for (var node : nodes) {
85 var qualifiedNameString = linkingHelper.getCrossRefNodeAsString(node, true);
86 if (Strings.isNullOrEmpty(qualifiedNameString)) {
87 continue;
88 }
89 var qualifiedName = NamingUtil.stripRootPrefix(
90 qualifiedNameConverter.toQualifiedName(qualifiedNameString));
91 var aliases = aliasesMap.computeIfAbsent(qualifiedName, ignored -> new LinkedHashSet<>());
92 aliases.add(alias);
93 }
94 }
95
96 public ImportCollection getAllImports(Resource resource) {
97 return cache.get(ALL_IMPORTS_KEY, resource, () -> this.computeAllImports(resource));
98 }
99
100 protected ImportCollection computeAllImports(Resource resource) {
101 var collection = new ImportCollection();
102 collection.addAll(getDirectImports(resource).toList());
103 var loadOnDemand = loadOnDemandProvider.get();
104 loadOnDemand.setContext(resource);
105 var seen = new HashSet<URI>();
106 seen.add(resource.getURI());
107 var queue = new ArrayDeque<>(collection.toUriSet());
108 while (!queue.isEmpty()) {
109 var uri = queue.removeFirst();
110 seen.add(uri);
111 collection.add(new TransitiveImport(uri));
112 var resourceDescription = loadOnDemand.getResourceDescription(uri);
113 if (resourceDescription == null) {
114 continue;
115 }
116 var problemDescriptions = resourceDescription.getExportedObjectsByType(ProblemPackage.Literals.PROBLEM);
117 for (var eObjectDescription : problemDescriptions) {
118 for (var importedUri : getImports(eObjectDescription)) {
119 if (!seen.contains(importedUri)) {
120 queue.addLast(importedUri);
121 }
122 }
123 }
124 }
125 collection.remove(resource.getURI());
126 return collection;
127 }
128
129 protected List<URI> getImports(IEObjectDescription eObjectDescription) {
130 var importString = eObjectDescription.getUserData(ProblemResourceDescriptionStrategy.IMPORTS);
131 if (importString == null) {
132 return List.of();
133 }
134 return Splitter.on(ProblemResourceDescriptionStrategy.IMPORTS_SEPARATOR).splitToStream(importString)
135 .map(URI::createURI)
136 .toList();
137 }
138}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/NamedImport.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/NamedImport.java
new file mode 100644
index 00000000..f5e89605
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/NamedImport.java
@@ -0,0 +1,22 @@
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 org.eclipse.emf.common.util.URI;
9import org.eclipse.xtext.naming.QualifiedName;
10
11import java.util.List;
12
13public record NamedImport(URI uri, QualifiedName qualifiedName, List<QualifiedName> aliases,
14 boolean alsoImplicit) implements Import {
15 public static NamedImport implicit(URI uri, QualifiedName qualifiedName) {
16 return new NamedImport(uri, qualifiedName, List.of(), true);
17 }
18
19 public static NamedImport explicit(URI uri, QualifiedName qualifiedName, List<QualifiedName> aliases) {
20 return new NamedImport(uri, qualifiedName, aliases, false);
21 }
22}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/TransitiveImport.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/TransitiveImport.java
new file mode 100644
index 00000000..6f5ceaa6
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/TransitiveImport.java
@@ -0,0 +1,11 @@
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 org.eclipse.emf.common.util.URI;
9
10public record TransitiveImport(URI uri) implements Import {
11}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java
index 59e26561..0bd1e50b 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java
@@ -1,5 +1,5 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
@@ -11,6 +11,7 @@ import org.eclipse.emf.ecore.EObject;
11import org.eclipse.emf.ecore.resource.Resource; 11import org.eclipse.emf.ecore.resource.Resource;
12import org.eclipse.xtext.util.IResourceScopeCache; 12import org.eclipse.xtext.util.IResourceScopeCache;
13import org.eclipse.xtext.util.Tuples; 13import org.eclipse.xtext.util.Tuples;
14import tools.refinery.language.library.BuiltinLibrary;
14import tools.refinery.language.model.problem.*; 15import tools.refinery.language.model.problem.*;
15 16
16import java.util.*; 17import java.util.*;
@@ -27,8 +28,8 @@ public class ProblemDesugarer {
27 28
28 private Optional<Problem> doGetBuiltinProblem(Resource resource) { 29 private Optional<Problem> doGetBuiltinProblem(Resource resource) {
29 return Optional.ofNullable(resource).map(Resource::getResourceSet) 30 return Optional.ofNullable(resource).map(Resource::getResourceSet)
30 .map(resourceSet -> resourceSet.getResource(ProblemUtil.BUILTIN_LIBRARY_URI, true)) 31 .map(resourceSet -> resourceSet.getResource(BuiltinLibrary.BUILTIN_LIBRARY_URI, true))
31 .map(Resource::getContents).filter(contents -> !contents.isEmpty()).map(contents -> contents.get(0)) 32 .map(Resource::getContents).filter(contents -> !contents.isEmpty()).map(List::getFirst)
32 .filter(Problem.class::isInstance).map(Problem.class::cast); 33 .filter(Problem.class::isInstance).map(Problem.class::cast);
33 } 34 }
34 35
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java
index 0f87c04b..23ff55e7 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java
@@ -5,16 +5,13 @@
5 */ 5 */
6package tools.refinery.language.utils; 6package tools.refinery.language.utils;
7 7
8import org.eclipse.emf.common.util.URI;
9import org.eclipse.emf.ecore.EObject; 8import org.eclipse.emf.ecore.EObject;
10import org.eclipse.emf.ecore.util.EcoreUtil; 9import org.eclipse.emf.ecore.util.EcoreUtil;
11import org.eclipse.xtext.EcoreUtil2; 10import org.eclipse.xtext.EcoreUtil2;
11import tools.refinery.language.library.BuiltinLibrary;
12import tools.refinery.language.model.problem.*; 12import tools.refinery.language.model.problem.*;
13 13
14public final class ProblemUtil { 14public final class ProblemUtil {
15 public static final String BUILTIN_LIBRARY_NAME = "builtin";
16 public static final URI BUILTIN_LIBRARY_URI = getLibraryUri();
17
18 private ProblemUtil() { 15 private ProblemUtil() {
19 throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); 16 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
20 } 17 }
@@ -23,7 +20,7 @@ public final class ProblemUtil {
23 if (eObject != null) { 20 if (eObject != null) {
24 var eResource = eObject.eResource(); 21 var eResource = eObject.eResource();
25 if (eResource != null) { 22 if (eResource != null) {
26 return ProblemUtil.BUILTIN_LIBRARY_URI.equals(eResource.getURI()); 23 return BuiltinLibrary.BUILTIN_LIBRARY_URI.equals(eResource.getURI());
27 } 24 }
28 } 25 }
29 return false; 26 return false;
@@ -131,13 +128,4 @@ public final class ProblemUtil {
131 var problem = EcoreUtil2.getContainerOfType(eObject, Problem.class); 128 var problem = EcoreUtil2.getContainerOfType(eObject, Problem.class);
132 return problem != null && problem.getKind() == ModuleKind.MODULE; 129 return problem != null && problem.getKind() == ModuleKind.MODULE;
133 } 130 }
134
135 private static URI getLibraryUri() {
136 var libraryResource = ProblemUtil.class.getClassLoader()
137 .getResource("tools/refinery/language/%s.problem".formatted(BUILTIN_LIBRARY_NAME));
138 if (libraryResource == null) {
139 throw new AssertionError("Library '%s' was not found".formatted(BUILTIN_LIBRARY_NAME));
140 }
141 return URI.createURI(libraryResource.toString());
142 }
143} 131}
diff --git a/subprojects/language/src/main/resources/META-INF/services/tools.refinery.language.library.RefineryLibrary b/subprojects/language/src/main/resources/META-INF/services/tools.refinery.language.library.RefineryLibrary
new file mode 100644
index 00000000..bb7e369d
--- /dev/null
+++ b/subprojects/language/src/main/resources/META-INF/services/tools.refinery.language.library.RefineryLibrary
@@ -0,0 +1,4 @@
1# SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2#
3# SPDX-License-Identifier: EPL-2.0
4tools.refinery.language.library.BuiltinLibrary
diff --git a/subprojects/language/src/main/resources/tools/refinery/language/builtin.problem b/subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery
index 022c3167..022c3167 100644
--- a/subprojects/language/src/main/resources/tools/refinery/language/builtin.problem
+++ b/subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery