aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language/src
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2024-02-02 16:28:19 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2024-02-02 17:36:24 +0100
commit2dfcb286216419976368ad926f8ac7f018aa2bf9 (patch)
treeb9d235ebf2049e42e58126e743c782333d64681a /subprojects/language/src
parentrefactor: serialize solutions as modules (diff)
downloadrefinery-2dfcb286216419976368ad926f8ac7f018aa2bf9.tar.gz
refinery-2dfcb286216419976368ad926f8ac7f018aa2bf9.tar.zst
refinery-2dfcb286216419976368ad926f8ac7f018aa2bf9.zip
refactor(language): name disambiguation
* Use fully qualified names starting with :: (as in C++) to unambiguously refer to an element. * Name shadowing within modules.
Diffstat (limited to 'subprojects/language/src')
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/Problem.xtext14
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java23
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java33
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/naming/ProblemDelegateQualifiedNameProvider.java18
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java2
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameProvider.java41
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/parser/antlr/IdentifierTokenProvider.java25
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/parser/antlr/ProblemTokenSource.java38
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescription.java106
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionManager.java19
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java62
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/NormalizedSelectable.java122
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java23
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java49
14 files changed, 488 insertions, 87 deletions
diff --git a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
index 4ce3fae1..f0d6c38c 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
+++ b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
@@ -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 */
@@ -16,10 +16,13 @@ enum ModuleKind:
16 PROBLEM="problem" | MODULE="module"; 16 PROBLEM="problem" | MODULE="module";
17 17
18Statement: 18Statement:
19 Assertion | ClassDeclaration | EnumDeclaration | 19 ImportStatement | Assertion | ClassDeclaration | EnumDeclaration |
20 PredicateDefinition | /* FunctionDefinition | RuleDefinition | */ 20 PredicateDefinition | /* FunctionDefinition | RuleDefinition | */
21 ScopeDeclaration | NodeDeclaration; 21 ScopeDeclaration | NodeDeclaration;
22 22
23ImportStatement:
24 "import" importedModule=[Problem|QualifiedName] ("as" alias=ID)? ".";
25
23ClassDeclaration: 26ClassDeclaration:
24 abstract?="abstract"? "class" 27 abstract?="abstract"? "class"
25 name=Identifier 28 name=Identifier
@@ -266,10 +269,10 @@ UpperBound returns ecore::EInt:
266 INT | "*"; 269 INT | "*";
267 270
268QualifiedName hidden(): 271QualifiedName hidden():
269 Identifier ("::" Identifier)*; 272 "::"? Identifier (QUALIFIED_NAME_SEPARATOR Identifier)*;
270 273
271NonContainmentQualifiedName hidden(): 274NonContainmentQualifiedName hidden():
272 NonContainmentIdentifier ("::" Identifier)*; 275 (NonContainmentIdentifier | "::" Identifier) (QUALIFIED_NAME_SEPARATOR Identifier)*;
273 276
274Identifier: 277Identifier:
275 NonContainmentIdentifier | "contains" | "container"; 278 NonContainmentIdentifier | "contains" | "container";
@@ -284,6 +287,9 @@ Real returns ecore::EDouble:
284terminal TRANSITIVE_CLOSURE: 287terminal TRANSITIVE_CLOSURE:
285 "synthetic:TRANSITIVE_CLOSURE"; 288 "synthetic:TRANSITIVE_CLOSURE";
286 289
290terminal QUALIFIED_NAME_SEPARATOR:
291 "synthetic::QUALIFIED_NAME_SEPARATOR";
292
287@Override 293@Override
288terminal ID: 294terminal ID:
289 ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*; 295 ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*;
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 00dd3de3..19816da4 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.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 */
@@ -14,6 +14,7 @@ import com.google.inject.name.Names;
14import org.eclipse.xtext.conversion.IValueConverterService; 14import org.eclipse.xtext.conversion.IValueConverterService;
15import org.eclipse.xtext.linking.ILinkingService; 15import org.eclipse.xtext.linking.ILinkingService;
16import org.eclipse.xtext.naming.IQualifiedNameConverter; 16import org.eclipse.xtext.naming.IQualifiedNameConverter;
17import org.eclipse.xtext.naming.IQualifiedNameProvider;
17import org.eclipse.xtext.parser.IParser; 18import org.eclipse.xtext.parser.IParser;
18import org.eclipse.xtext.resource.*; 19import org.eclipse.xtext.resource.*;
19import org.eclipse.xtext.scoping.IGlobalScopeProvider; 20import org.eclipse.xtext.scoping.IGlobalScopeProvider;
@@ -26,12 +27,11 @@ import org.eclipse.xtext.validation.IResourceValidator;
26import org.eclipse.xtext.xbase.annotations.validation.DerivedStateAwareResourceValidator; 27import org.eclipse.xtext.xbase.annotations.validation.DerivedStateAwareResourceValidator;
27import tools.refinery.language.conversion.ProblemValueConverterService; 28import tools.refinery.language.conversion.ProblemValueConverterService;
28import tools.refinery.language.linking.ProblemLinkingService; 29import tools.refinery.language.linking.ProblemLinkingService;
30import tools.refinery.language.naming.ProblemDelegateQualifiedNameProvider;
29import tools.refinery.language.naming.ProblemQualifiedNameConverter; 31import tools.refinery.language.naming.ProblemQualifiedNameConverter;
32import tools.refinery.language.naming.ProblemQualifiedNameProvider;
30import tools.refinery.language.parser.antlr.TokenSourceInjectingProblemParser; 33import tools.refinery.language.parser.antlr.TokenSourceInjectingProblemParser;
31import tools.refinery.language.resource.ProblemDerivedStateComputer; 34import tools.refinery.language.resource.*;
32import tools.refinery.language.resource.ProblemLocationInFileProvider;
33import tools.refinery.language.resource.ProblemResource;
34import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
35import tools.refinery.language.scoping.ProblemGlobalScopeProvider; 35import tools.refinery.language.scoping.ProblemGlobalScopeProvider;
36import tools.refinery.language.scoping.ProblemLocalScopeProvider; 36import tools.refinery.language.scoping.ProblemLocalScopeProvider;
37import tools.refinery.language.serializer.PreferShortAssertionsProblemSemanticSequencer; 37import tools.refinery.language.serializer.PreferShortAssertionsProblemSemanticSequencer;
@@ -54,6 +54,17 @@ public class ProblemRuntimeModule extends AbstractProblemRuntimeModule {
54 return ProblemQualifiedNameConverter.class; 54 return ProblemQualifiedNameConverter.class;
55 } 55 }
56 56
57 public void configureIQualifiedNameProviderDelegate(Binder binder) {
58 binder.bind(IQualifiedNameProvider.class)
59 .annotatedWith(Names.named(ProblemQualifiedNameProvider.NAMED_DELEGATE))
60 .to(ProblemDelegateQualifiedNameProvider.class);
61 }
62
63 @Override
64 public Class<? extends IQualifiedNameProvider> bindIQualifiedNameProvider() {
65 return ProblemQualifiedNameProvider.class;
66 }
67
57 public Class<? extends IDefaultResourceDescriptionStrategy> bindIDefaultResourceDescriptionStrategy() { 68 public Class<? extends IDefaultResourceDescriptionStrategy> bindIDefaultResourceDescriptionStrategy() {
58 return ProblemResourceDescriptionStrategy.class; 69 return ProblemResourceDescriptionStrategy.class;
59 } 70 }
@@ -87,7 +98,7 @@ public class ProblemRuntimeModule extends AbstractProblemRuntimeModule {
87 // Method name follows Xtext convention. 98 // Method name follows Xtext convention.
88 @SuppressWarnings("squid:S100") 99 @SuppressWarnings("squid:S100")
89 public Class<? extends IResourceDescription.Manager> bindIResourceDescription$Manager() { 100 public Class<? extends IResourceDescription.Manager> bindIResourceDescription$Manager() {
90 return DerivedStateAwareResourceDescriptionManager.class; 101 return ProblemResourceDescriptionManager.class;
91 } 102 }
92 103
93 public Class<? extends IResourceValidator> bindIResourceValidator() { 104 public Class<? extends IResourceValidator> bindIResourceValidator() {
diff --git a/subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java b/subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java
index 1647d4e7..feae5ebb 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java
@@ -1,21 +1,24 @@
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 */
6package tools.refinery.language.naming; 6package tools.refinery.language.naming;
7 7
8import org.eclipse.xtext.naming.QualifiedName;
9
8import java.util.regex.Pattern; 10import java.util.regex.Pattern;
9 11
10public final class NamingUtil { 12public final class NamingUtil {
11 private static final String SINGLETON_VARIABLE_PREFIX = "_"; 13 private static final String SINGLETON_VARIABLE_PREFIX = "_";
12 14 public static final QualifiedName ROOT_NAME = QualifiedName.create("");
13 private static final Pattern ID_REGEX = Pattern.compile("[_a-zA-Z][_0-9a-zA-Z]*"); 15
16 private static final Pattern ID_REGEX = Pattern.compile("[_a-zA-Z]\\w*");
14 17
15 private NamingUtil() { 18 private NamingUtil() {
16 throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); 19 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
17 } 20 }
18 21
19 public static boolean isNullOrEmpty(String name) { 22 public static boolean isNullOrEmpty(String name) {
20 return name == null || name.isEmpty(); 23 return name == null || name.isEmpty();
21 } 24 }
@@ -23,8 +26,28 @@ public final class NamingUtil {
23 public static boolean isSingletonVariableName(String name) { 26 public static boolean isSingletonVariableName(String name) {
24 return name != null && name.startsWith(SINGLETON_VARIABLE_PREFIX); 27 return name != null && name.startsWith(SINGLETON_VARIABLE_PREFIX);
25 } 28 }
26 29
30 // This method name only makes sense if it checks for the positive case.
31 @SuppressWarnings("BooleanMethodIsAlwaysInverted")
27 public static boolean isValidId(String name) { 32 public static boolean isValidId(String name) {
28 return name != null && ID_REGEX.matcher(name).matches(); 33 return name != null && ID_REGEX.matcher(name).matches();
29 } 34 }
35
36 public static boolean isFullyQualified(QualifiedName name) {
37 return name.startsWith(ROOT_NAME);
38 }
39
40 public static QualifiedName stripRootPrefix(QualifiedName name) {
41 if (name == null) {
42 return null;
43 }
44 return isFullyQualified(name) ? name.skipFirst(ROOT_NAME.getSegmentCount()) : name;
45 }
46
47 public static QualifiedName addRootPrefix(QualifiedName name) {
48 if (name == null) {
49 return null;
50 }
51 return isFullyQualified(name) ? name : ROOT_NAME.append(name);
52 }
30} 53}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemDelegateQualifiedNameProvider.java b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemDelegateQualifiedNameProvider.java
new file mode 100644
index 00000000..1e78cee1
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemDelegateQualifiedNameProvider.java
@@ -0,0 +1,18 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.naming;
7
8import org.eclipse.xtext.naming.DefaultDeclarativeQualifiedNameProvider;
9import org.eclipse.xtext.naming.QualifiedName;
10import tools.refinery.language.model.problem.Problem;
11
12public class ProblemDelegateQualifiedNameProvider extends DefaultDeclarativeQualifiedNameProvider {
13 protected QualifiedName qualifiedName(Problem ele) {
14 var qualifiedName = computeFullyQualifiedNameFromNameAttribute(ele);
15 // Strip the root prefix even if explicitly provided.
16 return NamingUtil.stripRootPrefix(qualifiedName);
17 }
18}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java
index 74b4e208..88a0fe9a 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java
@@ -12,7 +12,7 @@ import com.google.inject.Singleton;
12@Singleton 12@Singleton
13public class ProblemQualifiedNameConverter extends IQualifiedNameConverter.DefaultImpl { 13public class ProblemQualifiedNameConverter extends IQualifiedNameConverter.DefaultImpl {
14 public static final String DELIMITER = "::"; 14 public static final String DELIMITER = "::";
15 15
16 @Override 16 @Override
17 public String getDelimiter() { 17 public String getDelimiter() {
18 return DELIMITER; 18 return DELIMITER;
diff --git a/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameProvider.java b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameProvider.java
new file mode 100644
index 00000000..5b682058
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameProvider.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.naming;
7
8import com.google.inject.Inject;
9import com.google.inject.name.Named;
10import org.eclipse.emf.ecore.EObject;
11import org.eclipse.xtext.naming.IQualifiedNameProvider;
12import org.eclipse.xtext.naming.QualifiedName;
13import org.eclipse.xtext.util.IResourceScopeCache;
14import org.eclipse.xtext.util.Tuples;
15import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
16
17public class ProblemQualifiedNameProvider extends IQualifiedNameProvider.AbstractImpl {
18 private static final String PREFIX = "tools.refinery.language.naming.ProblemQualifiedNameProvider.";
19 public static final String NAMED_DELEGATE = PREFIX + "NAMED_DELEGATE";
20 public static final String CACHE_KEY = PREFIX + "CACHE_KEY";
21
22 @Inject
23 @Named(NAMED_DELEGATE)
24 private IQualifiedNameProvider delegate;
25
26 @Inject
27 private IResourceScopeCache cache = IResourceScopeCache.NullImpl.INSTANCE;
28
29 @Override
30 public QualifiedName getFullyQualifiedName(EObject obj) {
31 return cache.get(Tuples.pair(obj, CACHE_KEY), obj.eResource(), () -> computeFullyQualifiedName(obj));
32 }
33
34 public QualifiedName computeFullyQualifiedName(EObject obj) {
35 var qualifiedName = delegate.getFullyQualifiedName(obj);
36 if (qualifiedName != null && ProblemResourceDescriptionStrategy.shouldExport(obj)) {
37 return NamingUtil.addRootPrefix(qualifiedName);
38 }
39 return qualifiedName;
40 }
41}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/IdentifierTokenProvider.java b/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/IdentifierTokenProvider.java
index 306a86fc..0e19357f 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/IdentifierTokenProvider.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/IdentifierTokenProvider.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 */
@@ -59,7 +59,7 @@ public class IdentifierTokenProvider {
59 59
60 private void createValueToTokenIdMap() { 60 private void createValueToTokenIdMap() {
61 var tokenIdToValueMap = tokenDefProvider.getTokenDefMap(); 61 var tokenIdToValueMap = tokenDefProvider.getTokenDefMap();
62 valueToTokenIdMap = new HashMap<>(tokenIdToValueMap.size()); 62 valueToTokenIdMap = HashMap.newHashMap(tokenIdToValueMap.size());
63 for (var entry : tokenIdToValueMap.entrySet()) { 63 for (var entry : tokenIdToValueMap.entrySet()) {
64 valueToTokenIdMap.put(entry.getValue(), entry.getKey()); 64 valueToTokenIdMap.put(entry.getValue(), entry.getKey());
65 } 65 }
@@ -74,17 +74,16 @@ public class IdentifierTokenProvider {
74 } 74 }
75 75
76 private void collectIdentifierTokensFromElement(AbstractElement element) { 76 private void collectIdentifierTokensFromElement(AbstractElement element) {
77 if (element instanceof Alternatives alternatives) { 77 switch (element) {
78 for (var alternative : alternatives.getElements()) { 78 case Alternatives alternatives -> {
79 collectIdentifierTokensFromElement(alternative); 79 for (var alternative : alternatives.getElements()) {
80 } 80 collectIdentifierTokensFromElement(alternative);
81 } else if (element instanceof RuleCall ruleCall) { 81 }
82 collectIdentifierTokensFromRule(ruleCall.getRule()); 82 }
83 } else if (element instanceof Keyword keyword) { 83 case RuleCall ruleCall -> collectIdentifierTokensFromRule(ruleCall.getRule());
84 collectToken("'" + keyword.getValue() + "'"); 84 case Keyword keyword -> collectToken("'" + keyword.getValue() + "'");
85 } else { 85 default -> throw new IllegalArgumentException("Unknown Xtext grammar element: " + element);
86 throw new IllegalArgumentException("Unknown Xtext grammar element: " + element); 86 }
87 }
88 } 87 }
89 88
90 private void collectToken(String value) { 89 private void collectToken(String value) {
diff --git a/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/ProblemTokenSource.java b/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/ProblemTokenSource.java
index 5b91a6cc..487e4ceb 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/ProblemTokenSource.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/ProblemTokenSource.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 */
@@ -28,6 +28,8 @@ public class ProblemTokenSource implements TokenSource {
28 28
29 private boolean seenId; 29 private boolean seenId;
30 30
31 private boolean lastVisible;
32
31 public ProblemTokenSource(TokenSource delegate) { 33 public ProblemTokenSource(TokenSource delegate) {
32 this.delegate = delegate; 34 this.delegate = delegate;
33 } 35 }
@@ -47,18 +49,18 @@ public class ProblemTokenSource implements TokenSource {
47 49
48 @Override 50 @Override
49 public Token nextToken() { 51 public Token nextToken() {
50 if (!buffer.isEmpty()) { 52 boolean fromStream = buffer.isEmpty();
51 return buffer.removeFirst(); 53 var token = fromStream ? delegate.nextToken() : buffer.removeFirst();
52 } 54 if (seenId) {
53 var token = delegate.nextToken(); 55 if (fromStream && isPlusOrTransitiveClosure(token) && peekForTransitiveClosure()) {
54 if (isIdentifier(token)) {
55 seenId = true;
56 } else if (seenId && isPlusOrTransitiveClosure(token)) {
57 if (peekForTransitiveClosure()) {
58 token.setType(InternalProblemParser.RULE_TRANSITIVE_CLOSURE); 56 token.setType(InternalProblemParser.RULE_TRANSITIVE_CLOSURE);
57 } else if (lastVisible && isQualifiedNameSeparator(token)) {
58 token.setType(InternalProblemParser.RULE_QUALIFIED_NAME_SEPARATOR);
59 } 59 }
60 } else if (isVisibleToken(token)) { 60 }
61 seenId = false; 61 lastVisible = isVisibleToken(token);
62 if (lastVisible) {
63 seenId = isIdentifier(token);
62 } 64 }
63 return token; 65 return token;
64 } 66 }
@@ -76,6 +78,10 @@ public class ProblemTokenSource implements TokenSource {
76 return token.getType() == InternalProblemParser.PlusSign; 78 return token.getType() == InternalProblemParser.PlusSign;
77 } 79 }
78 80
81 protected boolean isQualifiedNameSeparator(Token token) {
82 return token.getType() == InternalProblemParser.ColonColon;
83 }
84
79 protected boolean isVisibleToken(Token token) { 85 protected boolean isVisibleToken(Token token) {
80 int tokenId = token.getType(); 86 int tokenId = token.getType();
81 return tokenId != InternalProblemParser.RULE_WS && tokenId != InternalProblemParser.RULE_SL_COMMENT && 87 return tokenId != InternalProblemParser.RULE_WS && tokenId != InternalProblemParser.RULE_SL_COMMENT &&
@@ -87,11 +93,16 @@ public class ProblemTokenSource implements TokenSource {
87 if (token.getType() != InternalProblemParser.LeftParenthesis) { 93 if (token.getType() != InternalProblemParser.LeftParenthesis) {
88 return false; 94 return false;
89 } 95 }
96 boolean allowFullyQualifiedName = true;
90 while (true) { 97 while (true) {
91 token = peekWithSkipWhitespace(); 98 token = peekWithSkipWhitespace();
99 if (allowFullyQualifiedName && token.getType() == InternalProblemParser.ColonColon) {
100 token = peekWithSkipWhitespace();
101 }
92 if (!isIdentifier(token)) { 102 if (!isIdentifier(token)) {
93 return false; 103 return false;
94 } 104 }
105 allowFullyQualifiedName = false;
95 token = peekWithSkipWhitespace(); 106 token = peekWithSkipWhitespace();
96 switch (token.getType()) { 107 switch (token.getType()) {
97 case InternalProblemParser.Comma: 108 case InternalProblemParser.Comma:
@@ -112,11 +123,6 @@ public class ProblemTokenSource implements TokenSource {
112 123
113 protected Token peekToken() { 124 protected Token peekToken() {
114 var token = delegate.nextToken(); 125 var token = delegate.nextToken();
115 if (isIdentifier(token)) {
116 seenId = true;
117 } else if (isVisibleToken(token)) {
118 seenId = false;
119 }
120 buffer.addLast(token); 126 buffer.addLast(token);
121 return token; 127 return token;
122 } 128 }
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescription.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescription.java
new file mode 100644
index 00000000..498a7c57
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescription.java
@@ -0,0 +1,106 @@
1/*******************************************************************************
2 * Copyright (c) 2009, 2011 itemis AG (http://www.itemis.eu) and others.
3 * Copyright (c) 2024 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-2.0.
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.language.resource;
10
11import org.apache.log4j.Logger;
12import org.eclipse.emf.common.util.TreeIterator;
13import org.eclipse.emf.ecore.EObject;
14import org.eclipse.emf.ecore.resource.Resource;
15import org.eclipse.emf.ecore.util.EcoreUtil;
16import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy;
17import org.eclipse.xtext.resource.IEObjectDescription;
18import org.eclipse.xtext.resource.impl.DefaultResourceDescription;
19import org.eclipse.xtext.resource.impl.EObjectDescriptionLookUp;
20import org.eclipse.xtext.util.IAcceptor;
21import tools.refinery.language.naming.NamingUtil;
22
23import java.io.IOException;
24import java.util.*;
25
26/**
27 * A resource description that takes {@link ProblemResourceDescriptionStrategy#SHADOWING_KEY} into account when
28 * describing EObjects.
29 * <p>
30 * Based on {@link DefaultResourceDescription}.
31 */
32public class ProblemResourceDescription extends DefaultResourceDescription {
33 private static final Logger log = Logger.getLogger(ProblemResourceDescription.class);
34
35 private final IDefaultResourceDescriptionStrategy strategy;
36
37 public ProblemResourceDescription(Resource resource, IDefaultResourceDescriptionStrategy strategy) {
38 super(resource, strategy);
39 this.strategy = strategy;
40 }
41
42 /**
43 * Based on {@link DefaultResourceDescription#computeExportedObjects()}.
44 *
45 * @return The computed exported objects, taking shadowing into account.
46 */
47 @Override
48 protected List<IEObjectDescription> computeExportedObjects() {
49 if (!getResource().isLoaded()) {
50 try {
51 getResource().load(null);
52 } catch (IOException e) {
53 log.error(e.getMessage(), e);
54 return Collections.emptyList();
55 }
56 }
57 final Map<ProblemResourceDescriptionStrategy.ShadowingKey, List<IEObjectDescription>> nameToDescriptionsMap =
58 new LinkedHashMap<>();
59 IAcceptor<IEObjectDescription> acceptor = eObjectDescription -> {
60 var key = ProblemResourceDescriptionStrategy.getShadowingKey(eObjectDescription);
61 var descriptions = nameToDescriptionsMap.computeIfAbsent(key, ignored -> new ArrayList<>());
62 descriptions.add(eObjectDescription);
63 };
64 TreeIterator<EObject> allProperContents = EcoreUtil.getAllProperContents(getResource(), false);
65 while (allProperContents.hasNext()) {
66 EObject content = allProperContents.next();
67 if (!strategy.createEObjectDescriptions(content, acceptor)) {
68 allProperContents.prune();
69 }
70 }
71 return omitShadowedNames(nameToDescriptionsMap);
72 }
73
74 private static List<IEObjectDescription> omitShadowedNames(
75 Map<ProblemResourceDescriptionStrategy.ShadowingKey, List<IEObjectDescription>> nameToDescriptionsMap) {
76 final List<IEObjectDescription> exportedEObjects = new ArrayList<>();
77 for (var entry : nameToDescriptionsMap.entrySet()) {
78 var descriptions = entry.getValue();
79 if (NamingUtil.isFullyQualified(entry.getKey().name())) {
80 exportedEObjects.addAll(descriptions);
81 } else {
82 boolean foundPreferred = false;
83 for (var description : descriptions) {
84 if (ProblemResourceDescriptionStrategy.PREFERRED_NAME_TRUE.equals(
85 description.getUserData(ProblemResourceDescriptionStrategy.PREFERRED_NAME))) {
86 exportedEObjects.add(description);
87 foundPreferred = true;
88 }
89 }
90 if (!foundPreferred) {
91 exportedEObjects.addAll(descriptions);
92 }
93 }
94 }
95 return exportedEObjects;
96 }
97
98 // Based on {@code DerivedStateAwareResourceDescriptionManager#createResourceDescription}.
99 @Override
100 protected EObjectDescriptionLookUp getLookUp() {
101 if (lookup == null) {
102 lookup = new EObjectDescriptionLookUp(computeExportedObjects());
103 }
104 return lookup;
105 }
106}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionManager.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionManager.java
new file mode 100644
index 00000000..23ca139a
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionManager.java
@@ -0,0 +1,19 @@
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 org.eclipse.emf.ecore.resource.Resource;
9import org.eclipse.xtext.resource.DerivedStateAwareResourceDescriptionManager;
10import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy;
11import org.eclipse.xtext.resource.IResourceDescription;
12
13public class ProblemResourceDescriptionManager extends DerivedStateAwareResourceDescriptionManager {
14 @Override
15 protected IResourceDescription createResourceDescription(Resource resource,
16 IDefaultResourceDescriptionStrategy strategy) {
17 return new ProblemResourceDescription(resource, strategy);
18 }
19}
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 79dad6e7..76fd5852 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
@@ -25,9 +25,16 @@ import java.util.Map;
25@Singleton 25@Singleton
26public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy { 26public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy {
27 private static final String DATA_PREFIX = "tools.refinery.language.resource.ProblemResourceDescriptionStrategy."; 27 private static final String DATA_PREFIX = "tools.refinery.language.resource.ProblemResourceDescriptionStrategy.";
28
28 public static final String ARITY = DATA_PREFIX + "ARITY"; 29 public static final String ARITY = DATA_PREFIX + "ARITY";
29 public static final String ERROR_PREDICATE = DATA_PREFIX + "ERROR_PREDICATE"; 30 public static final String ERROR_PREDICATE = DATA_PREFIX + "ERROR_PREDICATE";
30 public static final String ERROR_PREDICATE_TRUE = "true"; 31 public static final String ERROR_PREDICATE_TRUE = "true";
32 public static final String SHADOWING_KEY = DATA_PREFIX + "SHADOWING_KEY";
33 public static final String SHADOWING_KEY_PROBLEM = "problem";
34 public static final String SHADOWING_KEY_NODE = "node";
35 public static final String SHADOWING_KEY_RELATION = "relation";
36 public static final String PREFERRED_NAME = DATA_PREFIX + "PREFERRED_NAME";
37 public static final String PREFERRED_NAME_TRUE = "true";
31 public static final String COLOR_RELATION = DATA_PREFIX + "COLOR_RELATION"; 38 public static final String COLOR_RELATION = DATA_PREFIX + "COLOR_RELATION";
32 public static final String COLOR_RELATION_TRUE = "true"; 39 public static final String COLOR_RELATION_TRUE = "true";
33 40
@@ -44,8 +51,12 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
44 return true; 51 return true;
45 } 52 }
46 var problem = EcoreUtil2.getContainerOfType(eObject, Problem.class); 53 var problem = EcoreUtil2.getContainerOfType(eObject, Problem.class);
47 var problemQualifiedName = getNameAsQualifiedName(problem);
48 var userData = getUserData(eObject); 54 var userData = getUserData(eObject);
55 if (eObject.equals(problem)) {
56 acceptEObjectDescription(eObject, qualifiedName, QualifiedName.EMPTY, userData, true, acceptor);
57 return true;
58 }
59 var problemQualifiedName = getNameAsQualifiedName(problem);
49 QualifiedName lastQualifiedNameToExport = null; 60 QualifiedName lastQualifiedNameToExport = null;
50 if (shouldExportSimpleName(eObject)) { 61 if (shouldExportSimpleName(eObject)) {
51 lastQualifiedNameToExport = qualifiedName; 62 lastQualifiedNameToExport = qualifiedName;
@@ -82,10 +93,14 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
82 if (NamingUtil.isNullOrEmpty(name)) { 93 if (NamingUtil.isNullOrEmpty(name)) {
83 return null; 94 return null;
84 } 95 }
85 return qualifiedNameConverter.toQualifiedName(name); 96 var qualifiedName = qualifiedNameConverter.toQualifiedName(name);
97 if (eObject instanceof Problem) {
98 return NamingUtil.stripRootPrefix(qualifiedName);
99 }
100 return qualifiedName;
86 } 101 }
87 102
88 protected boolean shouldExport(EObject eObject) { 103 public static boolean shouldExport(EObject eObject) {
89 if (eObject instanceof Variable) { 104 if (eObject instanceof Variable) {
90 // Variables are always private to the containing predicate definition. 105 // Variables are always private to the containing predicate definition.
91 return false; 106 return false;
@@ -98,7 +113,12 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
98 113
99 protected Map<String, String> getUserData(EObject eObject) { 114 protected Map<String, String> getUserData(EObject eObject) {
100 var builder = ImmutableMap.<String, String>builder(); 115 var builder = ImmutableMap.<String, String>builder();
101 if (eObject instanceof Relation relation) { 116 if (eObject instanceof Problem) {
117 builder.put(SHADOWING_KEY, SHADOWING_KEY_PROBLEM);
118 } else if (eObject instanceof Node) {
119 builder.put(SHADOWING_KEY, SHADOWING_KEY_NODE);
120 } else if (eObject instanceof Relation relation) {
121 builder.put(SHADOWING_KEY, SHADOWING_KEY_RELATION);
102 int arity = ProblemUtil.getArity(relation); 122 int arity = ProblemUtil.getArity(relation);
103 builder.put(ARITY, Integer.toString(arity)); 123 builder.put(ARITY, Integer.toString(arity));
104 } 124 }
@@ -124,20 +144,31 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
124 } 144 }
125 145
126 private void acceptEObjectDescription(EObject eObject, QualifiedName prefix, QualifiedName qualifiedName, 146 private void acceptEObjectDescription(EObject eObject, QualifiedName prefix, QualifiedName qualifiedName,
127 Map<String, String> userData, boolean fullyQualified, 147 Map<String, String> userData, boolean preferredName,
128 IAcceptor<IEObjectDescription> acceptor) { 148 IAcceptor<IEObjectDescription> acceptor) {
129 var qualifiedNameWithPrefix = prefix == null ? qualifiedName : prefix.append(qualifiedName); 149 var qualifiedNameWithPrefix = prefix == null ? qualifiedName : prefix.append(qualifiedName);
130 Map<String, String> userDataWithFullyQualified; 150 var userDataWithPreference = userData;
131 if (fullyQualified && shouldColorRelation(eObject)) { 151 if (preferredName) {
132 userDataWithFullyQualified = ImmutableMap.<String, String>builder() 152 userDataWithPreference = ImmutableMap.<String, String>builder()
133 .putAll(userData) 153 .putAll(userData)
134 .put(COLOR_RELATION, COLOR_RELATION_TRUE) 154 .put(PREFERRED_NAME, PREFERRED_NAME_TRUE)
135 .build(); 155 .build();
136 } else {
137 userDataWithFullyQualified = userData;
138 } 156 }
139 var description = EObjectDescription.create(qualifiedNameWithPrefix, eObject, userDataWithFullyQualified); 157 var description = EObjectDescription.create(qualifiedNameWithPrefix, eObject, userDataWithPreference);
140 acceptor.accept(description); 158 acceptor.accept(description);
159 if (!preferredName) {
160 return;
161 }
162 var userDataWithFullyQualified = userDataWithPreference;
163 if (shouldColorRelation(eObject)) {
164 userDataWithFullyQualified = ImmutableMap.<String, String>builder()
165 .putAll(userDataWithPreference)
166 .put(COLOR_RELATION, COLOR_RELATION_TRUE)
167 .build();
168 }
169 var rootQualifiedName = NamingUtil.addRootPrefix(qualifiedNameWithPrefix);
170 var rootDescription = EObjectDescription.create(rootQualifiedName, eObject, userDataWithFullyQualified);
171 acceptor.accept(rootDescription);
141 } 172 }
142 173
143 private boolean shouldColorRelation(EObject eObject) { 174 private boolean shouldColorRelation(EObject eObject) {
@@ -146,4 +177,11 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
146 } 177 }
147 return eObject instanceof ClassDeclaration || eObject instanceof EnumDeclaration; 178 return eObject instanceof ClassDeclaration || eObject instanceof EnumDeclaration;
148 } 179 }
180
181 public static ShadowingKey getShadowingKey(IEObjectDescription description) {
182 return new ShadowingKey(description.getName(), description.getUserData(SHADOWING_KEY));
183 }
184
185 public record ShadowingKey(QualifiedName name, String shadowingKey) {
186 }
149} 187}
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
new file mode 100644
index 00000000..0c7828d8
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/NormalizedSelectable.java
@@ -0,0 +1,122 @@
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;
14import org.eclipse.xtext.resource.impl.AliasedEObjectDescription;
15import org.jetbrains.annotations.NotNull;
16
17import java.util.Iterator;
18import java.util.NoSuchElementException;
19
20public class NormalizedSelectable implements ISelectable {
21 private final ISelectable delegateSelectable;
22 private final QualifiedName originalPrefix;
23 private final QualifiedName normalizedPrefix;
24
25 private NormalizedSelectable(ISelectable delegateSelectable, QualifiedName originalPrefix,
26 QualifiedName normalizedPrefix) {
27 this.delegateSelectable = delegateSelectable;
28 this.originalPrefix = originalPrefix;
29 this.normalizedPrefix = normalizedPrefix;
30 }
31
32 @Override
33 public boolean isEmpty() {
34 return delegateSelectable.isEmpty();
35 }
36
37 @Override
38 public Iterable<IEObjectDescription> getExportedObjects() {
39 var delegateIterable = delegateSelectable.getExportedObjects();
40 var aliasedIterable = getAliasedElements(delegateIterable);
41 return Iterables.concat(delegateIterable, aliasedIterable);
42 }
43
44 @Override
45 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 name.startsWith(normalizedPrefix);
49 if (startsWith && name.getSegmentCount() > normalizedPrefix.getSegmentCount()) {
50 var originalName = originalPrefix.append(name.skipFirst(normalizedPrefix.getSegmentCount()));
51 var originalIterable = Iterables.transform(
52 delegateSelectable.getExportedObjects(type, originalName, ignoreCase),
53 description -> {
54 var normalizedName = normalizedPrefix.append(
55 description.getName().skipFirst(originalPrefix.getSegmentCount()));
56 return new AliasedEObjectDescription(normalizedName, description);
57 });
58 return Iterables.concat(originalIterable, delegateIterable);
59 }
60 return delegateIterable;
61 }
62
63 @Override
64 public Iterable<IEObjectDescription> getExportedObjectsByType(EClass type) {
65 var delegateIterable = delegateSelectable.getExportedObjectsByType(type);
66 var aliasedIterable = getAliasedElements(delegateIterable);
67 return Iterables.concat(delegateIterable, aliasedIterable);
68 }
69
70 @Override
71 public Iterable<IEObjectDescription> getExportedObjectsByObject(EObject object) {
72 var delegateIterable = delegateSelectable.getExportedObjectsByObject(object);
73 var aliasedIterable = getAliasedElements(delegateIterable);
74 return Iterables.concat(delegateIterable, aliasedIterable);
75 }
76
77 private Iterable<IEObjectDescription> getAliasedElements(Iterable<IEObjectDescription> delegateIterable) {
78 return () -> new Iterator<>() {
79 private final Iterator<IEObjectDescription> delegateIterator = delegateIterable.iterator();
80 private IEObjectDescription next = computeNext();
81
82 @Override
83 public boolean hasNext() {
84 return next != null;
85 }
86
87 @Override
88 public IEObjectDescription next() {
89 if (!hasNext()) {
90 throw new NoSuchElementException();
91 }
92 var current = next;
93 next = computeNext();
94 return current;
95 }
96
97 private IEObjectDescription computeNext() {
98 while (delegateIterator.hasNext()) {
99 var description = delegateIterator.next();
100 var qualifiedName = description.getName();
101 if (qualifiedName.startsWith(originalPrefix) &&
102 qualifiedName.getSegmentCount() > originalPrefix.getSegmentCount()) {
103 var alias = normalizedPrefix.append(qualifiedName.skipFirst(originalPrefix.getSegmentCount()));
104 return new AliasedEObjectDescription(alias, description);
105 }
106 }
107 return null;
108 }
109 };
110 }
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}
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 4d2dd772..37a67c0c 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
@@ -5,14 +5,21 @@
5 */ 5 */
6package tools.refinery.language.scoping; 6package tools.refinery.language.scoping;
7 7
8import java.util.LinkedHashSet; 8import com.google.common.base.Predicate;
9
10import org.eclipse.emf.common.util.URI; 9import org.eclipse.emf.common.util.URI;
10import org.eclipse.emf.ecore.EClass;
11import org.eclipse.emf.ecore.resource.Resource; 11import org.eclipse.emf.ecore.resource.Resource;
12import org.eclipse.xtext.naming.QualifiedName;
13import org.eclipse.xtext.resource.IEObjectDescription;
14import org.eclipse.xtext.resource.IResourceDescriptions;
15import org.eclipse.xtext.resource.ISelectable;
16import org.eclipse.xtext.scoping.IScope;
12import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider; 17import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider;
13 18import org.eclipse.xtext.scoping.impl.SelectableBasedScope;
14import tools.refinery.language.utils.ProblemUtil; 19import tools.refinery.language.utils.ProblemUtil;
15 20
21import java.util.LinkedHashSet;
22
16public class ProblemGlobalScopeProvider extends ImportUriGlobalScopeProvider { 23public class ProblemGlobalScopeProvider extends ImportUriGlobalScopeProvider {
17 @Override 24 @Override
18 protected LinkedHashSet<URI> getImportedUris(Resource resource) { 25 protected LinkedHashSet<URI> getImportedUris(Resource resource) {
@@ -20,4 +27,14 @@ public class ProblemGlobalScopeProvider extends ImportUriGlobalScopeProvider {
20 importedUris.add(ProblemUtil.BUILTIN_LIBRARY_URI); 27 importedUris.add(ProblemUtil.BUILTIN_LIBRARY_URI);
21 return importedUris; 28 return importedUris;
22 } 29 }
30
31 @Override
32 protected IScope createLazyResourceScope(IScope parent, URI uri, IResourceDescriptions descriptions, EClass type,
33 Predicate<IEObjectDescription> filter, boolean ignoreCase) {
34 ISelectable description = descriptions.getResourceDescription(uri);
35 if (description != null && ProblemUtil.BUILTIN_LIBRARY_URI.equals(uri)) {
36 description = NormalizedSelectable.of(description, QualifiedName.create("builtin"), QualifiedName.EMPTY);
37 }
38 return SelectableBasedScope.createScope(parent, description, filter, type, ignoreCase);
39 }
23} 40}
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 229960a0..9be32636 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
@@ -1,47 +1,42 @@
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 */
6package tools.refinery.language.scoping; 6package tools.refinery.language.scoping;
7 7
8import java.util.List; 8import com.google.inject.Inject;
9
10import org.eclipse.emf.ecore.EObject;
11import org.eclipse.emf.ecore.resource.Resource; 9import org.eclipse.emf.ecore.resource.Resource;
10import org.eclipse.xtext.naming.IQualifiedNameProvider;
12import org.eclipse.xtext.naming.QualifiedName; 11import org.eclipse.xtext.naming.QualifiedName;
13import org.eclipse.xtext.resource.IResourceDescriptions;
14import org.eclipse.xtext.resource.IResourceDescriptionsProvider; 12import org.eclipse.xtext.resource.IResourceDescriptionsProvider;
15import org.eclipse.xtext.resource.ISelectable; 13import org.eclipse.xtext.resource.ISelectable;
16import org.eclipse.xtext.scoping.impl.ImportNormalizer; 14import org.eclipse.xtext.scoping.impl.SimpleLocalScopeProvider;
17import org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider; 15import tools.refinery.language.naming.NamingUtil;
18
19import com.google.inject.Inject;
20
21import tools.refinery.language.utils.ProblemUtil;
22 16
23public class ProblemLocalScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { 17public class ProblemLocalScopeProvider extends SimpleLocalScopeProvider {
24 private static final QualifiedName BUILTIN_LIBRARY_QUALIFIED_NAME = QualifiedName 18 @Inject
25 .create(ProblemUtil.BUILTIN_LIBRARY_NAME); 19 private IQualifiedNameProvider qualifiedNameProvider;
26 20
27 @Inject 21 @Inject
28 private IResourceDescriptionsProvider resourceDescriptionsProvider; 22 private IResourceDescriptionsProvider resourceDescriptionsProvider;
29 23
30 @Override 24 @Override
31 protected List<ImportNormalizer> getImplicitImports(boolean ignoreCase) { 25 protected ISelectable getAllDescriptions(Resource resource) {
32 return List.of(doCreateImportNormalizer(BUILTIN_LIBRARY_QUALIFIED_NAME, true, ignoreCase));
33 }
34
35 @Override
36 protected List<ImportNormalizer> getImportedNamespaceResolvers(EObject context, boolean ignoreCase) {
37 return List.of();
38 }
39
40 @Override
41 protected ISelectable internalGetAllDescriptions(Resource resource) {
42 // Force the use of ProblemResourceDescriptionStrategy to include all QualifiedNames of objects. 26 // Force the use of ProblemResourceDescriptionStrategy to include all QualifiedNames of objects.
43 IResourceDescriptions resourceDescriptions = resourceDescriptionsProvider 27 var resourceDescriptions = resourceDescriptionsProvider
44 .getResourceDescriptions(resource.getResourceSet()); 28 .getResourceDescriptions(resource.getResourceSet());
45 return resourceDescriptions.getResourceDescription(resource.getURI()); 29 var resourceDescription = resourceDescriptions.getResourceDescription(resource.getURI());
30 if (resourceDescription != null && !resource.getContents().isEmpty()) {
31 var rootElement = resource.getContents().getFirst();
32 if (rootElement != null) {
33 var rootName = NamingUtil.stripRootPrefix(qualifiedNameProvider.getFullyQualifiedName(rootElement));
34 if (rootName == null) {
35 return resourceDescription;
36 }
37 return NormalizedSelectable.of(resourceDescription, rootName, QualifiedName.EMPTY);
38 }
39 }
40 return resourceDescription;
46 } 41 }
47} 42}