diff options
author | 2021-11-05 19:54:27 +0100 | |
---|---|---|
committer | 2021-11-05 19:54:27 +0100 | |
commit | 8350a0634d1caf34826fb3ac41d5a892cf3ff1c9 (patch) | |
tree | 6769f681ff339e0796e2ca43525df3e58b6fc6db /language | |
parent | Merge pull request #9 from kris7t/cm6-sonar-fixes (diff) | |
parent | chore(web): implicit completion info in grammar (diff) | |
download | refinery-8350a0634d1caf34826fb3ac41d5a892cf3ff1c9.tar.gz refinery-8350a0634d1caf34826fb3ac41d5a892cf3ff1c9.tar.zst refinery-8350a0634d1caf34826fb3ac41d5a892cf3ff1c9.zip |
Merge pull request #10 from kris7t/cm6-fixes
More CodeMirrror fixes
Diffstat (limited to 'language')
3 files changed, 103 insertions, 3 deletions
diff --git a/language/src/main/java/tools/refinery/language/Problem.xtext b/language/src/main/java/tools/refinery/language/Problem.xtext index 6f6a8588..0fa47d63 100644 --- a/language/src/main/java/tools/refinery/language/Problem.xtext +++ b/language/src/main/java/tools/refinery/language/Problem.xtext | |||
@@ -154,7 +154,7 @@ ScopeDeclaration: | |||
154 | "scope" typeScopes+=TypeScope ("," typeScopes+=TypeScope)* "."; | 154 | "scope" typeScopes+=TypeScope ("," typeScopes+=TypeScope)* "."; |
155 | 155 | ||
156 | TypeScope: | 156 | TypeScope: |
157 | targetType=[ClassDeclaration] | 157 | targetType=[ClassDeclaration|QualifiedName] |
158 | (increment?="+=" | "=") | 158 | (increment?="+=" | "=") |
159 | multiplicity=DefiniteMultiplicity; | 159 | multiplicity=DefiniteMultiplicity; |
160 | 160 | ||
@@ -183,8 +183,8 @@ QualifiedName hidden(): | |||
183 | Identifier ("::" Identifier)*; | 183 | Identifier ("::" Identifier)*; |
184 | 184 | ||
185 | Identifier: | 185 | Identifier: |
186 | ID | "true" | "false" | "unknown" | "error" | "class" | "abstract" | "extends" | "enum" | "pred" | "scope" | | 186 | ID | "true" | "false" | "unknown" | "error" | "class" | "abstract" | "extends" | "enum" | "pred" | |
187 | "unique" | "default" | "problem" | "new" | "delete"; | 187 | "unique" | "problem" | "new" | "delete"; |
188 | 188 | ||
189 | Integer returns ecore::EInt hidden(): | 189 | Integer returns ecore::EInt hidden(): |
190 | "-"? INT; | 190 | "-"? INT; |
diff --git a/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java b/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java new file mode 100644 index 00000000..7525dfc6 --- /dev/null +++ b/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java | |||
@@ -0,0 +1,51 @@ | |||
1 | package tools.refinery.language.resource; | ||
2 | |||
3 | import java.util.HashMap; | ||
4 | import java.util.Map; | ||
5 | |||
6 | import org.eclipse.emf.ecore.EObject; | ||
7 | import org.eclipse.xtext.util.IResourceScopeCache; | ||
8 | |||
9 | import com.google.inject.Inject; | ||
10 | import com.google.inject.Singleton; | ||
11 | |||
12 | import tools.refinery.language.model.problem.Problem; | ||
13 | |||
14 | @Singleton | ||
15 | public class ReferenceCounter { | ||
16 | @Inject | ||
17 | private IResourceScopeCache cache; | ||
18 | |||
19 | public int countReferences(Problem problem, EObject eObject) { | ||
20 | var count = getReferenceCounts(problem).get(eObject); | ||
21 | if (count == null) { | ||
22 | return 0; | ||
23 | } | ||
24 | return count; | ||
25 | } | ||
26 | |||
27 | protected Map<EObject, Integer> getReferenceCounts(Problem problem) { | ||
28 | var resource = problem.eResource(); | ||
29 | if (resource == null) { | ||
30 | return doGetReferenceCounts(problem); | ||
31 | } | ||
32 | return cache.get(problem, resource, () -> doGetReferenceCounts(problem)); | ||
33 | } | ||
34 | |||
35 | protected Map<EObject, Integer> doGetReferenceCounts(Problem problem) { | ||
36 | var map = new HashMap<EObject, Integer>(); | ||
37 | countCrossReferences(problem, map); | ||
38 | var iterator = problem.eAllContents(); | ||
39 | while (iterator.hasNext()) { | ||
40 | var eObject = iterator.next(); | ||
41 | countCrossReferences(eObject, map); | ||
42 | } | ||
43 | return map; | ||
44 | } | ||
45 | |||
46 | protected void countCrossReferences(EObject eObject, Map<EObject, Integer> map) { | ||
47 | for (var referencedObject : eObject.eCrossReferences()) { | ||
48 | map.compute(referencedObject, (key, currentValue) -> currentValue == null ? 1 : currentValue + 1); | ||
49 | } | ||
50 | } | ||
51 | } | ||
diff --git a/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java b/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java index f2378df6..dfd386f5 100644 --- a/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java +++ b/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java | |||
@@ -3,6 +3,19 @@ | |||
3 | */ | 3 | */ |
4 | package tools.refinery.language.validation; | 4 | package tools.refinery.language.validation; |
5 | 5 | ||
6 | import org.eclipse.xtext.EcoreUtil2; | ||
7 | import org.eclipse.xtext.validation.Check; | ||
8 | |||
9 | import com.google.inject.Inject; | ||
10 | |||
11 | import tools.refinery.language.model.ProblemUtil; | ||
12 | import tools.refinery.language.model.problem.Node; | ||
13 | import tools.refinery.language.model.problem.Problem; | ||
14 | import tools.refinery.language.model.problem.ProblemPackage; | ||
15 | import tools.refinery.language.model.problem.Variable; | ||
16 | import tools.refinery.language.model.problem.VariableOrNodeArgument; | ||
17 | import tools.refinery.language.resource.ReferenceCounter; | ||
18 | |||
6 | /** | 19 | /** |
7 | * This class contains custom validation rules. | 20 | * This class contains custom validation rules. |
8 | * | 21 | * |
@@ -10,4 +23,40 @@ package tools.refinery.language.validation; | |||
10 | * https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation | 23 | * https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation |
11 | */ | 24 | */ |
12 | public class ProblemValidator extends AbstractProblemValidator { | 25 | public class ProblemValidator extends AbstractProblemValidator { |
26 | private static final String ISSUE_PREFIX = "tools.refinery.language.validation.ProblemValidator."; | ||
27 | |||
28 | public static final String SINGLETON_VARIABLE_ISSUE = ISSUE_PREFIX + "SINGLETON_VARIABLE"; | ||
29 | |||
30 | public static final String NON_UNIQUE_NODE_ISSUE = ISSUE_PREFIX + "NON_UNIQUE_NODE"; | ||
31 | |||
32 | @Inject | ||
33 | private ReferenceCounter referenceCounter; | ||
34 | |||
35 | @Check | ||
36 | public void checkUniqueVariable(VariableOrNodeArgument argument) { | ||
37 | var variableOrNode = argument.getVariableOrNode(); | ||
38 | if (variableOrNode instanceof Variable variable && ProblemUtil.isImplicitVariable(variable) | ||
39 | && !ProblemUtil.isSingletonVariable(variable)) { | ||
40 | var problem = EcoreUtil2.getContainerOfType(variable, Problem.class); | ||
41 | if (problem != null && referenceCounter.countReferences(problem, variable) <= 1) { | ||
42 | var name = variable.getName(); | ||
43 | var message = "Variable '%s' has only a single reference. Add another reference or mark is as a singleton variable: '_%s'" | ||
44 | .formatted(name, name); | ||
45 | warning(message, argument, ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE, | ||
46 | INSIGNIFICANT_INDEX, SINGLETON_VARIABLE_ISSUE); | ||
47 | } | ||
48 | } | ||
49 | } | ||
50 | |||
51 | @Check | ||
52 | public void checkNonUniqueNode(VariableOrNodeArgument argument) { | ||
53 | var variableOrNode = argument.getVariableOrNode(); | ||
54 | if (variableOrNode instanceof Node node && !ProblemUtil.isUniqueNode(node)) { | ||
55 | var name = node.getName(); | ||
56 | var message = "Only unique nodes can be referenced in predicates. Mark '%s' as unique with the declaration 'unique %s.'" | ||
57 | .formatted(name, name); | ||
58 | error(message, argument, ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE, | ||
59 | INSIGNIFICANT_INDEX, NON_UNIQUE_NODE_ISSUE); | ||
60 | } | ||
61 | } | ||
13 | } | 62 | } |