aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperVisitor.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperVisitor.java')
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperVisitor.java441
1 files changed, 441 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperVisitor.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperVisitor.java
new file mode 100644
index 00000000..b5de8d20
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperVisitor.java
@@ -0,0 +1,441 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Gabor Bergmann, Istvan Rath 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.base.core;
10
11import java.util.Collections;
12import java.util.HashMap;
13import java.util.Map;
14import java.util.Set;
15
16import org.eclipse.emf.ecore.EAttribute;
17import org.eclipse.emf.ecore.EClass;
18import org.eclipse.emf.ecore.EClassifier;
19import org.eclipse.emf.ecore.EObject;
20import org.eclipse.emf.ecore.EReference;
21import org.eclipse.emf.ecore.EStructuralFeature;
22import org.eclipse.emf.ecore.resource.Resource;
23import org.eclipse.emf.ecore.util.EcoreUtil;
24import tools.refinery.viatra.runtime.base.api.IndexingLevel;
25import tools.refinery.viatra.runtime.base.comprehension.EMFModelComprehension;
26import tools.refinery.viatra.runtime.base.comprehension.EMFVisitor;
27
28public abstract class NavigationHelperVisitor extends EMFVisitor {
29
30 /**
31 * A visitor for processing a single change event. Does not traverse the model. Uses all the observed types.
32 */
33 public static class ChangeVisitor extends NavigationHelperVisitor {
34 // local copies to save actual state, in case visitor has to be saved for later due unresolvable proxies
35 private final IndexingLevel wildcardMode;
36 private final Map<Object, IndexingLevel> allObservedClasses;
37 private final Map<Object, IndexingLevel> observedDataTypes;
38 private final Map<Object, IndexingLevel> observedFeatures;
39 private final Map<Object, Boolean> sampledClasses;
40
41 public ChangeVisitor(NavigationHelperImpl navigationHelper, boolean isInsertion) {
42 super(navigationHelper, isInsertion, false);
43 wildcardMode = navigationHelper.getWildcardLevel();
44 allObservedClasses = navigationHelper.getAllObservedClassesInternal(); // new HashSet<EClass>();
45 observedDataTypes = navigationHelper.getObservedDataTypesInternal(); // new HashSet<EDataType>();
46 observedFeatures = navigationHelper.getObservedFeaturesInternal(); // new HashSet<EStructuralFeature>();
47 sampledClasses = new HashMap<Object, Boolean>();
48 }
49
50 @Override
51 protected boolean observesClass(Object eClass) {
52 return wildcardMode.hasInstances() || (IndexingLevel.FULL == allObservedClasses.get(eClass)) || registerSampledClass(eClass);
53 }
54
55 protected boolean registerSampledClass(Object eClass) {
56 Boolean classAlreadyChecked = sampledClasses.get(eClass);
57 if (classAlreadyChecked != null) {
58 return classAlreadyChecked;
59 }
60 boolean isSampledClass = isSampledClass(eClass);
61 sampledClasses.put(eClass, isSampledClass);
62 // do not modify observation configuration during traversal
63 return false;
64 }
65
66 @Override
67 protected boolean observesDataType(Object type) {
68 return wildcardMode.hasInstances() || (IndexingLevel.FULL == observedDataTypes.get(type));
69 }
70
71 @Override
72 protected boolean observesFeature(Object feature) {
73 return wildcardMode.hasInstances() || (IndexingLevel.FULL == observedFeatures.get(feature));
74 }
75
76 @Override
77 protected boolean countsFeature(Object feature) {
78 return wildcardMode.hasStatistics() || observedFeatures.containsKey(feature) && observedFeatures.get(feature).hasStatistics();
79 }
80
81 @Override
82 protected boolean countsDataType(Object type) {
83 return wildcardMode.hasStatistics() || observedDataTypes.containsKey(type) && observedDataTypes.get(type).hasStatistics();
84 }
85
86 @Override
87 protected boolean countsClass(Object eClass) {
88 return wildcardMode.hasStatistics() || allObservedClasses.containsKey(eClass) && allObservedClasses.get(eClass).hasStatistics();
89 }
90 }
91
92 /**
93 * A visitor for a single-pass traversal of the whole model, processing only the given types and inserting them.
94 */
95 public static class TraversingVisitor extends NavigationHelperVisitor {
96 private final IndexingLevel wildcardMode;
97 Map<Object, IndexingLevel> features;
98 Map<Object, IndexingLevel> newClasses;
99 Map<Object, IndexingLevel> oldClasses; // if decends from an old class, no need to add!
100 Map<Object, IndexingLevel> classObservationMap; // true for a class even if only a supertype is included in classes;
101 Map<Object, IndexingLevel> dataTypes;
102
103 public TraversingVisitor(NavigationHelperImpl navigationHelper, Map<Object, IndexingLevel> features, Map<Object, IndexingLevel> newClasses,
104 Map<Object, IndexingLevel> oldClasses, Map<Object, IndexingLevel> dataTypes) {
105 super(navigationHelper, true, true);
106 wildcardMode = navigationHelper.getWildcardLevel();
107 this.features = features;
108 this.newClasses = newClasses;
109 this.oldClasses = oldClasses;
110 this.classObservationMap = new HashMap<Object, IndexingLevel>();
111 this.dataTypes = dataTypes;
112 }
113
114 protected IndexingLevel getExistingIndexingLevel(Object eClass){
115 IndexingLevel result = IndexingLevel.NONE;
116 result = result.merge(oldClasses.get(eClass));
117 result = result.merge(oldClasses.get(metaStore.getEObjectClassKey()));
118 if (IndexingLevel.FULL == result) return result;
119 Set<Object> superTypes = metaStore.getSuperTypeMap().get(eClass);
120 if (superTypes != null){
121 for(Object superType: superTypes){
122 result = result.merge(oldClasses.get(superType));
123 if (IndexingLevel.FULL == result) return result;
124 }
125 }
126 return result;
127 }
128
129 protected IndexingLevel getRequestedIndexingLevel(Object eClass){
130 IndexingLevel result = IndexingLevel.NONE;
131 result = result.merge(newClasses.get(eClass));
132 result = result.merge(newClasses.get(metaStore.getEObjectClassKey()));
133 if (IndexingLevel.FULL == result) return result;
134 Set<Object> superTypes = metaStore.getSuperTypeMap().get(eClass);
135 if (superTypes != null){
136 for(Object superType: superTypes){
137 result = result.merge(newClasses.get(superType));
138 if (IndexingLevel.FULL == result) return result;
139 }
140 }
141 return result;
142 }
143
144 protected IndexingLevel getTraversalIndexing(Object eClass){
145 IndexingLevel level = classObservationMap.get(eClass);
146 if (level == null){
147 IndexingLevel existing = getExistingIndexingLevel(eClass);
148 IndexingLevel requested = getRequestedIndexingLevel(eClass);
149
150 // Calculate the type of indexing which needs to be executed to reach requested indexing state
151 // Considering indexes which are already available
152 if (existing == requested || existing == IndexingLevel.FULL) return IndexingLevel.NONE;
153 if (requested == IndexingLevel.FULL) return IndexingLevel.FULL;
154 if (requested.hasStatistics() == existing.hasStatistics()) return IndexingLevel.NONE;
155 if (requested.hasStatistics()) return IndexingLevel.STATISTICS;
156 return IndexingLevel.NONE;
157 }
158 return level;
159 }
160
161 @Override
162 protected boolean observesClass(Object eClass) {
163 if (wildcardMode.hasInstances()) {
164 return true;
165 }
166 return IndexingLevel.FULL == getTraversalIndexing(eClass);
167 }
168
169 @Override
170 protected boolean countsClass(Object eClass) {
171 return wildcardMode.hasStatistics() || getTraversalIndexing(eClass).hasStatistics();
172 }
173
174 @Override
175 protected boolean observesDataType(Object type) {
176 return wildcardMode.hasInstances() || (IndexingLevel.FULL == dataTypes.get(type));
177 }
178
179 @Override
180 protected boolean observesFeature(Object feature) {
181 return wildcardMode.hasInstances() || (IndexingLevel.FULL == features.get(feature));
182 }
183
184 @Override
185 protected boolean countsDataType(Object type) {
186 return wildcardMode.hasStatistics() || dataTypes.containsKey(type) && dataTypes.get(type).hasStatistics();
187 }
188
189 @Override
190 protected boolean countsFeature(Object feature) {
191 return wildcardMode.hasStatistics() || features.containsKey(feature) && features.get(feature).hasStatistics();
192 }
193
194 @Override
195 public boolean avoidTransientContainmentLink(EObject source, EReference reference, EObject targetObject) {
196 return !targetObject.eAdapters().contains(navigationHelper.contentAdapter);
197 }
198 }
199
200 protected NavigationHelperImpl navigationHelper;
201 boolean isInsertion;
202 boolean descendHierarchy;
203 boolean traverseOnlyWellBehavingDerivedFeatures;
204 EMFBaseIndexInstanceStore instanceStore;
205 EMFBaseIndexStatisticsStore statsStore;
206 EMFBaseIndexMetaStore metaStore;
207
208 NavigationHelperVisitor(NavigationHelperImpl navigationHelper, boolean isInsertion, boolean descendHierarchy) {
209 super(isInsertion /* preOrder iff insertion */);
210 this.navigationHelper = navigationHelper;
211 instanceStore = navigationHelper.instanceStore;
212 metaStore = navigationHelper.metaStore;
213 statsStore = navigationHelper.statsStore;
214 this.isInsertion = isInsertion;
215 this.descendHierarchy = descendHierarchy;
216 this.traverseOnlyWellBehavingDerivedFeatures = navigationHelper.getBaseIndexOptions()
217 .isTraverseOnlyWellBehavingDerivedFeatures();
218 }
219
220 @Override
221 public boolean pruneSubtrees(EObject source) {
222 return !descendHierarchy;
223 }
224
225 @Override
226 public boolean pruneSubtrees(Resource source) {
227 return !descendHierarchy;
228 }
229
230 @Override
231 public boolean pruneFeature(EStructuralFeature feature) {
232 Object featureKey = toKey(feature);
233 if (observesFeature(featureKey) || countsFeature(featureKey)) {
234 return false;
235 }
236 if (feature instanceof EAttribute){
237 Object dataTypeKey = toKey(((EAttribute) feature).getEAttributeType());
238 if (observesDataType(dataTypeKey) || countsDataType(dataTypeKey)) {
239 return false;
240 }
241 }
242 return !(isInsertion && navigationHelper.isExpansionAllowed() && feature instanceof EReference
243 && !((EReference) feature).isContainment());
244 }
245
246 /**
247 * @param feature
248 * key of feature (EStructuralFeature or String id)
249 */
250 protected abstract boolean observesFeature(Object feature);
251
252 /**
253 * @param feature
254 * key of data type (EDatatype or String id)
255 */
256 protected abstract boolean observesDataType(Object type);
257
258 /**
259 * @param feature
260 * key of class (EClass or String id)
261 */
262 protected abstract boolean observesClass(Object eClass);
263
264 protected abstract boolean countsFeature(Object feature);
265
266 protected abstract boolean countsDataType(Object type);
267
268 protected abstract boolean countsClass(Object eClass);
269
270 @Override
271 public void visitElement(EObject source) {
272 EClass eClass = source.eClass();
273 if (eClass.eIsProxy()) {
274 eClass = (EClass) EcoreUtil.resolve(eClass, source);
275 }
276
277 final Object classKey = toKey(eClass);
278 if (observesClass(classKey)) {
279 if (isInsertion) {
280 instanceStore.insertIntoInstanceSet(classKey, source);
281 } else {
282 instanceStore.removeFromInstanceSet(classKey, source);
283 }
284 }
285 if (countsClass(classKey)){
286 if (isInsertion){
287 statsStore.addInstance(classKey);
288 } else {
289 statsStore.removeInstance(classKey);
290 }
291 }
292 }
293
294 @Override
295 public void visitAttribute(EObject source, EAttribute feature, Object target) {
296 Object featureKey = toKey(feature);
297 final Object eAttributeType = toKey(feature.getEAttributeType());
298 Object internalValueRepresentation = null;
299 if (observesFeature(featureKey)) {
300 // if (internalValueRepresentation == null) // always true
301 internalValueRepresentation = metaStore.toInternalValueRepresentation(target);
302 boolean unique = feature.isUnique();
303 if (isInsertion) {
304 instanceStore.insertFeatureTuple(featureKey, unique, internalValueRepresentation, source);
305 } else {
306 instanceStore.removeFeatureTuple(featureKey, unique, internalValueRepresentation, source);
307 }
308 }
309 if (countsFeature(featureKey)){
310 if (isInsertion) {
311 statsStore.addFeature(source, featureKey);
312 }else{
313 statsStore.removeFeature(source, featureKey);
314 }
315 }
316 if (observesDataType(eAttributeType)) {
317 if (internalValueRepresentation == null)
318 internalValueRepresentation = metaStore.toInternalValueRepresentation(target);
319 if (isInsertion) {
320 instanceStore.insertIntoDataTypeMap(eAttributeType, internalValueRepresentation);
321 } else {
322 instanceStore.removeFromDataTypeMap(eAttributeType, internalValueRepresentation);
323 }
324 }
325 if (countsDataType(eAttributeType)){
326 if (isInsertion){
327 statsStore.addInstance(eAttributeType);
328 } else {
329 statsStore.removeInstance(eAttributeType);
330 }
331 }
332 }
333
334 @Override
335 public void visitInternalContainment(EObject source, EReference feature, EObject target) {
336 visitReference(source, feature, target);
337 }
338
339 @Override
340 public void visitNonContainmentReference(EObject source, EReference feature, EObject target) {
341 visitReference(source, feature, target);
342 if (isInsertion) {
343 navigationHelper.considerForExpansion(target);
344 }
345 }
346
347 protected void visitReference(EObject source, EReference feature, EObject target) {
348 Object featureKey = toKey(feature);
349 if (observesFeature(featureKey)) {
350 boolean unique = feature.isUnique();
351 if (isInsertion) {
352 instanceStore.insertFeatureTuple(featureKey, unique, target, source);
353 } else {
354 instanceStore.removeFeatureTuple(featureKey, unique, target, source);
355 }
356 }
357 if (countsFeature(featureKey)){
358 if (isInsertion){
359 statsStore.addFeature(source, featureKey);
360 } else {
361 statsStore.removeFeature(source, featureKey);
362 }
363 }
364 }
365
366 @Override
367 // do not attempt to resolve proxies referenced from resources that are still being loaded
368 public boolean attemptProxyResolutions(EObject source, EReference feature) {
369 // emptyness is checked first to avoid costly resource lookup in most cases
370 if (navigationHelper.resolutionDelayingResources.isEmpty())
371 return true;
372 else
373 return ! navigationHelper.resolutionDelayingResources.contains(source.eResource());
374 }
375
376 @Override
377 public void visitProxyReference(EObject source, EReference reference, EObject targetObject, Integer position) {
378 if (isInsertion) { // only attempt to resolve proxies if they are inserted
379 // final Object result = source.eGet(reference, true);
380 // if (reference.isMany()) {
381 // // no idea which element to get, have to iterate through
382 // for (EObject touch : (Iterable<EObject>) result);
383 // }
384 if (navigationHelper.isFeatureResolveIgnored(reference))
385 return; // skip resolution; would be ignored anyways
386 if (position != null && reference.isMany() && attemptProxyResolutions(source, reference)) {
387 // there is added value in doing the resolution now, when we know the position
388 // this may save an iteration through the EList if successful
389 @SuppressWarnings("unchecked")
390 EObject touch = ((java.util.List<EObject>) source.eGet(reference, true)).get(position);
391 // if resolution successful, no further action needed
392 if (!touch.eIsProxy())
393 return;
394 }
395 // otherwise, attempt resolution later, at the end of the coalesced traversal block
396 navigationHelper.delayedProxyResolutions.addPairOrNop(source, reference);
397 }
398 }
399
400 protected Object toKey(EStructuralFeature feature) {
401 return metaStore.toKey(feature);
402 }
403
404 protected Object toKey(EClassifier eClassifier) {
405 return metaStore.toKey(eClassifier);
406 }
407
408 /**
409 * Decides whether the type must be observed in order to allow re-sampling of any of its features. If not
410 * well-behaving features are traversed and there is such a feature for this class, the class will be registered
411 * into the navigation helper, which may cause a re-traversal.
412 *
413 */
414 protected boolean isSampledClass(Object eClass) {
415 if (!traverseOnlyWellBehavingDerivedFeatures) {
416 // TODO we could save this reverse lookup if the calling method would have the EClass, not just the key
417 EClass knownClass = (EClass) metaStore.getKnownClassifierForKey(eClass);
418 // check features that are traversed, and whether there is any that must be sampled
419 for (EStructuralFeature feature : knownClass.getEAllStructuralFeatures()) {
420 EMFModelComprehension comprehension = navigationHelper.getComprehension();
421 if (comprehension.untraversableDirectly(feature))
422 continue;
423 final boolean visitorPrunes = pruneFeature(feature);
424 if (visitorPrunes)
425 continue;
426 // we found a feature to be visited
427 if (comprehension.onlySamplingFeature(feature)) {
428 // we found a feature that must be sampled
429 navigationHelper.registerEClasses(Collections.singleton(feature.getEContainingClass()), IndexingLevel.FULL);
430 return true;
431 }
432 }
433 }
434 return false;
435 }
436
437 @Override
438 public boolean descendAlongCrossResourceContainments() {
439 return this.navigationHelper.traversalDescendsAlongCrossResourceContainment();
440 }
441}