blob: dd9f10539b53ccd99d13064489a1675692c367c2 (
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
/*
* 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.resource.IResourceDescription;
import org.eclipse.xtext.scoping.IScopeProvider;
import org.eclipse.xtext.scoping.impl.GlobalResourceDescriptionProvider;
import org.eclipse.xtext.util.IResourceScopeCache;
import tools.refinery.language.model.problem.*;
import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
import tools.refinery.language.scoping.imports.ImportCollector;
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;
@Inject
private ImportCollector importCollector;
@Inject
private GlobalResourceDescriptionProvider globalResourceDescriptionProvider;
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 resourceDescriptions = getResourceDescriptions(problem);
var qualifiedNameStrings = new TreeSet<String>();
for (var resourceDescription : resourceDescriptions) {
for (var description : resourceDescription.getExportedObjectsByType(ProblemPackage.Literals.RELATION)) {
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();
}
private List<IResourceDescription> getResourceDescriptions(Problem problem) {
var resource = problem.eResource();
if (resource == null) {
return List.of();
}
var resourceDescriptions = new ArrayList<IResourceDescription>();
var resourceDescription = globalResourceDescriptionProvider.getResourceDescription(resource);
if (resourceDescription != null) {
resourceDescriptions.add(resourceDescription);
}
var resourceSet = resource.getResourceSet();
if (resourceSet != null) {
for (var importedUri : importCollector.getAllImports(resource).toUriSet()) {
var importedResource = resourceSet.getResource(importedUri, false);
if (importedResource != null) {
var importedResourceDescription = globalResourceDescriptionProvider.getResourceDescription(
importedResource);
if (importedResourceDescription != null) {
resourceDescriptions.add(importedResourceDescription);
}
}
}
}
return resourceDescriptions;
}
}
|