aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/MetadataCreator.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/MetadataCreator.java')
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/MetadataCreator.java200
1 files changed, 200 insertions, 0 deletions
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/MetadataCreator.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/MetadataCreator.java
new file mode 100644
index 00000000..cc262129
--- /dev/null
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/metadata/MetadataCreator.java
@@ -0,0 +1,200 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.semantics.metadata;
7
8import com.google.inject.Inject;
9import org.eclipse.emf.ecore.EObject;
10import org.eclipse.xtext.naming.IQualifiedNameConverter;
11import org.eclipse.xtext.naming.IQualifiedNameProvider;
12import org.eclipse.xtext.naming.QualifiedName;
13import org.eclipse.xtext.scoping.IScope;
14import org.eclipse.xtext.scoping.IScopeProvider;
15import tools.refinery.language.model.problem.*;
16import tools.refinery.language.semantics.model.ModelInitializer;
17import tools.refinery.language.semantics.model.TracedException;
18import tools.refinery.language.utils.ProblemUtil;
19import tools.refinery.store.reasoning.representation.PartialRelation;
20
21import java.util.*;
22
23public class MetadataCreator {
24 @Inject
25 private IScopeProvider scopeProvider;
26
27 @Inject
28 private IQualifiedNameProvider qualifiedNameProvider;
29
30 @Inject
31 private IQualifiedNameConverter qualifiedNameConverter;
32
33 private ModelInitializer initializer;
34
35 private IScope nodeScope;
36
37 private IScope relationScope;
38
39 public void setInitializer(ModelInitializer initializer) {
40 if (initializer == null) {
41 throw new IllegalArgumentException("Initializer was already set");
42 }
43 this.initializer = initializer;
44 var problem = initializer.getProblem();
45 nodeScope = scopeProvider.getScope(problem, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE);
46 relationScope = scopeProvider.getScope(problem, ProblemPackage.Literals.ASSERTION__RELATION);
47 }
48
49 public static String unnamedNode(int nodeId) {
50 return "::" + nodeId;
51 }
52
53 public List<NodeMetadata> getNodesMetadata() {
54 return getNodesMetadata(initializer.getNodeCount(), true);
55 }
56
57 public List<NodeMetadata> getNodesMetadata(int nodeCount, boolean preserveNewNodes) {
58 var nodes = new NodeMetadata[Math.max(initializer.getNodeCount(), nodeCount)];
59 for (var entry : initializer.getNodeTrace().keyValuesView()) {
60 var node = entry.getOne();
61 var id = entry.getTwo();
62 nodes[id] = getNodeMetadata(id, node, preserveNewNodes);
63 }
64 for (int i = 0; i < nodes.length; i++) {
65 if (nodes[i] == null) {
66 var nodeName = unnamedNode(i);
67 nodes[i] = new NodeMetadata(nodeName, nodeName, NodeKind.IMPLICIT);
68 }
69 }
70 return List.of(nodes);
71 }
72
73 private NodeMetadata getNodeMetadata(int nodeId, Node node, boolean preserveNewNodes) {
74 var kind = getNodeKind(node);
75 if (!preserveNewNodes && kind == NodeKind.NEW) {
76 var nodeName = unnamedNode(nodeId);
77 return new NodeMetadata(nodeName, nodeName, NodeKind.IMPLICIT);
78 }
79 var qualifiedName = getQualifiedName(node);
80 var simpleName = getSimpleName(node, qualifiedName, nodeScope);
81 return new NodeMetadata(qualifiedNameConverter.toString(qualifiedName),
82 qualifiedNameConverter.toString(simpleName), getNodeKind(node));
83 }
84
85 private NodeKind getNodeKind(Node node) {
86 if (ProblemUtil.isImplicitNode(node)) {
87 return NodeKind.IMPLICIT;
88 } else if (ProblemUtil.isIndividualNode(node)) {
89 return NodeKind.INDIVIDUAL;
90 } else if (ProblemUtil.isNewNode(node)) {
91 return NodeKind.NEW;
92 } else {
93 throw new TracedException(node, "Unknown node type");
94 }
95 }
96
97 public List<RelationMetadata> getRelationsMetadata() {
98 var relationTrace = initializer.getRelationTrace();
99 var relations = new ArrayList<RelationMetadata>(relationTrace.size());
100 for (var entry : relationTrace.entrySet()) {
101 var relation = entry.getKey();
102 var partialRelation = entry.getValue();
103 var metadata = getRelationMetadata(relation, partialRelation);
104 relations.add(metadata);
105 }
106 return Collections.unmodifiableList(relations);
107 }
108
109 private RelationMetadata getRelationMetadata(Relation relation, PartialRelation partialRelation) {
110 var qualifiedName = getQualifiedName(relation);
111 var qualifiedNameString = qualifiedNameConverter.toString(qualifiedName);
112 var simpleName = getSimpleName(relation, qualifiedName, relationScope);
113 var simpleNameString = qualifiedNameConverter.toString(simpleName);
114 var arity = partialRelation.arity();
115 var detail = getRelationDetail(relation, partialRelation);
116 return new RelationMetadata(qualifiedNameString, simpleNameString, arity, detail);
117 }
118
119 private RelationDetail getRelationDetail(Relation relation, PartialRelation partialRelation) {
120 if (ProblemUtil.isBuiltIn(relation) && !ProblemUtil.isError(relation)) {
121 return getBuiltInDetail();
122 }
123 if (relation instanceof ClassDeclaration classDeclaration) {
124 return getClassDetail(classDeclaration);
125 } else if (relation instanceof ReferenceDeclaration) {
126 return getReferenceDetail(partialRelation);
127 } else if (relation instanceof EnumDeclaration) {
128 return getEnumDetail();
129 } else if (relation instanceof PredicateDefinition predicateDefinition) {
130 return getPredicateDetail(predicateDefinition);
131 } else {
132 throw new TracedException(relation, "Unknown relation");
133 }
134 }
135
136 private RelationDetail getBuiltInDetail() {
137 return BuiltInDetail.INSTANCE;
138 }
139
140 private RelationDetail getClassDetail(ClassDeclaration classDeclaration) {
141 return ClassDetail.ofAbstractClass(classDeclaration.isAbstract());
142 }
143
144 private RelationDetail getReferenceDetail(PartialRelation partialRelation) {
145 var metamodel = initializer.getMetamodel();
146 var opposite = metamodel.oppositeReferences().get(partialRelation);
147 if (opposite == null) {
148 boolean isContainment = metamodel.containmentHierarchy().containsKey(partialRelation);
149 return ReferenceDetail.ofContainment(isContainment);
150 } else {
151 boolean isContainer = metamodel.containmentHierarchy().containsKey(opposite);
152 return new OppositeReferenceDetail(isContainer, opposite.name());
153 }
154 }
155
156 private RelationDetail getEnumDetail() {
157 return ClassDetail.CONCRETE_CLASS;
158 }
159
160 private RelationDetail getPredicateDetail(PredicateDefinition predicate) {
161 return PredicateDetail.ofError(predicate.isError());
162 }
163
164 private QualifiedName getQualifiedName(EObject eObject) {
165 var qualifiedName = qualifiedNameProvider.getFullyQualifiedName(eObject);
166 if (qualifiedName == null) {
167 throw new TracedException(eObject, "Unknown qualified name");
168 }
169 return qualifiedName;
170 }
171
172 private QualifiedName getSimpleName(EObject eObject, QualifiedName qualifiedName, IScope scope) {
173 var descriptions = scope.getElements(eObject);
174 var names = new HashSet<QualifiedName>();
175 for (var description : descriptions) {
176 // {@code getQualifiedName()} will refer to the full name for objects that are loaded from the global
177 // scope, but {@code getName()} returns the qualified name that we set in
178 // {@code ProblemResourceDescriptionStrategy}.
179 names.add(description.getName());
180 }
181 var iterator = names.stream().sorted(Comparator.comparingInt(QualifiedName::getSegmentCount)).iterator();
182 while (iterator.hasNext()) {
183 var simpleName = iterator.next();
184 if (names.contains(simpleName) && isUnique(scope, simpleName)) {
185 return simpleName;
186 }
187 }
188 throw new TracedException(eObject, "Ambiguous qualified name: " +
189 qualifiedNameConverter.toString(qualifiedName));
190 }
191
192 private boolean isUnique(IScope scope, QualifiedName name) {
193 var iterator = scope.getElements(name).iterator();
194 if (!iterator.hasNext()) {
195 return false;
196 }
197 iterator.next();
198 return !iterator.hasNext();
199 }
200}