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