aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/EMFModelComprehension.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/EMFModelComprehension.java')
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/EMFModelComprehension.java356
1 files changed, 356 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/EMFModelComprehension.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/EMFModelComprehension.java
new file mode 100644
index 00000000..bde93367
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/EMFModelComprehension.java
@@ -0,0 +1,356 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.base.comprehension;
11
12import java.util.ArrayList;
13import java.util.Iterator;
14import java.util.List;
15
16import org.eclipse.emf.common.notify.Notifier;
17import org.eclipse.emf.common.util.EList;
18import org.eclipse.emf.ecore.EAttribute;
19import org.eclipse.emf.ecore.EObject;
20import org.eclipse.emf.ecore.EReference;
21import org.eclipse.emf.ecore.EStructuralFeature;
22import org.eclipse.emf.ecore.EcorePackage;
23import org.eclipse.emf.ecore.InternalEObject;
24import org.eclipse.emf.ecore.resource.Resource;
25import org.eclipse.emf.ecore.resource.ResourceSet;
26import org.eclipse.emf.ecore.util.EcoreUtil;
27import org.eclipse.emf.ecore.util.ExtendedMetaData;
28import org.eclipse.emf.ecore.util.FeatureMap;
29import org.eclipse.emf.ecore.util.FeatureMap.Entry;
30import org.eclipse.emf.ecore.util.InternalEList;
31import tools.refinery.viatra.runtime.base.api.BaseIndexOptions;
32import tools.refinery.viatra.runtime.base.api.filters.IBaseIndexFeatureFilter;
33import tools.refinery.viatra.runtime.base.api.filters.IBaseIndexObjectFilter;
34import tools.refinery.viatra.runtime.base.api.filters.IBaseIndexResourceFilter;
35
36/**
37 * @author Bergmann Gábor
38 *
39 * Does not directly visit derived links, unless marked as a WellBehavingFeature. Derived edges are
40 * automatically interpreted correctly in these cases: - EFeatureMaps - eOpposites of containments
41 *
42 * @noextend This class is not intended to be subclassed by clients.
43 */
44public class EMFModelComprehension {
45
46 /**
47 * @since 2.3
48 */
49 protected BaseIndexOptions options;
50
51 /**
52 * Creates a model comprehension with the specified options. The options are copied, therefore subsequent changes
53 * will not affect the comprehension.
54 */
55 public EMFModelComprehension(BaseIndexOptions options) {
56 this.options = options.copy();
57 }
58
59 /**
60 * Should not traverse this feature directly. It is still possible that it can be represented in IQBase if
61 * {@link #representable(EStructuralFeature)} is true.
62 */
63 public boolean untraversableDirectly(EStructuralFeature feature) {
64
65 if((feature instanceof EReference && ((EReference)feature).isContainer())) {
66 // container features are always represented through their opposite
67 return true;
68 }
69
70 //If the feature is filtered by the feature filter specified in the BaseIndexOptions, return true
71 final IBaseIndexFeatureFilter featureFilter = options.getFeatureFilterConfiguration();
72 if(featureFilter != null && featureFilter.isFiltered(feature)){
73 return true;
74 }
75
76 boolean suspect = onlySamplingFeature(feature);
77 if(suspect) {
78 // even if the feature can only be sampled, it may be used if the proper base index option is set
79 suspect = options.isTraverseOnlyWellBehavingDerivedFeatures();
80 }
81 return suspect;
82 }
83
84 /**
85 * Decides whether a feature can only be sampled as there is no guarantee that proper notifications will be
86 * delivered by their implementation.
87 *
88 * <p/> Such features are derived (and/or volatile) features that are not well-behaving.
89 */
90 public boolean onlySamplingFeature(EStructuralFeature feature) {
91 boolean suspect =
92 feature.isDerived() ||
93 feature.isVolatile();
94 if (suspect) {
95 // override support here
96 // (e.g. if manual notifications available, or no changes expected afterwards)
97 suspect = !WellbehavingDerivedFeatureRegistry.isWellbehavingFeature(feature);
98 // TODO verbose flag somewhere to ease debugging (for such warnings)
99 // TODO add warning about not visited subtree (containment, FeatureMap and annotation didn't define
100 // otherwise)
101 }
102 return suspect;
103 }
104
105 /**
106 * This feature can be represented in IQBase.
107 */
108 public boolean representable(EStructuralFeature feature) {
109 if (!untraversableDirectly(feature))
110 return true;
111
112 if (feature instanceof EReference) {
113 final EReference reference = (EReference) feature;
114 if (reference.isContainer() && representable(reference.getEOpposite()))
115 return true;
116 }
117
118 boolean isMixed = "mixed".equals(EcoreUtil.getAnnotation(feature.getEContainingClass(),
119 ExtendedMetaData.ANNOTATION_URI, "kind"));
120 if (isMixed)
121 return true; // TODO maybe check the "name"=":mixed" or ":group" feature for representability?
122
123 // Group features are alternative features that are used when the ecore is derived from an xsd schema containing
124 // choices; in that case instead of the asked feature we should index the corresponding group feature
125 final EStructuralFeature groupFeature = ExtendedMetaData.INSTANCE.getGroup(feature);
126 if (groupFeature != null) {
127 return representable(groupFeature);
128 }
129
130 return false;
131 }
132
133 /**
134 * Resource filters not consulted here (for performance), because model roots are assumed to be pre-filtered.
135 */
136 public void traverseModel(EMFVisitor visitor, Notifier source) {
137 if (source == null)
138 return;
139 if (source instanceof EObject) {
140 final EObject sourceObject = (EObject) source;
141 if (sourceObject.eIsProxy())
142 throw new IllegalArgumentException("Proxy EObject cannot act as model roots for VIATRA: " + source);
143 traverseObject(visitor, sourceObject);
144 } else if (source instanceof Resource) {
145 traverseResource(visitor, (Resource) source);
146 } else if (source instanceof ResourceSet) {
147 traverseResourceSet(visitor, (ResourceSet) source);
148 }
149 }
150
151 public void traverseResourceSet(EMFVisitor visitor, ResourceSet source) {
152 if (source == null)
153 return;
154 final List<Resource> resources = new ArrayList<Resource>(source.getResources());
155 for (Resource resource : resources) {
156 traverseResourceIfUnfiltered(visitor, resource);
157 }
158 }
159
160 public void traverseResourceIfUnfiltered(EMFVisitor visitor, Resource resource) {
161 final IBaseIndexResourceFilter resourceFilter = options.getResourceFilterConfiguration();
162 if (resourceFilter != null && resourceFilter.isResourceFiltered(resource))
163 return;
164 final IBaseIndexObjectFilter objectFilter = options.getObjectFilterConfiguration();
165 if (objectFilter != null && objectFilter.isFiltered(resource))
166 return;
167
168 traverseResource(visitor, resource);
169 }
170
171 public void traverseResource(EMFVisitor visitor, Resource source) {
172 if (source == null)
173 return;
174 if (visitor.pruneSubtrees(source))
175 return;
176 final EList<EObject> contents = source.getContents();
177 for (EObject eObject : contents) {
178 traverseObjectIfUnfiltered(visitor, eObject);
179 }
180 }
181
182
183 public void traverseObjectIfUnfiltered(EMFVisitor visitor, EObject targetObject) {
184 final IBaseIndexObjectFilter objectFilter = options.getObjectFilterConfiguration();
185 if (objectFilter != null && objectFilter.isFiltered(targetObject))
186 return;
187
188 traverseObject(visitor, targetObject);
189 }
190
191 public void traverseObject(EMFVisitor visitor, EObject source) {
192 if (source == null)
193 return;
194
195 if (visitor.preOrder()) visitor.visitElement(source);
196 for (EStructuralFeature feature : source.eClass().getEAllStructuralFeatures()) {
197 if (untraversableDirectly(feature))
198 continue;
199 final boolean visitorPrunes = visitor.pruneFeature(feature);
200 if (visitorPrunes && !unprunableFeature(visitor, source, feature))
201 continue;
202
203 traverseFeatureTargets(visitor, source, feature, visitorPrunes);
204 }
205 if (!visitor.preOrder()) visitor.visitElement(source);
206 }
207
208 protected void traverseFeatureTargets(EMFVisitor visitor, EObject source, EStructuralFeature feature,
209 final boolean visitorPrunes) {
210 boolean attemptResolve = (feature instanceof EAttribute) || visitor.attemptProxyResolutions(source, (EReference)feature);
211 if (feature.isMany()) {
212 EList<?> targets = (EList<?>) source.eGet(feature);
213 int position = 0;
214 Iterator<?> iterator = attemptResolve ? targets.iterator() : ((InternalEList<?>)targets).basicIterator();
215 while (iterator.hasNext()) {
216 Object target = iterator.next();
217 traverseFeatureInternal(visitor, source, feature, target, visitorPrunes, position++);
218 }
219 } else {
220 Object target = source.eGet(feature, attemptResolve);
221 if (target != null)
222 traverseFeatureInternal(visitor, source, feature, target, visitorPrunes, null);
223 }
224 }
225 /**
226 * @since 2.3
227 */
228 protected boolean unprunableFeature(EMFVisitor visitor, EObject source, EStructuralFeature feature) {
229 return (feature instanceof EAttribute && EcorePackage.eINSTANCE.getEFeatureMapEntry().equals(
230 ((EAttribute) feature).getEAttributeType()))
231 || (feature instanceof EReference && ((EReference) feature).isContainment() && (!visitor
232 .pruneSubtrees(source) || ((EReference) feature).getEOpposite() != null));
233 }
234
235 /**
236 * @param position optional: known position in multivalued collection (for more efficient proxy resolution)
237 */
238 public void traverseFeature(EMFVisitor visitor, EObject source, EStructuralFeature feature, Object target, Integer position) {
239 if (target == null)
240 return;
241 if (untraversableDirectly(feature))
242 return;
243 traverseFeatureInternalSimple(visitor, source, feature, target, position);
244 }
245
246 /**
247 * @param position optional: known position in multivalued collection (for more efficient proxy resolution)
248 * @since 2.3
249 */
250 protected void traverseFeatureInternalSimple(EMFVisitor visitor, EObject source, EStructuralFeature feature,
251 Object target, Integer position) {
252 final boolean visitorPrunes = visitor.pruneFeature(feature);
253 if (visitorPrunes && !unprunableFeature(visitor, source, feature))
254 return;
255
256 traverseFeatureInternal(visitor, source, feature, target, visitorPrunes, position);
257 }
258
259 /**
260 * @pre target != null
261 * @param position optional: known position in multivalued collection (for more efficient proxy resolution)
262 * @since 2.3
263 */
264 protected void traverseFeatureInternal(EMFVisitor visitor, EObject source, EStructuralFeature feature,
265 Object target, boolean visitorPrunes, Integer position) {
266 if (feature instanceof EAttribute) {
267 if (!visitorPrunes)
268 visitor.visitAttribute(source, (EAttribute) feature, target);
269 if (target instanceof FeatureMap.Entry) { // emulated derived edge based on FeatureMap
270 Entry entry = (FeatureMap.Entry) target;
271 final EStructuralFeature emulated = entry.getEStructuralFeature();
272 final Object emulatedTarget = entry.getValue();
273
274 emulateUntraversableFeature(visitor, source, emulated, emulatedTarget);
275 }
276 } else if (feature instanceof EReference) {
277 EReference reference = (EReference) feature;
278 EObject targetObject = (EObject) target;
279 if (reference.isContainment()) {
280 if (!visitor.avoidTransientContainmentLink(source, reference, targetObject)) {
281 if (!visitorPrunes)
282 visitor.visitInternalContainment(source, reference, targetObject);
283 if (!visitor.pruneSubtrees(source)) {
284 // Recursively follow containment...
285 // unless cross-resource containment (in which case we may skip)
286 Resource targetResource = (targetObject instanceof InternalEObject)?
287 ((InternalEObject)targetObject).eDirectResource() : null;
288 boolean crossResourceContainment = targetResource != null;
289 if (!crossResourceContainment || visitor.descendAlongCrossResourceContainments()) {
290 // in-resource containment shall be followed
291 // as well as cross-resource containment for an object scope
292 traverseObjectIfUnfiltered(visitor, targetObject);
293 } else {
294 // do not follow
295 // target will be traversed separately from its resource (resourceSet scope)
296 // or left out of scope (resource scope)
297 }
298 }
299
300 final EReference opposite = reference.getEOpposite();
301 if (opposite != null) { // emulated derived edge based on container opposite
302 emulateUntraversableFeature(visitor, targetObject, opposite, source);
303 }
304 }
305 } else {
306 // if (containedElements.contains(target))
307 if (!visitorPrunes)
308 visitor.visitNonContainmentReference(source, reference, targetObject);
309 }
310 if (targetObject.eIsProxy()) {
311 if (!reference.isResolveProxies()) {
312 throw new IllegalStateException(String.format(
313 "EReference '%s' of EClass %s is set as proxy-non-resolving (i.e. it should never point to a proxy, and never lead cross-resource), " +
314 "yet VIATRA Base encountered a proxy object %s referenced from %s.",
315 reference.getName(), reference.getEContainingClass().getInstanceTypeName(),
316 targetObject, source));
317 }
318 visitor.visitProxyReference(source, reference, targetObject, position);
319 }
320 }
321
322 }
323
324
325 /**
326 * Emulates a derived edge, if it is not visited otherwise
327 *
328 * @pre target != null
329 * @since 2.3
330 */
331 protected void emulateUntraversableFeature(EMFVisitor visitor, EObject source,
332 final EStructuralFeature emulated, final Object target) {
333 if (untraversableDirectly(emulated))
334 traverseFeatureInternalSimple(visitor, source, emulated, target, null);
335 }
336
337 /**
338 * Can be called to attempt to resolve a reference pointing to one or more proxies, using eGet().
339 */
340 @SuppressWarnings("unchecked")
341 public void tryResolveReference(EObject source, EReference reference) {
342 final Object result = source.eGet(reference, true);
343 if (reference.isMany()) {
344 // no idea which element to get, have to iterate through
345 ((Iterable<EObject>) result).forEach(EObject -> {/*proxy resolution as a side-effect of traversal*/});
346 }
347 }
348
349 /**
350 * Finds out whether the Resource is currently loading
351 */
352 public boolean isLoading(Resource resource) {
353 return !resource.isLoaded() || ((Resource.Internal)resource).isLoading();
354 }
355
356} \ No newline at end of file