diff options
Diffstat (limited to 'subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel')
5 files changed, 328 insertions, 0 deletions
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/ContainedTypeHierarchyBuilder.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/ContainedTypeHierarchyBuilder.java new file mode 100644 index 00000000..cc43bce6 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/ContainedTypeHierarchyBuilder.java | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.reasoning.translator.metamodel; | ||
7 | |||
8 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
9 | import tools.refinery.store.reasoning.translator.containment.ContainmentHierarchyTranslator; | ||
10 | import tools.refinery.store.reasoning.translator.typehierarchy.TypeHierarchyBuilder; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | |||
14 | public class ContainedTypeHierarchyBuilder extends TypeHierarchyBuilder { | ||
15 | ContainedTypeHierarchyBuilder() { | ||
16 | } | ||
17 | |||
18 | boolean isInvalidType(PartialRelation type) { | ||
19 | return !typeInfoMap.containsKey(type); | ||
20 | } | ||
21 | |||
22 | void setContainedTypes(Collection<PartialRelation> containedTypes) { | ||
23 | for (var containedType : containedTypes) { | ||
24 | var currentInfo = typeInfoMap.get(containedType); | ||
25 | if (currentInfo == null) { | ||
26 | throw new IllegalArgumentException("Invalid contained type: " + containedType); | ||
27 | } | ||
28 | var newInfo = currentInfo.addSupertype(ContainmentHierarchyTranslator.CONTAINED_SYMBOL); | ||
29 | typeInfoMap.put(containedType, newInfo); | ||
30 | } | ||
31 | } | ||
32 | } | ||
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/Metamodel.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/Metamodel.java new file mode 100644 index 00000000..72b836ff --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/Metamodel.java | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.reasoning.translator.metamodel; | ||
7 | |||
8 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
9 | import tools.refinery.store.reasoning.translator.containment.ContainmentInfo; | ||
10 | import tools.refinery.store.reasoning.translator.crossreference.DirectedCrossReferenceInfo; | ||
11 | import tools.refinery.store.reasoning.translator.crossreference.UndirectedCrossReferenceInfo; | ||
12 | import tools.refinery.store.reasoning.translator.typehierarchy.TypeHierarchy; | ||
13 | |||
14 | import java.util.Map; | ||
15 | |||
16 | public record Metamodel(TypeHierarchy typeHierarchy, Map<PartialRelation, ContainmentInfo> containmentHierarchy, | ||
17 | Map<PartialRelation, DirectedCrossReferenceInfo> directedCrossReferences, | ||
18 | Map<PartialRelation, UndirectedCrossReferenceInfo> undirectedCrossReferences, | ||
19 | Map<PartialRelation, PartialRelation> oppositeReferences) { | ||
20 | public static MetamodelBuilder builder() { | ||
21 | return new MetamodelBuilder(); | ||
22 | } | ||
23 | } | ||
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..92370e25 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/MetamodelBuilder.java | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.reasoning.translator.metamodel; | ||
7 | |||
8 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
9 | import tools.refinery.store.reasoning.translator.containment.ContainmentHierarchyTranslator; | ||
10 | import tools.refinery.store.reasoning.translator.containment.ContainmentInfo; | ||
11 | import tools.refinery.store.reasoning.translator.crossreference.DirectedCrossReferenceInfo; | ||
12 | import tools.refinery.store.reasoning.translator.crossreference.UndirectedCrossReferenceInfo; | ||
13 | import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity; | ||
14 | import tools.refinery.store.reasoning.translator.multiplicity.UnconstrainedMultiplicity; | ||
15 | import tools.refinery.store.reasoning.translator.typehierarchy.TypeInfo; | ||
16 | import tools.refinery.store.representation.cardinality.CardinalityIntervals; | ||
17 | |||
18 | import java.util.*; | ||
19 | |||
20 | public 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 IllegalArgumentException("Only references of arity 2 are supported, got %s with %d instead" | ||
74 | .formatted(linkType, linkType.arity())); | ||
75 | } | ||
76 | var putResult = referenceInfoMap.put(linkType, info); | ||
77 | if (putResult != null && !putResult.equals(info)) { | ||
78 | throw new IllegalArgumentException("Duplicate reference info for partial relation: " + linkType); | ||
79 | } | ||
80 | return this; | ||
81 | } | ||
82 | |||
83 | public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType, boolean containment, | ||
84 | Multiplicity multiplicity, PartialRelation targetType, | ||
85 | PartialRelation opposite) { | ||
86 | return reference(linkType, new ReferenceInfo(containment, sourceType, multiplicity, targetType, opposite)); | ||
87 | } | ||
88 | |||
89 | public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType, Multiplicity multiplicity, | ||
90 | PartialRelation targetType, PartialRelation opposite) { | ||
91 | return reference(linkType, sourceType, false, multiplicity, targetType, opposite); | ||
92 | } | ||
93 | |||
94 | public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType, | ||
95 | boolean containment, PartialRelation targetType, PartialRelation opposite) { | ||
96 | return reference(linkType, sourceType, containment, UnconstrainedMultiplicity.INSTANCE, targetType, opposite); | ||
97 | } | ||
98 | |||
99 | public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType, PartialRelation targetType, | ||
100 | PartialRelation opposite) { | ||
101 | return reference(linkType, sourceType, UnconstrainedMultiplicity.INSTANCE, targetType, opposite); | ||
102 | } | ||
103 | |||
104 | public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType, boolean containment, | ||
105 | Multiplicity multiplicity, PartialRelation targetType) { | ||
106 | return reference(linkType, sourceType, containment, multiplicity, targetType, null); | ||
107 | } | ||
108 | |||
109 | public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType, Multiplicity multiplicity, | ||
110 | PartialRelation targetType) { | ||
111 | return reference(linkType, sourceType, multiplicity, targetType, null); | ||
112 | } | ||
113 | |||
114 | public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType, boolean containment, | ||
115 | PartialRelation targetType) { | ||
116 | return reference(linkType, sourceType, containment, targetType, null); | ||
117 | } | ||
118 | |||
119 | public MetamodelBuilder reference(PartialRelation linkType, PartialRelation sourceType, | ||
120 | PartialRelation targetType) { | ||
121 | return reference(linkType, sourceType, targetType, null); | ||
122 | } | ||
123 | |||
124 | public MetamodelBuilder references(Collection<Map.Entry<PartialRelation, ReferenceInfo>> entries) { | ||
125 | for (var entry : entries) { | ||
126 | reference(entry.getKey(), entry.getValue()); | ||
127 | } | ||
128 | return this; | ||
129 | } | ||
130 | |||
131 | public MetamodelBuilder references(Map<PartialRelation, ReferenceInfo> map) { | ||
132 | return references(map.entrySet()); | ||
133 | } | ||
134 | |||
135 | public Metamodel build() { | ||
136 | for (var entry : referenceInfoMap.entrySet()) { | ||
137 | var linkType = entry.getKey(); | ||
138 | var info = entry.getValue(); | ||
139 | processReferenceInfo(linkType, info); | ||
140 | } | ||
141 | typeHierarchyBuilder.setContainedTypes(containedTypes); | ||
142 | var typeHierarchy = typeHierarchyBuilder.build(); | ||
143 | return new Metamodel(typeHierarchy, Collections.unmodifiableMap(containmentHierarchy), | ||
144 | Collections.unmodifiableMap(directedCrossReferences), | ||
145 | Collections.unmodifiableMap(undirectedCrossReferences), | ||
146 | Collections.unmodifiableMap(oppositeReferences)); | ||
147 | } | ||
148 | |||
149 | private void processReferenceInfo(PartialRelation linkType, ReferenceInfo info) { | ||
150 | if (oppositeReferences.containsKey(linkType) || containmentHierarchy.containsKey(linkType)) { | ||
151 | // We already processed this reference while processing its opposite. | ||
152 | return; | ||
153 | } | ||
154 | var sourceType = info.sourceType(); | ||
155 | var targetType = info.targetType(); | ||
156 | if (typeHierarchyBuilder.isInvalidType(sourceType)) { | ||
157 | throw new IllegalArgumentException("Source type %s of %s is not in type hierarchy" | ||
158 | .formatted(sourceType, linkType)); | ||
159 | } | ||
160 | if (typeHierarchyBuilder.isInvalidType(targetType)) { | ||
161 | throw new IllegalArgumentException("Target type %s of %s is not in type hierarchy" | ||
162 | .formatted(targetType, linkType)); | ||
163 | } | ||
164 | var opposite = info.opposite(); | ||
165 | Multiplicity targetMultiplicity = UnconstrainedMultiplicity.INSTANCE; | ||
166 | if (opposite != null) { | ||
167 | var oppositeInfo = referenceInfoMap.get(opposite); | ||
168 | validateOpposite(linkType, info, opposite, oppositeInfo); | ||
169 | targetMultiplicity = oppositeInfo.multiplicity(); | ||
170 | if (oppositeInfo.containment()) { | ||
171 | // Skip processing this reference and process it once we encounter its containment opposite. | ||
172 | return; | ||
173 | } | ||
174 | if (opposite.equals(linkType)) { | ||
175 | if (!sourceType.equals(targetType)) { | ||
176 | throw new IllegalArgumentException("Target %s of undirected reference %s differs from source %s" | ||
177 | .formatted(targetType, linkType, sourceType)); | ||
178 | } | ||
179 | undirectedCrossReferences.put(linkType, new UndirectedCrossReferenceInfo(sourceType, | ||
180 | info.multiplicity())); | ||
181 | return; | ||
182 | } | ||
183 | oppositeReferences.put(opposite, linkType); | ||
184 | } | ||
185 | if (info.containment()) { | ||
186 | if (targetMultiplicity.multiplicity().meet(CardinalityIntervals.ONE).isEmpty()) { | ||
187 | throw new IllegalArgumentException("Invalid opposite %s with multiplicity %s of containment %s" | ||
188 | .formatted(opposite, targetMultiplicity, linkType)); | ||
189 | } | ||
190 | containedTypes.add(targetType); | ||
191 | containmentHierarchy.put(linkType, new ContainmentInfo(sourceType, info.multiplicity(), targetType)); | ||
192 | return; | ||
193 | } | ||
194 | directedCrossReferences.put(linkType, new DirectedCrossReferenceInfo(sourceType, info.multiplicity(), | ||
195 | targetType, targetMultiplicity)); | ||
196 | } | ||
197 | |||
198 | private static void validateOpposite(PartialRelation linkType, ReferenceInfo info, PartialRelation opposite, | ||
199 | ReferenceInfo oppositeInfo) { | ||
200 | var sourceType = info.sourceType(); | ||
201 | var targetType = info.targetType(); | ||
202 | if (oppositeInfo == null) { | ||
203 | throw new IllegalArgumentException("Opposite %s of %s is not defined" | ||
204 | .formatted(opposite, linkType)); | ||
205 | } | ||
206 | if (!oppositeInfo.opposite().equals(linkType)) { | ||
207 | throw new IllegalArgumentException("Expected %s to have opposite %s, got %s instead" | ||
208 | .formatted(opposite, linkType, oppositeInfo.opposite())); | ||
209 | } | ||
210 | if (!oppositeInfo.sourceType().equals(targetType)) { | ||
211 | throw new IllegalArgumentException("Expected %s to have source type %s, got %s instead" | ||
212 | .formatted(opposite, targetType, oppositeInfo.sourceType())); | ||
213 | } | ||
214 | if (!oppositeInfo.targetType().equals(sourceType)) { | ||
215 | throw new IllegalArgumentException("Expected %s to have target type %s, got %s instead" | ||
216 | .formatted(opposite, sourceType, oppositeInfo.targetType())); | ||
217 | } | ||
218 | if (oppositeInfo.containment() && info.containment()) { | ||
219 | throw new IllegalArgumentException("Opposite %s of containment %s cannot be containment" | ||
220 | .formatted(opposite, linkType)); | ||
221 | } | ||
222 | } | ||
223 | } | ||
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/MetamodelTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/MetamodelTranslator.java new file mode 100644 index 00000000..5afa58f2 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/MetamodelTranslator.java | |||
@@ -0,0 +1,37 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.reasoning.translator.metamodel; | ||
7 | |||
8 | import tools.refinery.store.model.ModelStoreBuilder; | ||
9 | import tools.refinery.store.model.ModelStoreConfiguration; | ||
10 | import tools.refinery.store.reasoning.translator.containment.ContainmentHierarchyTranslator; | ||
11 | import tools.refinery.store.reasoning.translator.crossreference.DirectedCrossReferenceTranslator; | ||
12 | import tools.refinery.store.reasoning.translator.crossreference.UndirectedCrossReferenceTranslator; | ||
13 | import tools.refinery.store.reasoning.translator.opposite.OppositeRelationTranslator; | ||
14 | import tools.refinery.store.reasoning.translator.typehierarchy.TypeHierarchyTranslator; | ||
15 | |||
16 | public class MetamodelTranslator implements ModelStoreConfiguration { | ||
17 | private final Metamodel metamodel; | ||
18 | |||
19 | public MetamodelTranslator(Metamodel metamodel) { | ||
20 | this.metamodel = metamodel; | ||
21 | } | ||
22 | |||
23 | @Override | ||
24 | public void apply(ModelStoreBuilder storeBuilder) { | ||
25 | storeBuilder.with(new TypeHierarchyTranslator(metamodel.typeHierarchy())); | ||
26 | storeBuilder.with(new ContainmentHierarchyTranslator(metamodel.containmentHierarchy())); | ||
27 | for (var entry : metamodel.directedCrossReferences().entrySet()) { | ||
28 | storeBuilder.with(new DirectedCrossReferenceTranslator(entry.getKey(), entry.getValue())); | ||
29 | } | ||
30 | for (var entry : metamodel.undirectedCrossReferences().entrySet()) { | ||
31 | storeBuilder.with(new UndirectedCrossReferenceTranslator(entry.getKey(), entry.getValue())); | ||
32 | } | ||
33 | for (var entry : metamodel.oppositeReferences().entrySet()) { | ||
34 | storeBuilder.with(new OppositeRelationTranslator(entry.getKey(), entry.getValue())); | ||
35 | } | ||
36 | } | ||
37 | } | ||
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/ReferenceInfo.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/ReferenceInfo.java new file mode 100644 index 00000000..9a6b4012 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/metamodel/ReferenceInfo.java | |||
@@ -0,0 +1,13 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.reasoning.translator.metamodel; | ||
7 | |||
8 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
9 | import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity; | ||
10 | |||
11 | public record ReferenceInfo(boolean containment, PartialRelation sourceType, Multiplicity multiplicity, | ||
12 | PartialRelation targetType, PartialRelation opposite) { | ||
13 | } | ||