blob: f75ecdb2291bb252e46a7028bd897c9feafdfa02 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
/*
* SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
*
* SPDX-License-Identifier: EPL-2.0
*/
package tools.refinery.language.ide.syntaxcoloring;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
import org.eclipse.xtext.scoping.IScopeProvider;
import org.eclipse.xtext.util.IResourceScopeCache;
import tools.refinery.language.model.problem.*;
import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
import tools.refinery.language.utils.ProblemUtil;
import java.util.*;
@Singleton
public class TypeHashProvider {
private static final String CACHE_KEY = "tools.refinery.language.ide.syntaxcoloring.TypeHashProvider";
private static final int COLOR_COUNT = 10;
@Inject
private IResourceScopeCache resourceScopeCache;
@Inject
private IScopeProvider scopeProvider;
@Inject
private IQualifiedNameProvider qualifiedNameProvider;
@Inject
private IQualifiedNameConverter qualifiedNameConverter;
public String getTypeHash(Relation relation) {
if (!(relation instanceof ClassDeclaration || relation instanceof EnumDeclaration) ||
ProblemUtil.isBuiltIn(relation)) {
return null;
}
var qualifiedName = qualifiedNameProvider.getFullyQualifiedName(relation);
if (qualifiedName == null) {
return null;
}
var qualifiedNameString = qualifiedNameConverter.toString(qualifiedName);
var problem = EcoreUtil2.getContainerOfType(relation, Problem.class);
if (problem == null) {
return null;
}
var cache = resourceScopeCache.get(CACHE_KEY, problem.eResource(), () -> computeHashes(problem));
return cache.get(qualifiedNameString);
}
private Map<String, String> computeHashes(Problem problem) {
var qualifiedNameStrings = new TreeSet<String>();
var scope = scopeProvider.getScope(problem, ProblemPackage.Literals.ASSERTION__RELATION);
for (var description : scope.getAllElements()) {
if (ProblemResourceDescriptionStrategy.COLOR_RELATION_TRUE.equals(
description.getUserData(ProblemResourceDescriptionStrategy.COLOR_RELATION))) {
var qualifiedNameString = qualifiedNameConverter.toString(description.getQualifiedName());
qualifiedNameStrings.add(qualifiedNameString);
}
}
var stringList = new ArrayList<>(qualifiedNameStrings);
int size = stringList.size();
if (size == 0) {
return Map.of();
}
// The use of a non-cryptographic random generator is safe here, because we only use it to shuffle the color
// IDs in a pseudo-random way. The shuffle depends on the size of the list of identifiers before padding to
// make sure that adding a new class randomizes all color IDs.
@SuppressWarnings("squid:S2245")
var random = new Random(size);
int padding = COLOR_COUNT - (size % COLOR_COUNT);
for (int i = 0; i < padding; i++) {
stringList.add(null);
}
size += padding;
Collections.shuffle(stringList, random);
var mapBuilder = ImmutableMap.<String, String>builder();
for (int i = 0; i < size; i++) {
var key = stringList.get(i);
if (key != null) {
int colorId = i % COLOR_COUNT;
mapBuilder.put(key, Integer.toString(colorId));
}
}
return mapBuilder.build();
}
}
|