blob: f34dc5209751fdc7f5ba4295b10be6776d413b3f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010-2012, Istvan Rath and Daniel Varro
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Tamas Szabo, Gabor Bergmann - initial API and implementation
*******************************************************************************/
package org.eclipse.viatra.query.runtime.base.core;
import static com.google.common.base.Preconditions.checkArgument;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.Callable;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.notify.NotifyingList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.viatra.query.runtime.base.api.BaseIndexOptions;
import org.eclipse.viatra.query.runtime.base.api.DataTypeListener;
import org.eclipse.viatra.query.runtime.base.api.EMFBaseIndexChangeListener;
import org.eclipse.viatra.query.runtime.base.api.FeatureListener;
import org.eclipse.viatra.query.runtime.base.api.IEClassifierProcessor.IEClassProcessor;
import org.eclipse.viatra.query.runtime.base.api.IEClassifierProcessor.IEDataTypeProcessor;
import org.eclipse.viatra.query.runtime.base.api.IEMFIndexingErrorListener;
import org.eclipse.viatra.query.runtime.base.api.IEStructuralFeatureProcessor;
import org.eclipse.viatra.query.runtime.base.api.IndexingLevel;
import org.eclipse.viatra.query.runtime.base.api.InstanceListener;
import org.eclipse.viatra.query.runtime.base.api.LightweightEObjectObserver;
import org.eclipse.viatra.query.runtime.base.api.NavigationHelper;
import org.eclipse.viatra.query.runtime.base.api.filters.IBaseIndexObjectFilter;
import org.eclipse.viatra.query.runtime.base.api.filters.IBaseIndexResourceFilter;
import org.eclipse.viatra.query.runtime.base.comprehension.EMFModelComprehension;
import org.eclipse.viatra.query.runtime.base.comprehension.EMFVisitor;
import org.eclipse.viatra.query.runtime.base.exception.ViatraBaseException;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
public class NavigationHelperImpl implements NavigationHelper {
/**
* This is never null.
*/
protected IndexingLevel wildcardMode;
protected Notifier notifier;
protected Set<Notifier> modelRoots;
private boolean expansionAllowed;
// protected NavigationHelperVisitor visitor;
protected NavigationHelperContentAdapter contentAdapter;
private final Logger logger;
// type object or String id
protected Map<Object, IndexingLevel> directlyObservedClasses = new HashMap<Object, IndexingLevel>();
// including subclasses; if null, must be recomputed
protected Map<Object, IndexingLevel> allObservedClasses = null;
protected Map<Object, IndexingLevel> observedDataTypes;
protected Map<Object, IndexingLevel> observedFeatures;
// ignore RESOLVE for these features, as they are just starting to be observed - see [428458]
protected Set<Object> ignoreResolveNotificationFeatures;
/**
* Feature registration and model traversal is delayed while true
*/
protected boolean delayTraversals = false;
/**
* Classes (or String ID in dynamic mode) to be registered once the coalescing period is over
*/
protected Map<Object, IndexingLevel> delayedClasses;
/**
* EStructuralFeatures (or String ID in dynamic mode) to be registered once the coalescing period is over
*/
protected Map<Object, IndexingLevel> delayedFeatures;
/**
* EDataTypes (or String ID in dynamic mode) to be registered once the coalescing period is over
*/
protected Map<Object, IndexingLevel> delayedDataTypes;
/**
* Features per EObject to be resolved later (towards the end of a coalescing period when no Resources are loading)
*/
protected Multimap<EObject, EReference> delayedProxyResolutions = LinkedHashMultimap.create();
/**
* Reasources that are currently loading, implying the proxy resolution attempts should be delayed
*/
protected Set<Resource> resolutionDelayingResources = new HashSet<Resource>();
protected Queue<Runnable> traversalCallbacks = new LinkedList<Runnable>();
/**
* These global listeners will be called after updates.
*/
// private final Set<Runnable> afterUpdateCallbacks;
private final Set<EMFBaseIndexChangeListener> baseIndexChangeListeners;
private final Map<LightweightEObjectObserver, Collection<EObject>> lightweightObservers;
// These are the user subscriptions to notifications
private final Map<InstanceListener, Set<EClass>> subscribedInstanceListeners;
private final Map<FeatureListener, Set<EStructuralFeature>> subscribedFeatureListeners;
private final Map<DataTypeListener, Set<EDataType>> subscribedDataTypeListeners;
// these are the internal notification tables
// (element Type or String id) -> listener -> (subscription types)
// if null, must be recomputed from subscriptions
// potentially multiple subscription types for each element type because (a) nsURI collisions, (b) multiple
// supertypes
private Table<Object, InstanceListener, Set<EClass>> instanceListeners;
private Table<Object, FeatureListener, Set<EStructuralFeature>> featureListeners;
private Table<Object, DataTypeListener, Set<EDataType>> dataTypeListeners;
private final Set<IEMFIndexingErrorListener> errorListeners;
private final BaseIndexOptions baseIndexOptions;
private EMFModelComprehension comprehension;
private boolean loggedRegistrationMessage = false;
EMFBaseIndexMetaStore metaStore;
EMFBaseIndexInstanceStore instanceStore;
EMFBaseIndexStatisticsStore statsStore;
<T> Set<T> setMinus(Collection<? extends T> a, Collection<T> b) {
Set<T> result = new HashSet<T>(a);
result.removeAll(b);
return result;
}
@SuppressWarnings("unchecked")
<T extends EObject> Set<T> resolveAllInternal(Set<? extends T> a) {
if (a == null)
a = Collections.emptySet();
Set<T> result = new HashSet<T>();
for (T t : a) {
if (t.eIsProxy()) {
result.add((T) EcoreUtil.resolve(t, (ResourceSet) null));
} else {
result.add(t);
}
}
return result;
}
Set<Object> resolveClassifiersToKey(Set<? extends EClassifier> classes) {
Set<? extends EClassifier> resolveds = resolveAllInternal(classes);
Set<Object> result = new HashSet<Object>();
for (EClassifier resolved : resolveds) {
result.add(toKey(resolved));
}
return result;
}
Set<Object> resolveFeaturesToKey(Set<? extends EStructuralFeature> features) {
Set<EStructuralFeature> resolveds = resolveAllInternal(features);
Set<Object> result = new HashSet<Object>();
for (EStructuralFeature resolved : resolveds) {
result.add(toKey(resolved));
}
return result;
}
@Override
public boolean isInWildcardMode() {
return isInWildcardMode(IndexingLevel.FULL);
}
@Override
public boolean isInWildcardMode(IndexingLevel level) {
return wildcardMode.providesLevel(level);
}
@Override
public boolean isInDynamicEMFMode() {
return baseIndexOptions.isDynamicEMFMode();
}
/**
* @return the baseIndexOptions
*/
public BaseIndexOptions getBaseIndexOptions() {
return baseIndexOptions.copy();
}
/**
* @return the comprehension
*/
public EMFModelComprehension getComprehension() {
return comprehension;
}
public NavigationHelperImpl(Notifier emfRoot, BaseIndexOptions options, Logger logger) throws ViatraBaseException {
this.baseIndexOptions = options.copy();
this.logger = logger;
assert (logger != null);
this.comprehension = new EMFModelComprehension(baseIndexOptions);
this.wildcardMode = baseIndexOptions.getWildcardLevel();
this.subscribedInstanceListeners = new HashMap<InstanceListener, Set<EClass>>();
this.subscribedFeatureListeners = new HashMap<FeatureListener, Set<EStructuralFeature>>();
this.subscribedDataTypeListeners = new HashMap<DataTypeListener, Set<EDataType>>();
this.lightweightObservers = new HashMap<LightweightEObjectObserver, Collection<EObject>>();
this.observedFeatures = new HashMap<Object, IndexingLevel>();
this.ignoreResolveNotificationFeatures = new HashSet<Object>();
this.observedDataTypes = new HashMap<Object, IndexingLevel>();
metaStore = new EMFBaseIndexMetaStore(this);
instanceStore = new EMFBaseIndexInstanceStore(this, logger);
statsStore = new EMFBaseIndexStatisticsStore(this, logger);
this.contentAdapter = new NavigationHelperContentAdapter(this);
this.baseIndexChangeListeners = new HashSet<EMFBaseIndexChangeListener>();
this.errorListeners = new LinkedHashSet<IEMFIndexingErrorListener>();
this.notifier = emfRoot;
this.modelRoots = new HashSet<Notifier>();
this.expansionAllowed = false;
if (emfRoot != null) {
addRootInternal(emfRoot);
}
}
@Override
public IndexingLevel getWildcardLevel() {
return wildcardMode;
}
@Override
public void setWildcardLevel(final IndexingLevel level) {
try{
IndexingLevel mergedLevel = NavigationHelperImpl.this.wildcardMode.merge(level);
if (mergedLevel != NavigationHelperImpl.this.wildcardMode){
NavigationHelperImpl.this.wildcardMode = mergedLevel;
// force traversal upon change of wildcard level
final NavigationHelperVisitor visitor = new NavigationHelperVisitor.TraversingVisitor(this,
Collections.<Object, IndexingLevel>emptyMap(), Collections.<Object, IndexingLevel>emptyMap(), Collections.<Object, IndexingLevel>emptyMap(), Collections.<Object, IndexingLevel>emptyMap());
coalesceTraversals(new Callable<Void>() {
@Override
public Void call() throws Exception {
traverse(visitor);
return null;
}
});
}
} catch (InvocationTargetException ex) {
processingFatal(ex.getCause(), "Setting wildcard level: " + level);
} catch (Exception ex) {
processingFatal(ex, "Setting wildcard level: " + level);
}
}
public NavigationHelperContentAdapter getContentAdapter() {
return contentAdapter;
}
public Map<Object, IndexingLevel> getObservedFeaturesInternal() {
return observedFeatures;
}
public boolean isFeatureResolveIgnored(EStructuralFeature feature) {
return ignoreResolveNotificationFeatures.contains(toKey(feature));
}
@Override
public void dispose() {
ensureNoListenersForDispose();
for (Notifier root : modelRoots) {
contentAdapter.removeAdapter(root);
}
}
@Override
public Set<Object> getDataTypeInstances(EDataType type) {
Object typeKey = toKey(type);
Map<Object, Integer> valMap = instanceStore.getDataTypeMap(typeKey);
if (valMap != null) {
return Collections.unmodifiableSet(valMap.keySet());
} else {
return Collections.emptySet();
}
}
@Override
public Set<Setting> findByAttributeValue(Object value_) {
Object value = toCanonicalValueRepresentation(value_);
Set<Setting> retSet = new HashSet<Setting>();
Map<Object, Collection<EObject>> valMap = instanceStore.getValueToFeatureToHolderMap().row(value);
for (Entry<Object, Collection<EObject>> entry : valMap.entrySet()) {
final Collection<EObject> holders = entry.getValue();
EStructuralFeature feature = metaStore.getKnownFeatureForKey(entry.getKey());
for (EObject holder : EMFBaseIndexInstanceStore.holderCollectionToUniqueSet(holders)) {
retSet.add(new NavigationHelperSetting(feature, holder, value));
}
}
return retSet;
}
@Override
public Set<Setting> findByAttributeValue(Object value_, Collection<EAttribute> attributes) {
Object value = toCanonicalValueRepresentation(value_);
Set<Setting> retSet = new HashSet<Setting>();
Map<Object, Collection<EObject>> valMap = instanceStore.getValueToFeatureToHolderMap().row(value);
for (EAttribute attr : attributes) {
Object feature = toKey(attr);
final Collection<EObject> holders = valMap.get(feature);
if (holders != null) {
for (EObject holder : EMFBaseIndexInstanceStore.holderCollectionToUniqueSet(holders)) {
retSet.add(new NavigationHelperSetting(attr, holder, value));
}
}
}
return retSet;
}
@Override
public Set<EObject> findByAttributeValue(Object value_, EAttribute attribute) {
Object value = toCanonicalValueRepresentation(value_);
Map<Object, Collection<EObject>> valMap = instanceStore.getValueToFeatureToHolderMap().row(value);
Object feature = toKey(attribute);
final Collection<EObject> holders = valMap.get(feature);
if (holders == null) {
return Collections.emptySet();
} else {
return Collections.unmodifiableSet(EMFBaseIndexInstanceStore.holderCollectionToUniqueSet(holders));
}
}
@Override
public void processAllFeatureInstances(EStructuralFeature feature, IEStructuralFeatureProcessor processor) {
final Map<Object, Collection<EObject>> instanceMap = instanceStore.getValueToFeatureToHolderMap()
.column(toKey(feature));
for (Entry<Object, Collection<EObject>> entry : instanceMap.entrySet()) {
final Collection<EObject> holders = entry.getValue();
for (EObject src : EMFBaseIndexInstanceStore.holderCollectionToUniqueSet(holders)) {
processor.process(feature, src, entry.getKey());
}
}
}
@Override
public void processDirectInstances(EClass type, IEClassProcessor processor) {
Object typeKey = toKey(type);
processDirectInstancesInternal(type, processor, typeKey);
}
@Override
public void processAllInstances(EClass type, IEClassProcessor processor) {
Object typeKey = toKey(type);
Set<Object> subTypes = metaStore.getSubTypeMap().get(typeKey);
if (subTypes != null) {
for (Object subTypeKey : subTypes) {
processDirectInstancesInternal(type, processor, subTypeKey);
}
}
processDirectInstancesInternal(type, processor, typeKey);
}
@Override
public void processDataTypeInstances(EDataType type, IEDataTypeProcessor processor) {
Object typeKey = toKey(type);
Map<Object, Integer> valMap = instanceStore.getDataTypeMap(typeKey);
if (valMap == null) {
return;
}
for (Object value : valMap.keySet()) {
processor.process(type, value);
}
}
private void processDirectInstancesInternal(EClass type, IEClassProcessor processor, Object typeKey) {
final Set<EObject> instances = instanceStore.getInstanceSet(typeKey);
if (instances != null) {
for (EObject eObject : instances) {
processor.process(type, eObject);
}
}
}
@Override
public Set<Setting> getInverseReferences(EObject target) {
Set<Setting> retSet = new HashSet<Setting>();
Map<Object, Collection<EObject>> valMap = instanceStore.getValueToFeatureToHolderMap().row(target);
for (Entry<Object, Collection<EObject>> entry : valMap.entrySet()) {
final Collection<EObject> holders = entry.getValue();
for (EObject source : EMFBaseIndexInstanceStore.holderCollectionToUniqueSet(holders)) {
EStructuralFeature feature = metaStore.getKnownFeatureForKey(entry.getKey());
retSet.add(new NavigationHelperSetting(feature, source, target));
}
}
return retSet;
}
@Override
public Set<Setting> getInverseReferences(EObject target, Collection<EReference> references) {
Set<Setting> retSet = new HashSet<Setting>();
Map<Object, Collection<EObject>> valMap = instanceStore.getValueToFeatureToHolderMap().row(target);
for (EReference ref : references) {
Object feature = toKey(ref);
final Collection<EObject> holders = valMap.get(feature);
if (holders != null) {
for (EObject source : EMFBaseIndexInstanceStore.holderCollectionToUniqueSet(holders)) {
retSet.add(new NavigationHelperSetting(ref, source, target));
}
}
}
return retSet;
}
@Override
public Set<EObject> getInverseReferences(EObject target, EReference reference) {
Object feature = toKey(reference);
Map<Object, Collection<EObject>> valMap = instanceStore.getValueToFeatureToHolderMap().row(target);
final Collection<EObject> holders = valMap.get(feature);
if (holders == null) {
return Collections.emptySet();
} else {
return Collections.unmodifiableSet(EMFBaseIndexInstanceStore.holderCollectionToUniqueSet(holders));
}
}
@Override
@SuppressWarnings("unchecked")
public Set<EObject> getReferenceValues(EObject source, EReference reference) {
Set<Object> targets = getFeatureTargets(source, reference);
return (Set<EObject>) (Set<?>) targets; // this is known to be safe, as EReferences can only point to EObjects
}
@Override
public Set<Object> getFeatureTargets(EObject source, EStructuralFeature _feature) {
Object feature = toKey(_feature);
final Set<Object> valSet = instanceStore.getHolderToFeatureToValueMap().get(source, feature);
if (valSet == null) {
return Collections.emptySet();
} else {
return Collections.unmodifiableSet(valSet);
}
}
@Override
public Map<EObject, Set<Object>> getFeatureInstances(EStructuralFeature _feature) {
Object feature = toKey(_feature);
final Map<EObject, Set<Object>> valMap = instanceStore.getHolderToFeatureToValueMap().column(feature);
if (valMap == null) {
return Collections.emptyMap();
} else {
return Collections.unmodifiableMap(valMap);
}
}
@Override
public Set<EObject> getDirectInstances(EClass type) {
Object typeKey = toKey(type);
Set<EObject> valSet = instanceStore.getInstanceSet(typeKey);
if (valSet == null) {
return Collections.emptySet();
} else {
return Collections.unmodifiableSet(valSet);
}
}
private Object toKey(EClassifier eClassifier) {
return metaStore.toKey(eClassifier);
}
private Object toKey(EStructuralFeature feature) {
return metaStore.toKey(feature);
}
@Override
public Object toCanonicalValueRepresentation(Object value) {
return metaStore.toInternalValueRepresentation(value);
}
@Override
public Set<EObject> getAllInstances(EClass type) {
Set<EObject> retSet = new HashSet<EObject>();
Object typeKey = toKey(type);
Set<Object> subTypes = metaStore.getSubTypeMap().get(typeKey);
if (subTypes != null) {
for (Object subTypeKey : subTypes) {
final Set<EObject> instances = instanceStore.getInstanceSet(subTypeKey);
if (instances != null) {
retSet.addAll(instances);
}
}
}
final Set<EObject> instances = instanceStore.getInstanceSet(typeKey);
if (instances != null) {
retSet.addAll(instances);
}
return retSet;
}
@Override
public boolean isInstanceOfUnscoped(EObject object, EClass clazz) {
Object candidateTypeKey = toKey(clazz);
Object typeKey = toKey(object.eClass());
if (candidateTypeKey.equals(typeKey)) return true;
if (metaStore.getEObjectClassKey().equals(candidateTypeKey)) return true;
Set<Object> superTypes = metaStore.getSuperTypeMap().get(typeKey);
return superTypes.contains(candidateTypeKey);
}
@Override
public Set<EObject> findByFeatureValue(Object value_, EStructuralFeature _feature) {
Object value = toCanonicalValueRepresentation(value_);
Object feature = toKey(_feature);
Set<EObject> retSet = new HashSet<EObject>();
Map<Object, Collection<EObject>> valMap = instanceStore.getValueToFeatureToHolderMap().row(value);
final Collection<EObject> holders = valMap.get(feature);
if (holders != null) {
retSet.addAll(EMFBaseIndexInstanceStore.holderCollectionToUniqueSet(holders));
}
return retSet;
}
@Override
public Set<EObject> getHoldersOfFeature(EStructuralFeature _feature) {
Object feature = toKey(_feature);
Multiset<EObject> holders = instanceStore.getFeatureToHolderMap().get(feature);
if (holders == null) {
return Collections.emptySet();
} else {
return Collections.unmodifiableSet(holders.elementSet());
}
}
@Override
public void addInstanceListener(Collection<EClass> classes, InstanceListener listener) {
Set<EClass> registered = this.subscribedInstanceListeners.get(listener);
if (registered == null) {
registered = new HashSet<EClass>();
this.subscribedInstanceListeners.put(listener, registered);
}
Set<EClass> delta = setMinus(classes, registered);
if (!delta.isEmpty()) {
registered.addAll(delta);
if (instanceListeners != null) { // if already computed
for (EClass subscriptionType : delta) {
final Object superElementTypeKey = toKey(subscriptionType);
addInstanceListenerInternal(listener, subscriptionType, superElementTypeKey);
final Set<Object> subTypeKeys = metaStore.getSubTypeMap().get(superElementTypeKey);
if (subTypeKeys != null)
for (Object subTypeKey : subTypeKeys) {
addInstanceListenerInternal(listener, subscriptionType, subTypeKey);
}
}
}
}
}
@Override
public void removeInstanceListener(Collection<EClass> classes, InstanceListener listener) {
Set<EClass> restriction = this.subscribedInstanceListeners.get(listener);
if (restriction != null) {
boolean changed = restriction.removeAll(classes);
if (restriction.size() == 0) {
this.subscribedInstanceListeners.remove(listener);
}
if (changed)
instanceListeners = null; // recompute later on demand
}
}
@Override
public void addFeatureListener(Collection<? extends EStructuralFeature> features, FeatureListener listener) {
Set<EStructuralFeature> registered = this.subscribedFeatureListeners.get(listener);
if (registered == null) {
registered = new HashSet<EStructuralFeature>();
this.subscribedFeatureListeners.put(listener, registered);
}
Set<EStructuralFeature> delta = setMinus(features, registered);
if (!delta.isEmpty()) {
registered.addAll(delta);
if (featureListeners != null) { // if already computed
for (EStructuralFeature subscriptionType : delta) {
addFeatureListenerInternal(listener, subscriptionType, toKey(subscriptionType));
}
}
}
}
@Override
public void removeFeatureListener(Collection<? extends EStructuralFeature> features, FeatureListener listener) {
Collection<EStructuralFeature> restriction = this.subscribedFeatureListeners.get(listener);
if (restriction != null) {
boolean changed = restriction.removeAll(features);
if (restriction.size() == 0) {
this.subscribedFeatureListeners.remove(listener);
}
if (changed)
featureListeners = null; // recompute later on demand
}
}
@Override
public void addDataTypeListener(Collection<EDataType> types, DataTypeListener listener) {
Set<EDataType> registered = this.subscribedDataTypeListeners.get(listener);
if (registered == null) {
registered = new HashSet<EDataType>();
this.subscribedDataTypeListeners.put(listener, registered);
}
Set<EDataType> delta = setMinus(types, registered);
if (!delta.isEmpty()) {
registered.addAll(delta);
if (dataTypeListeners != null) { // if already computed
for (EDataType subscriptionType : delta) {
addDatatypeListenerInternal(listener, subscriptionType, toKey(subscriptionType));
}
}
}
}
@Override
public void removeDataTypeListener(Collection<EDataType> types, DataTypeListener listener) {
Collection<EDataType> restriction = this.subscribedDataTypeListeners.get(listener);
if (restriction != null) {
boolean changed = restriction.removeAll(types);
if (restriction.size() == 0) {
this.subscribedDataTypeListeners.remove(listener);
}
if (changed)
dataTypeListeners = null; // recompute later on demand
}
}
/**
* @return the observedDataTypes
*/
public Map<Object, IndexingLevel> getObservedDataTypesInternal() {
return observedDataTypes;
}
@Override
public boolean addLightweightEObjectObserver(LightweightEObjectObserver observer, EObject observedObject) {
Collection<EObject> observedObjects = lightweightObservers.get(observer);
if (observedObjects == null) {
observedObjects = new HashSet<EObject>();
lightweightObservers.put(observer, observedObjects);
}
return observedObjects.add(observedObject);
}
@Override
public boolean removeLightweightEObjectObserver(LightweightEObjectObserver observer, EObject observedObject) {
boolean result = false;
Collection<EObject> observedObjects = lightweightObservers.get(observer);
if (observedObjects != null) {
result = observedObjects.remove(observedObject);
if (observedObjects.isEmpty()) {
lightweightObservers.remove(observer);
}
}
return result;
}
/**
* @return the lightweightObservers
*/
public Map<LightweightEObjectObserver, Collection<EObject>> getLightweightObservers() {
return lightweightObservers;
}
public void notifyBaseIndexChangeListeners() {
notifyBaseIndexChangeListeners(instanceStore.isDirty);
if (instanceStore.isDirty) {
instanceStore.isDirty = false;
}
}
/**
* This will run after updates.
*/
protected void notifyBaseIndexChangeListeners(boolean baseIndexChanged) {
if (!baseIndexChangeListeners.isEmpty()) {
for (EMFBaseIndexChangeListener listener : new ArrayList<EMFBaseIndexChangeListener>(
baseIndexChangeListeners)) {
try {
if (!listener.onlyOnIndexChange() || baseIndexChanged) {
listener.notifyChanged(baseIndexChanged);
}
} catch (Exception ex) {
notifyFatalListener("VIATRA Base encountered an error in delivering notifications about changes. ",
ex);
}
}
}
}
void notifyDataTypeListeners(final Object typeKey, final Object value, final boolean isInsertion,
final boolean firstOrLastOccurrence) {
for (final Entry<DataTypeListener, Set<EDataType>> entry : getDataTypeListeners().row(typeKey).entrySet()) {
final DataTypeListener listener = entry.getKey();
for (final EDataType subscriptionType : entry.getValue()) {
if (isInsertion) {
listener.dataTypeInstanceInserted(subscriptionType, value, firstOrLastOccurrence);
} else {
listener.dataTypeInstanceDeleted(subscriptionType, value, firstOrLastOccurrence);
}
}
}
}
void notifyFeatureListeners(final EObject host, final Object featureKey, final Object value,
final boolean isInsertion) {
for (final Entry<FeatureListener, Set<EStructuralFeature>> entry : getFeatureListeners().row(featureKey)
.entrySet()) {
final FeatureListener listener = entry.getKey();
for (final EStructuralFeature subscriptionType : entry.getValue()) {
if (isInsertion) {
listener.featureInserted(host, subscriptionType, value);
} else {
listener.featureDeleted(host, subscriptionType, value);
}
}
}
}
void notifyInstanceListeners(final Object clazzKey, final EObject instance, final boolean isInsertion) {
for (final Entry<InstanceListener, Set<EClass>> entry : getInstanceListeners().row(clazzKey).entrySet()) {
final InstanceListener listener = entry.getKey();
for (final EClass subscriptionType : entry.getValue()) {
if (isInsertion) {
listener.instanceInserted(subscriptionType, instance);
} else {
listener.instanceDeleted(subscriptionType, instance);
}
}
}
}
void notifyLightweightObservers(final EObject host, final EStructuralFeature feature,
final Notification notification) {
for (final Entry<LightweightEObjectObserver, Collection<EObject>> entry : getLightweightObservers()
.entrySet()) {
if (entry.getValue().contains(host)) {
entry.getKey().notifyFeatureChanged(host, feature, notification);
}
}
}
@Override
public void addBaseIndexChangeListener(EMFBaseIndexChangeListener listener) {
checkArgument(listener != null, "Cannot add null listener!");
baseIndexChangeListeners.add(listener);
}
@Override
public void removeBaseIndexChangeListener(EMFBaseIndexChangeListener listener) {
checkArgument(listener != null, "Cannot remove null listener!");
baseIndexChangeListeners.remove(listener);
}
@Override
public boolean addIndexingErrorListener(IEMFIndexingErrorListener listener) {
return errorListeners.add(listener);
}
@Override
public boolean removeIndexingErrorListener(IEMFIndexingErrorListener listener) {
return errorListeners.remove(listener);
}
protected void processingFatal(final Throwable ex, final String task) {
notifyFatalListener(logTaskFormat(task), ex);
}
protected void processingError(final Throwable ex, final String task) {
notifyErrorListener(logTaskFormat(task), ex);
}
public void notifyErrorListener(String message, Throwable t) {
logger.error(message, t);
for (IEMFIndexingErrorListener listener : errorListeners) {
listener.error(message, t);
}
}
public void notifyFatalListener(String message, Throwable t) {
logger.fatal(message, t);
for (IEMFIndexingErrorListener listener : errorListeners) {
listener.fatal(message, t);
}
}
private String logTaskFormat(final String task) {
return "VIATRA Query encountered an error in processing the EMF model. " + "This happened while trying to "
+ task;
}
protected void considerForExpansion(EObject obj) {
if (expansionAllowed) {
Resource eResource = obj.eResource();
if (eResource != null && eResource.getResourceSet() == null) {
expandToAdditionalRoot(eResource);
}
}
}
protected void expandToAdditionalRoot(Notifier root) {
if (modelRoots.contains(root))
return;
if (root instanceof ResourceSet) {
expansionAllowed = true;
} else if (root instanceof Resource) {
IBaseIndexResourceFilter resourceFilter = baseIndexOptions.getResourceFilterConfiguration();
if (resourceFilter != null && resourceFilter.isResourceFiltered((Resource) root))
return;
}
final IBaseIndexObjectFilter objectFilter = baseIndexOptions.getObjectFilterConfiguration();
if (objectFilter != null && objectFilter.isFiltered(root))
return;
// no veto by filters
modelRoots.add(root);
contentAdapter.addAdapter(root);
notifyBaseIndexChangeListeners();
}
/**
* @return the expansionAllowed
*/
public boolean isExpansionAllowed() {
return expansionAllowed;
}
/**
* @return the directlyObservedClasses
*/
public Set<Object> getDirectlyObservedClassesInternal() {
return directlyObservedClasses.keySet();
}
boolean isObservedInternal(Object clazzKey) {
return isInWildcardMode() || getAllObservedClassesInternal().containsKey(clazzKey);
}
/**
* Add the given item the map with the given indexing level if it wasn't already added with a higher level.
*
* @param map
* @param key
* @param level
*/
private static <V> void putIntoMapIfHigherLevel(Map<V, IndexingLevel> map, V key, IndexingLevel level) {
IndexingLevel l = map.get(key);
if (l == null || level.compareTo(l) > 0) {
map.put(key, level);
}
}
private void addObservedClassesInternal(Object eClassKey, IndexingLevel level) {
putIntoMapIfHigherLevel(allObservedClasses, eClassKey, level);
final Set<Object> subTypes = metaStore.getSubTypeMap().get(eClassKey);
if (subTypes != null) {
for (Object subType : subTypes) {
/*
* It is necessary to check if the class has already been added with a higher indexing level as in case
* of multiple inheritance, a subclass may be registered for statistics only but full indexing may be
* required via one of its super classes.
*/
putIntoMapIfHigherLevel(allObservedClasses, subType, level);
}
}
}
/**
* not just the directly observed classes, but also their known subtypes
*/
public Map<Object, IndexingLevel> getAllObservedClassesInternal() {
if (allObservedClasses == null) {
allObservedClasses = new HashMap<Object, IndexingLevel>();
for (Entry<Object, IndexingLevel> entry : directlyObservedClasses.entrySet()) {
Object eClassKey = entry.getKey();
IndexingLevel level = entry.getValue();
addObservedClassesInternal(eClassKey, level);
}
}
return allObservedClasses;
}
/**
* @return the instanceListeners
*/
Table<Object, InstanceListener, Set<EClass>> getInstanceListeners() {
if (instanceListeners == null) {
instanceListeners = HashBasedTable.create(100, 1);
for (Entry<InstanceListener, Set<EClass>> subscription : subscribedInstanceListeners.entrySet()) {
final InstanceListener listener = subscription.getKey();
for (EClass subscriptionType : subscription.getValue()) {
final Object superElementTypeKey = toKey(subscriptionType);
addInstanceListenerInternal(listener, subscriptionType, superElementTypeKey);
final Set<Object> subTypeKeys = metaStore.getSubTypeMap().get(superElementTypeKey);
if (subTypeKeys != null)
for (Object subTypeKey : subTypeKeys) {
addInstanceListenerInternal(listener, subscriptionType, subTypeKey);
}
}
}
}
return instanceListeners;
}
Table<Object, InstanceListener, Set<EClass>> peekInstanceListeners() {
return instanceListeners;
}
void addInstanceListenerInternal(final InstanceListener listener, EClass subscriptionType,
final Object elementTypeKey) {
Set<EClass> subscriptionTypes = instanceListeners.get(elementTypeKey, listener);
if (subscriptionTypes == null) {
subscriptionTypes = new HashSet<EClass>();
instanceListeners.put(elementTypeKey, listener, subscriptionTypes);
}
subscriptionTypes.add(subscriptionType);
}
/**
* @return the featureListeners
*/
Table<Object, FeatureListener, Set<EStructuralFeature>> getFeatureListeners() {
if (featureListeners == null) {
featureListeners = HashBasedTable.create(100, 1);
for (Entry<FeatureListener, Set<EStructuralFeature>> subscription : subscribedFeatureListeners.entrySet()) {
final FeatureListener listener = subscription.getKey();
for (EStructuralFeature subscriptionType : subscription.getValue()) {
final Object elementTypeKey = toKey(subscriptionType);
addFeatureListenerInternal(listener, subscriptionType, elementTypeKey);
}
}
}
return featureListeners;
}
void addFeatureListenerInternal(final FeatureListener listener, EStructuralFeature subscriptionType,
final Object elementTypeKey) {
Set<EStructuralFeature> subscriptionTypes = featureListeners.get(elementTypeKey, listener);
if (subscriptionTypes == null) {
subscriptionTypes = new HashSet<EStructuralFeature>();
featureListeners.put(elementTypeKey, listener, subscriptionTypes);
}
subscriptionTypes.add(subscriptionType);
}
/**
* @return the dataTypeListeners
*/
Table<Object, DataTypeListener, Set<EDataType>> getDataTypeListeners() {
if (dataTypeListeners == null) {
dataTypeListeners = HashBasedTable.create(100, 1);
for (Entry<DataTypeListener, Set<EDataType>> subscription : subscribedDataTypeListeners.entrySet()) {
final DataTypeListener listener = subscription.getKey();
for (EDataType subscriptionType : subscription.getValue()) {
final Object elementTypeKey = toKey(subscriptionType);
addDatatypeListenerInternal(listener, subscriptionType, elementTypeKey);
}
}
}
return dataTypeListeners;
}
void addDatatypeListenerInternal(final DataTypeListener listener, EDataType subscriptionType,
final Object elementTypeKey) {
Set<EDataType> subscriptionTypes = dataTypeListeners.get(elementTypeKey, listener);
if (subscriptionTypes == null) {
subscriptionTypes = new HashSet<EDataType>();
dataTypeListeners.put(elementTypeKey, listener, subscriptionTypes);
}
subscriptionTypes.add(subscriptionType);
}
public void registerObservedTypes(Set<EClass> classes, Set<EDataType> dataTypes,
Set<? extends EStructuralFeature> features) {
registerObservedTypes(classes, dataTypes, features, IndexingLevel.FULL);
}
@Override
public void registerObservedTypes(Set<EClass> classes, Set<EDataType> dataTypes,
Set<? extends EStructuralFeature> features, final IndexingLevel level) {
if (isRegistrationNecessary(level) && (classes != null || features != null || dataTypes != null)) {
final Set<Object> resolvedFeatures = resolveFeaturesToKey(features);
final Set<Object> resolvedClasses = resolveClassifiersToKey(classes);
final Set<Object> resolvedDatatypes = resolveClassifiersToKey(dataTypes);
try {
coalesceTraversals(new Callable<Void>() {
@Override
public Void call() throws Exception {
Function<Object, IndexingLevel> f = new Function<Object, IndexingLevel>() {
@Override
public IndexingLevel apply(Object input) {
return level;
}
};
delayedFeatures.putAll(Maps.asMap(resolvedFeatures, f));
delayedDataTypes.putAll(Maps.asMap(resolvedDatatypes, f));
delayedClasses.putAll(Maps.asMap(resolvedClasses, f));
return null;
}
});
} catch (InvocationTargetException ex) {
processingFatal(ex.getCause(), "register en masse the observed EClasses " + resolvedClasses
+ " and EDatatypes " + resolvedDatatypes + " and EStructuralFeatures " + resolvedFeatures);
} catch (Exception ex) {
processingFatal(ex, "register en masse the observed EClasses " + resolvedClasses + " and EDatatypes "
+ resolvedDatatypes + " and EStructuralFeatures " + resolvedFeatures);
}
}
}
@Override
public void unregisterObservedTypes(Set<EClass> classes, Set<EDataType> dataTypes,
Set<? extends EStructuralFeature> features) {
unregisterEClasses(classes);
unregisterEDataTypes(dataTypes);
unregisterEStructuralFeatures(features);
}
@Override
public void registerEStructuralFeatures(Set<? extends EStructuralFeature> features, final IndexingLevel level) {
if (isRegistrationNecessary(level) && features != null) {
final Set<Object> resolved = resolveFeaturesToKey(features);
try {
coalesceTraversals(new Callable<Void>() {
@Override
public Void call() throws Exception {
for (Object o : resolved) {
delayedFeatures.put(o, level);
}
return null;
}
});
} catch (InvocationTargetException ex) {
processingFatal(ex.getCause(), "register the observed EStructuralFeatures: " + resolved);
} catch (Exception ex) {
processingFatal(ex, "register the observed EStructuralFeatures: " + resolved);
}
}
}
@Override
public void registerEStructuralFeatures(Set<? extends EStructuralFeature> features) {
registerEStructuralFeatures(features, IndexingLevel.FULL);
}
@Override
public void unregisterEStructuralFeatures(Set<? extends EStructuralFeature> features) {
if (isRegistrationNecessary(IndexingLevel.FULL) && features != null) {
final Set<Object> resolved = resolveFeaturesToKey(features);
ensureNoListeners(resolved, getFeatureListeners());
observedFeatures.keySet().removeAll(resolved);
delayedFeatures.keySet().removeAll(resolved);
for (Object f : resolved) {
instanceStore.getValueToFeatureToHolderMap().column(f).clear();
if (instanceStore.peekFeatureToHolderMap() != null) {
instanceStore.peekFeatureToHolderMap().remove(f);
}
if (instanceStore.peekHolderToFeatureToValueMap() != null) {
instanceStore.peekHolderToFeatureToValueMap().column(f).clear();
}
statsStore.removeType(f);
}
}
}
@Override
public void registerEClasses(Set<EClass> classes, final IndexingLevel level) {
if (isRegistrationNecessary(level) && classes != null) {
final Set<Object> resolvedClasses = resolveClassifiersToKey(classes);
try {
coalesceTraversals(new Callable<Void>() {
@Override
public Void call() throws Exception {
for (Object o : resolvedClasses) {
delayedClasses.put(o, level);
}
return null;
}
});
} catch (InvocationTargetException ex) {
processingFatal(ex.getCause(), "register the observed EClasses: " + resolvedClasses);
} catch (Exception ex) {
processingFatal(ex, "register the observed EClasses: " + resolvedClasses);
}
}
}
@Override
public void registerEClasses(Set<EClass> classes) {
registerEClasses(classes, IndexingLevel.FULL);
}
/**
* @param classes
*/
protected void startObservingClasses(Map<Object, IndexingLevel> classObservations) {
for (Entry<Object, IndexingLevel> classObservation : classObservations.entrySet()) {
putIntoMapIfHigherLevel(directlyObservedClasses, classObservation.getKey(), classObservation.getValue());
addObservedClassesInternal(classObservation.getKey(), classObservation.getValue());
}
}
@Override
public void unregisterEClasses(Set<EClass> classes) {
if (isRegistrationNecessary(IndexingLevel.FULL) && classes != null) {
final Set<Object> resolved = resolveClassifiersToKey(classes);
ensureNoListeners(resolved, getInstanceListeners());
directlyObservedClasses.keySet().removeAll(resolved);
allObservedClasses = null;
delayedClasses.keySet().removeAll(resolved);
for (Object c : resolved) {
instanceStore.removeInstanceSet(c);
statsStore.removeType(c);
}
}
}
@Override
public void registerEDataTypes(Set<EDataType> dataTypes, final IndexingLevel level) {
if (isRegistrationNecessary(level) && dataTypes != null) {
final Set<Object> resolved = resolveClassifiersToKey(dataTypes);
try {
coalesceTraversals(new Callable<Void>() {
@Override
public Void call() throws Exception {
for (Object o : resolved) {
delayedDataTypes.put(o, level);
}
return null;
}
});
} catch (InvocationTargetException ex) {
processingFatal(ex.getCause(), "register the observed EDataTypes: " + resolved);
} catch (Exception ex) {
processingFatal(ex, "register the observed EDataTypes: " + resolved);
}
}
}
@Override
public void registerEDataTypes(Set<EDataType> dataTypes) {
registerEDataTypes(dataTypes, IndexingLevel.FULL);
}
@Override
public void unregisterEDataTypes(Set<EDataType> dataTypes) {
if (isRegistrationNecessary(IndexingLevel.FULL) && dataTypes != null) {
final Set<Object> resolved = resolveClassifiersToKey(dataTypes);
ensureNoListeners(resolved, getDataTypeListeners());
observedDataTypes.keySet().removeAll(resolved);
delayedDataTypes.keySet().removeAll(resolved);
for (Object dataType : resolved) {
instanceStore.removeDataTypeMap(dataType);
statsStore.removeType(dataType);
}
}
}
@Override
public boolean isCoalescing() {
return delayTraversals;
}
@Override
public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException {
V finalResult = null;
if (delayTraversals) { // reentrant case, no special action needed
try {
finalResult = callable.call();
} catch (Exception e) {
throw new InvocationTargetException(e);
}
return finalResult;
}
boolean firstRun = true;
while (callable != null) { // repeat if post-processing needed
delayedClasses = new HashMap<Object, IndexingLevel>();
delayedFeatures = new HashMap<Object, IndexingLevel>();
delayedDataTypes = new HashMap<Object, IndexingLevel>();
try {
try {
delayTraversals = true;
V result = callable.call();
if (firstRun) {
firstRun = false;
finalResult = result;
}
// are there proxies left to be resolved? are we allowed to resolve them now?
while ((!delayedProxyResolutions.isEmpty()) && resolutionDelayingResources.isEmpty()) {
// pop first entry
final Collection<Entry<EObject, EReference>> entries = delayedProxyResolutions.entries();
final Entry<EObject, EReference> toResolve = entries.iterator().next();
entries.remove(toResolve);
// see if we can resolve proxies
comprehension.tryResolveReference(toResolve.getKey(), toResolve.getValue());
}
} finally {
delayTraversals = false;
callable = null;
// This set collects all types whose indexing level increases from statistics to full
Set<Object> moveFromStatsToFullRequests = new HashSet<>();
for(Entry<Object, IndexingLevel> entry: observedFeatures.entrySet()){
IndexingLevel requested = delayedFeatures.get(entry.getKey());
if (requested != null){
IndexingLevel old = entry.getValue();
IndexingLevel merged = requested.merge(old);
if (merged == old){
delayedFeatures.remove(entry.getKey());
} else {
delayedFeatures.put(entry.getKey(), merged);
}
if (merged.hasInstances() && old.hasStatistics() && !old.hasInstances()) {
moveFromStatsToFullRequests.add(entry.getKey());
}
}
}
for(Entry<Object, IndexingLevel> entry: directlyObservedClasses.entrySet()){
IndexingLevel requested = delayedClasses.get(entry.getKey());
if (requested != null){
IndexingLevel old = entry.getValue();
IndexingLevel merged = requested.merge(old);
if (merged == old){
delayedClasses.remove(entry.getKey());
} else{
delayedClasses.put(entry.getKey(), merged);
}
if (merged.hasInstances() && old.hasStatistics() && !old.hasInstances()) {
moveFromStatsToFullRequests.add(entry.getKey());
}
}
}
for(Entry<Object, IndexingLevel> entry: observedDataTypes.entrySet()){
IndexingLevel requested = delayedDataTypes.get(entry.getKey());
if (requested != null){
IndexingLevel old = entry.getValue();
IndexingLevel merged = requested.merge(old);
if (merged == old){
delayedDataTypes.remove(entry.getKey());
} else {
delayedDataTypes.put(entry.getKey(), merged);
}
if (merged.hasInstances() && old.hasStatistics() && !old.hasInstances()) {
moveFromStatsToFullRequests.add(entry.getKey());
}
}
}
boolean classesWarrantTraversal = !Maps
.difference(delayedClasses, getAllObservedClassesInternal()).areEqual();
if (!delayedClasses.isEmpty() || !delayedFeatures.isEmpty() || !delayedDataTypes.isEmpty()) {
final HashMap<Object, IndexingLevel> oldClasses = new HashMap<Object, IndexingLevel>(
directlyObservedClasses);
startObservingClasses(delayedClasses);
observedDataTypes.putAll(delayedDataTypes);
observedFeatures.putAll(delayedFeatures);
// make copies so that original accumulators can be cleaned for the next cycle
// or for the rare case that a coalesced traversal is invoked during visitation,
// e.g. by a derived feature implementation
final Map<Object, IndexingLevel> toGatherClasses = new HashMap<Object, IndexingLevel>(
delayedClasses);
final Map<Object, IndexingLevel> toGatherFeatures = new HashMap<Object, IndexingLevel>(
delayedFeatures);
final Map<Object, IndexingLevel> toGatherDataTypes = new HashMap<Object, IndexingLevel>(
delayedDataTypes);
// Instance indexing would add extra entries to the statistics store, so we have to clean the
// appropriate entries. At this point we simply empty the stats store for elements where
// indexing level is increased from statistics to full. See bug
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=518356 for more details.
// Technically, the statsStore cleanup seems only necessary for EDataTypes; otherwise everything
// works as expected, but it seems a better idea to do the cleanup for all types in the same way
for (Object type : moveFromStatsToFullRequests) {
statsStore.removeType(type);
}
if (classesWarrantTraversal || !toGatherFeatures.isEmpty() || !toGatherDataTypes.isEmpty()) {
// repeat the cycle with this visit
final NavigationHelperVisitor visitor = new NavigationHelperVisitor.TraversingVisitor(this,
toGatherFeatures, toGatherClasses, oldClasses, toGatherDataTypes);
callable = new Callable<V>() {
@Override
public V call() throws Exception {
// temporarily ignoring RESOLVE on these features, as they were not observed before
ignoreResolveNotificationFeatures.addAll(toGatherFeatures.keySet());
try {
traverse(visitor);
} finally {
ignoreResolveNotificationFeatures.removeAll(toGatherFeatures.keySet());
}
return null;
}
};
}
}
}
} catch (Exception e) {
notifyFatalListener(
"VIATRA Base encountered an error while traversing the EMF model to gather new information. ",
e);
throw new InvocationTargetException(e);
}
}
executeTraversalCallbacks();
return finalResult;
}
private void executeTraversalCallbacks() throws InvocationTargetException{
final Runnable[] callbacks = traversalCallbacks.toArray(new Runnable[traversalCallbacks.size()]);
traversalCallbacks.clear();
if (callbacks.length > 0){
coalesceTraversals(new Callable<Void>() {
@Override
public Void call() throws Exception {
for(Runnable callback : callbacks){
callback.run();
}
return null;
}
});
}
}
private void traverse(final NavigationHelperVisitor visitor) {
// Cloning model roots avoids a concurrent modification exception
for (Notifier root : new HashSet<Notifier>(modelRoots)) {
comprehension.traverseModel(visitor, root);
}
notifyBaseIndexChangeListeners();
}
@Override
public void addRoot(Notifier emfRoot) throws ViatraBaseException {
addRootInternal(emfRoot);
}
@Override
public <T extends EObject> void cheapMoveTo(T element, EList<T> targetContainmentReferenceList) {
if (element.eAdapters().contains(contentAdapter)
&& targetContainmentReferenceList instanceof NotifyingList<?>) {
final Object listNotifier = ((NotifyingList<?>) targetContainmentReferenceList).getNotifier();
if (listNotifier instanceof Notifier && ((Notifier) listNotifier).eAdapters().contains(contentAdapter)) {
contentAdapter.ignoreInsertionAndDeletion = element;
try {
targetContainmentReferenceList.add(element);
} finally {
contentAdapter.ignoreInsertionAndDeletion = null;
}
} else {
targetContainmentReferenceList.add(element);
}
} else {
targetContainmentReferenceList.add(element);
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public void cheapMoveTo(EObject element, EObject parent, EReference containmentFeature) {
metaStore.maintainMetamodel(containmentFeature);
if (containmentFeature.isMany())
cheapMoveTo(element, (EList) parent.eGet(containmentFeature));
else if (element.eAdapters().contains(contentAdapter) && parent.eAdapters().contains(contentAdapter)) {
contentAdapter.ignoreInsertionAndDeletion = element;
try {
parent.eSet(containmentFeature, element);
} finally {
contentAdapter.ignoreInsertionAndDeletion = null;
}
} else {
parent.eSet(containmentFeature, element);
}
}
/**
* @param emfRoot
* @throws ViatraBaseException
*/
private void addRootInternal(Notifier emfRoot) throws ViatraBaseException {
if (!((emfRoot instanceof EObject) || (emfRoot instanceof Resource) || (emfRoot instanceof ResourceSet))) {
throw new ViatraBaseException(ViatraBaseException.INVALID_EMFROOT);
}
expandToAdditionalRoot(emfRoot);
}
@Override
public Set<EClass> getAllCurrentClasses() {
return instanceStore.getAllCurrentClasses();
}
private boolean isRegistrationNecessary(IndexingLevel level) {
boolean inWildcardMode = isInWildcardMode(level);
if (inWildcardMode && !loggedRegistrationMessage) {
loggedRegistrationMessage = true;
logger.warn("Type registration/unregistration not required in wildcard mode. This message will not be repeated for future occurences.");
}
return !inWildcardMode;
}
private <X, Y> void ensureNoListeners(Set<Object> unobservedTypes,
final Table<Object, X, Set<Y>> listenerRegistry) {
if (!Collections.disjoint(unobservedTypes, listenerRegistry.rowKeySet()))
throw new IllegalStateException("Cannot unregister observed types for which there are active listeners");
}
private void ensureNoListenersForDispose() {
if (!(baseIndexChangeListeners.isEmpty() && subscribedFeatureListeners.isEmpty()
&& subscribedDataTypeListeners.isEmpty() && subscribedInstanceListeners.isEmpty()))
throw new IllegalStateException("Cannot dispose while there are active listeners");
}
/**
* Resamples the values of not well-behaving derived features if those features are also indexed.
*/
public void resampleDerivedFeatures() {
// otherwise notifications are delivered anyway
if (!baseIndexOptions.isTraverseOnlyWellBehavingDerivedFeatures()) {
// get all required classes
Set<EClass> allCurrentClasses = instanceStore.getAllCurrentClasses();
Set<EStructuralFeature> featuresToSample = Sets.newHashSet();
// collect features to sample
for (EClass cls : allCurrentClasses) {
EList<EStructuralFeature> features = cls.getEAllStructuralFeatures();
for (EStructuralFeature f : features) {
// is feature only sampled?
if (comprehension.onlySamplingFeature(f)) {
featuresToSample.add(f);
}
}
}
final EMFVisitor removalVisitor = contentAdapter.getVisitorForChange(false);
final EMFVisitor insertionVisitor = contentAdapter.getVisitorForChange(true);
// iterate on instances
for (final EStructuralFeature f : featuresToSample) {
EClass containingClass = f.getEContainingClass();
processAllInstances(containingClass, new IEClassProcessor() {
@Override
public void process(EClass type, EObject instance) {
resampleFeatureValueForHolder(instance, f, insertionVisitor, removalVisitor);
}
});
}
notifyBaseIndexChangeListeners();
}
}
protected void resampleFeatureValueForHolder(EObject source, EStructuralFeature feature,
EMFVisitor insertionVisitor, EMFVisitor removalVisitor) {
// traverse features and update value
Object newValue = source.eGet(feature);
Set<Object> oldValues = instanceStore.getOldValuesForHolderAndFeature(source, feature);
if (feature.isMany()) {
resampleManyFeatureValueForHolder(source, feature, newValue, oldValues, insertionVisitor, removalVisitor);
} else {
resampleSingleFeatureValueForHolder(source, feature, newValue, oldValues, insertionVisitor, removalVisitor);
}
}
private void resampleManyFeatureValueForHolder(EObject source, EStructuralFeature feature, Object newValue,
Set<Object> oldValues, EMFVisitor insertionVisitor, EMFVisitor removalVisitor) {
InternalEObject internalEObject = (InternalEObject) source;
Collection<?> newValues = (Collection<?>) newValue;
// add those that are in new but not in old
Set<Object> newValueSet = new HashSet<Object>(newValues);
newValueSet.removeAll(oldValues);
// remove those that are in old but not in new
oldValues.removeAll(newValues);
if (!oldValues.isEmpty()) {
for (Object ov : oldValues) {
comprehension.traverseFeature(removalVisitor, source, feature, ov, null);
}
ENotificationImpl removeNotification = new ENotificationImpl(internalEObject, Notification.REMOVE_MANY,
feature, oldValues, null);
notifyLightweightObservers(source, feature, removeNotification);
}
if (!newValueSet.isEmpty()) {
for (Object nv : newValueSet) {
comprehension.traverseFeature(insertionVisitor, source, feature, nv, null);
}
ENotificationImpl addNotification = new ENotificationImpl(internalEObject, Notification.ADD_MANY, feature,
null, newValueSet);
notifyLightweightObservers(source, feature, addNotification);
}
}
private void resampleSingleFeatureValueForHolder(EObject source, EStructuralFeature feature, Object newValue,
Set<Object> oldValues, EMFVisitor insertionVisitor, EMFVisitor removalVisitor) {
InternalEObject internalEObject = (InternalEObject) source;
Object oldValue = Iterables.getFirst(oldValues, null);
if (!Objects.equal(oldValue, newValue)) {
// value changed
comprehension.traverseFeature(removalVisitor, source, feature, oldValue, null);
comprehension.traverseFeature(insertionVisitor, source, feature, newValue, null);
ENotificationImpl notification = new ENotificationImpl(internalEObject, Notification.SET, feature, oldValue,
newValue);
notifyLightweightObservers(source, feature, notification);
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.viatra.query.runtime.base.api.NavigationHelper#countAllInstances(org.eclipse.emf.ecore.EClass)
*/
@Override
public int countAllInstances(EClass type) {
int result = 0;
Object typeKey = toKey(type);
Set<Object> subTypes = metaStore.getSubTypeMap().get(typeKey);
if (subTypes != null) {
for (Object subTypeKey : subTypes) {
result += statsStore.countInstances(subTypeKey);
}
}
result += statsStore.countInstances(typeKey);
return result;
}
@Override
public int countDataTypeInstances(EDataType dataType) {
return statsStore.countInstances(toKey(dataType));
}
@Override
public int countFeatureTargets(EObject seedSource, EStructuralFeature feature) {
return instanceStore.getHolderToFeatureToValueMap().get(seedSource, toKey(feature)).size();
}
@Override
public int countFeatures(EStructuralFeature feature) {
return statsStore.countFeatures(toKey(feature));
}
@Override
public IndexingLevel getIndexingLevel(EClass type) {
Object key = toKey(type);
IndexingLevel level = directlyObservedClasses.get(key);
if (level == null) {
level = delayedClasses.get(key);
}
// Wildcard mode is never null
return wildcardMode.merge(level);
}
@Override
public IndexingLevel getIndexingLevel(EDataType type) {
Object key = toKey(type);
IndexingLevel level = observedDataTypes.get(key);
if (level == null) {
level = delayedDataTypes.get(key);
}
// Wildcard mode is never null
return wildcardMode.merge(level);
}
@Override
public IndexingLevel getIndexingLevel(EStructuralFeature feature) {
Object key = toKey(feature);
IndexingLevel level = observedFeatures.get(key);
if (level == null) {
level = delayedFeatures.get(key);
}
// Wildcard mode is never null
return wildcardMode.merge(level);
}
@Override
public void executeAfterTraversal(final Runnable traversalCallback) throws InvocationTargetException {
coalesceTraversals(new Callable<Void>() {
@Override
public Void call() throws Exception {
traversalCallbacks.add(traversalCallback);
return null;
}
});
}
}