aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/MetamodelBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/MetamodelBuilder.java')
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/MetamodelBuilder.java225
1 files changed, 225 insertions, 0 deletions
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/MetamodelBuilder.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/MetamodelBuilder.java
new file mode 100644
index 00000000..ad0288ed
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/MetamodelBuilder.java
@@ -0,0 +1,225 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.reasoning.translator.metamodel;
7
8import tools.refinery.store.reasoning.representation.PartialRelation;
9import tools.refinery.store.reasoning.translator.TranslationException;
10import tools.refinery.store.reasoning.translator.containment.ContainmentHierarchyTranslator;
11import tools.refinery.store.reasoning.translator.containment.ContainmentInfo;
12import tools.refinery.store.reasoning.translator.crossreference.DirectedCrossReferenceInfo;
13import tools.refinery.store.reasoning.translator.crossreference.UndirectedCrossReferenceInfo;
14import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity;
15import tools.refinery.store.reasoning.translator.multiplicity.UnconstrainedMultiplicity;
16import tools.refinery.store.reasoning.translator.typehierarchy.TypeInfo;
17
18import java.util.*;
19
20public class MetamodelBuilder {
21 private final ContainedTypeHierarchyBuilder typeHierarchyBuilder = new ContainedTypeHierarchyBuilder();
22 private final Map<PartialRelation, ReferenceInfo> referenceInfoMap = new LinkedHashMap<>();
23 private final Set<PartialRelation> containedTypes = new HashSet<>();
24 private final Map<PartialRelation, ContainmentInfo> containmentHierarchy = new LinkedHashMap<>();
25 private final Map<PartialRelation, DirectedCrossReferenceInfo> directedCrossReferences = new LinkedHashMap<>();
26 private final Map<PartialRelation, UndirectedCrossReferenceInfo> undirectedCrossReferences = new LinkedHashMap<>();
27 private final Map<PartialRelation, PartialRelation> oppositeReferences = new LinkedHashMap<>();
28
29 MetamodelBuilder() {
30 typeHierarchyBuilder.type(ContainmentHierarchyTranslator.CONTAINED_SYMBOL, true);
31 }
32
33 public MetamodelBuilder type(PartialRelation partialRelation, TypeInfo typeInfo) {
34 typeHierarchyBuilder.type(partialRelation, typeInfo);
35 return this;
36
37 }
38
39 public MetamodelBuilder type(PartialRelation partialRelation, boolean abstractType,
40 PartialRelation... supertypes) {
41 typeHierarchyBuilder.type(partialRelation, abstractType, supertypes);
42 return this;
43 }
44
45 public MetamodelBuilder type(PartialRelation partialRelation, boolean abstractType,
46 Collection<PartialRelation> supertypes) {
47 typeHierarchyBuilder.type(partialRelation, abstractType, supertypes);
48 return this;
49 }
50
51 public MetamodelBuilder type(PartialRelation partialRelation, PartialRelation... supertypes) {
52 typeHierarchyBuilder.type(partialRelation, supertypes);
53 return this;
54 }
55
56 public MetamodelBuilder type(PartialRelation partialRelation, Collection<PartialRelation> supertypes) {
57 typeHierarchyBuilder.type(partialRelation, supertypes);
58 return this;
59 }
60
61 public MetamodelBuilder types(Collection<Map.Entry<PartialRelation, TypeInfo>> entries) {
62 typeHierarchyBuilder.types(entries);
63 return this;
64 }
65
66 public MetamodelBuilder types(Map<PartialRelation, TypeInfo> map) {
67 typeHierarchyBuilder.types(map);
68 return this;
69 }
70
71 public MetamodelBuilder reference(PartialRelation linkType, ReferenceInfo info) {
72 if (linkType.arity() != 2) {
73 throw new TranslationException(linkType,
74 "Only references of arity 2 are supported, got %s with %d instead".formatted(
75 linkType, linkType.arity()));
76 }
77 var putResult = referenceInfoMap.put(linkType, info);
78 if (putResult != null && !putResult.equals(info)) {
79 throw new TranslationException(linkType, "Duplicate reference info for partial relation: " + linkType);
80 }
81 return this;
82 }
83
84 public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType, boolean containment,
85 Multiplicity multiplicity, PartialRelation targetType,
86 PartialRelation opposite) {
87 return reference(linkType, new ReferenceInfo(containment, sourceType, multiplicity, targetType, opposite));
88 }
89
90 public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType, Multiplicity multiplicity,
91 PartialRelation targetType, PartialRelation opposite) {
92 return reference(linkType, sourceType, false, multiplicity, targetType, opposite);
93 }
94
95 public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType,
96 boolean containment, PartialRelation targetType, PartialRelation opposite) {
97 return reference(linkType, sourceType, containment, UnconstrainedMultiplicity.INSTANCE, targetType, opposite);
98 }
99
100 public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType, PartialRelation targetType,
101 PartialRelation opposite) {
102 return reference(linkType, sourceType, UnconstrainedMultiplicity.INSTANCE, targetType, opposite);
103 }
104
105 public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType, boolean containment,
106 Multiplicity multiplicity, PartialRelation targetType) {
107 return reference(linkType, sourceType, containment, multiplicity, targetType, null);
108 }
109
110 public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType, Multiplicity multiplicity,
111 PartialRelation targetType) {
112 return reference(linkType, sourceType, multiplicity, targetType, null);
113 }
114
115 public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType, boolean containment,
116 PartialRelation targetType) {
117 return reference(linkType, sourceType, containment, targetType, null);
118 }
119
120 public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType,
121 PartialRelation targetType) {
122 return reference(linkType, sourceType, targetType, null);
123 }
124
125 public MetamodelBuilder references(Collection<Map.Entry<PartialRelation, ReferenceInfo>> entries) {
126 for (var entry : entries) {
127 reference(entry.getKey(), entry.getValue());
128 }
129 return this;
130 }
131
132 public MetamodelBuilder references(Map<PartialRelation, ReferenceInfo> map) {
133 return references(map.entrySet());
134 }
135
136 public Metamodel build() {
137 for (var entry : referenceInfoMap.entrySet()) {
138 var linkType = entry.getKey();
139 var info = entry.getValue();
140 processReferenceInfo(linkType, info);
141 }
142 typeHierarchyBuilder.setContainedTypes(containedTypes);
143 var typeHierarchy = typeHierarchyBuilder.build();
144 return new Metamodel(typeHierarchy, Collections.unmodifiableMap(containmentHierarchy),
145 Collections.unmodifiableMap(directedCrossReferences),
146 Collections.unmodifiableMap(undirectedCrossReferences),
147 Collections.unmodifiableMap(oppositeReferences));
148 }
149
150 private void processReferenceInfo(PartialRelation linkType, ReferenceInfo info) {
151 if (oppositeReferences.containsKey(linkType) || containmentHierarchy.containsKey(linkType)) {
152 // We already processed this reference while processing its opposite.
153 return;
154 }
155 var sourceType = info.sourceType();
156 var targetType = info.targetType();
157 if (typeHierarchyBuilder.isInvalidType(sourceType)) {
158 throw new TranslationException(linkType, "Source type %s of %s is not in type hierarchy"
159 .formatted(sourceType, linkType));
160 }
161 if (typeHierarchyBuilder.isInvalidType(targetType)) {
162 throw new TranslationException(linkType, "Target type %s of %s is not in type hierarchy"
163 .formatted(targetType, linkType));
164 }
165 var opposite = info.opposite();
166 Multiplicity targetMultiplicity = UnconstrainedMultiplicity.INSTANCE;
167 if (opposite != null) {
168 var oppositeInfo = referenceInfoMap.get(opposite);
169 validateOpposite(linkType, info, opposite, oppositeInfo);
170 targetMultiplicity = oppositeInfo.multiplicity();
171 if (oppositeInfo.containment()) {
172 // Skip processing this reference and process it once we encounter its containment opposite.
173 return;
174 }
175 if (opposite.equals(linkType)) {
176 if (!sourceType.equals(targetType)) {
177 throw new TranslationException(linkType,
178 "Target %s of undirected reference %s differs from source %s".formatted(
179 targetType, linkType, sourceType));
180 }
181 undirectedCrossReferences.put(linkType, new UndirectedCrossReferenceInfo(sourceType,
182 info.multiplicity()));
183 return;
184 }
185 oppositeReferences.put(opposite, linkType);
186 }
187 if (info.containment()) {
188 if (!UnconstrainedMultiplicity.INSTANCE.equals(targetMultiplicity)) {
189 throw new TranslationException(opposite, "Invalid opposite %s with multiplicity %s of containment %s"
190 .formatted(opposite, targetMultiplicity, linkType));
191 }
192 containedTypes.add(targetType);
193 containmentHierarchy.put(linkType, new ContainmentInfo(sourceType, info.multiplicity(), targetType));
194 return;
195 }
196 directedCrossReferences.put(linkType, new DirectedCrossReferenceInfo(sourceType, info.multiplicity(),
197 targetType, targetMultiplicity));
198 }
199
200 private static void validateOpposite(PartialRelation linkType, ReferenceInfo info, PartialRelation opposite,
201 ReferenceInfo oppositeInfo) {
202 var sourceType = info.sourceType();
203 var targetType = info.targetType();
204 if (oppositeInfo == null) {
205 throw new TranslationException(linkType, "Opposite %s of %s is not defined"
206 .formatted(opposite, linkType));
207 }
208 if (!linkType.equals(oppositeInfo.opposite())) {
209 throw new TranslationException(opposite, "Expected %s to have opposite %s, got %s instead"
210 .formatted(opposite, linkType, oppositeInfo.opposite()));
211 }
212 if (!targetType.equals(oppositeInfo.sourceType())) {
213 throw new TranslationException(linkType, "Expected %s to have source type %s, got %s instead"
214 .formatted(opposite, targetType, oppositeInfo.sourceType()));
215 }
216 if (!sourceType.equals(oppositeInfo.targetType())) {
217 throw new TranslationException(linkType, "Expected %s to have target type %s, got %s instead"
218 .formatted(opposite, sourceType, oppositeInfo.targetType()));
219 }
220 if (oppositeInfo.containment() && info.containment()) {
221 throw new TranslationException(opposite, "Opposite %s of containment %s cannot be containment"
222 .formatted(opposite, linkType));
223 }
224 }
225}