diff options
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.java | 441 |
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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.base.core; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import org.eclipse.emf.ecore.EAttribute; | ||
17 | import org.eclipse.emf.ecore.EClass; | ||
18 | import org.eclipse.emf.ecore.EClassifier; | ||
19 | import org.eclipse.emf.ecore.EObject; | ||
20 | import org.eclipse.emf.ecore.EReference; | ||
21 | import org.eclipse.emf.ecore.EStructuralFeature; | ||
22 | import org.eclipse.emf.ecore.resource.Resource; | ||
23 | import org.eclipse.emf.ecore.util.EcoreUtil; | ||
24 | import tools.refinery.viatra.runtime.base.api.IndexingLevel; | ||
25 | import tools.refinery.viatra.runtime.base.comprehension.EMFModelComprehension; | ||
26 | import tools.refinery.viatra.runtime.base.comprehension.EMFVisitor; | ||
27 | |||
28 | public 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 | } | ||