/*******************************************************************************
* Copyright (c) 2004-2011 Abel Hegedus 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:
* Abel Hegedus - initial API and implementation
*******************************************************************************/
package org.eclipse.incquery.querybasedfeatures.runtime;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.incquery.runtime.api.IMatcherFactory;
import org.eclipse.incquery.runtime.api.IncQueryEngine;
import org.eclipse.incquery.runtime.api.IncQueryMatcher;
import org.eclipse.incquery.runtime.exception.IncQueryException;
import org.eclipse.incquery.runtime.extensibility.MatcherFactoryRegistry;
/**
* Utility class for instantiating query-based feature handlers ({@link IQueryBasedFeatureHandler}).
*
* @author Abel Hegedus
*/
public final class QueryBasedFeatureHelper {
/**
* Weak hash map for keeping the created
*/
private static final Map<Notifier, Map<EStructuralFeature, WeakReference<IQueryBasedFeatureHandler>>> FEATURE_MAP = new WeakHashMap<Notifier, Map<EStructuralFeature, WeakReference<IQueryBasedFeatureHandler>>>();
/**
* Constructor hidden for static utility class
*/
private QueryBasedFeatureHelper() {
}
/**
* Decide what {@link Notifier} to use as the scope of the {@link IncQueryMatcher} underlying the created
* {@link IQueryBasedFeatureHandler}.
*
* <p>
* Optimally, the {@link ResourceSet} is reachable and most other matchers will use it as well.
*
* <p>
* Otherwise, the {@link Resource} is used if the model is not inside a resource set.
*
* <p>
* If none of the above are reachable, the container hierarchy is traversed for a top element.
*
* <p>
* Finally, the source itself is returned.
*
* @param source
* the source object that initializes the handler
* @return the topmost reachable Notifier from the source
*/
private static Notifier prepareNotifierForSource(EObject source) {
if (source != null) {
Resource eResource = source.eResource();
if (eResource != null) {
ResourceSet resourceSet = eResource.getResourceSet();
if (resourceSet != null) {
return resourceSet;
} else {
return eResource;
}
} else {
EObject top = source;
while (top.eContainer() != null) {
top = top.eContainer();
}
return prepareNotifierForSource(top);
}
}
return source;
}
/**
* Returns the {@link IQueryBasedFeatureHandler} for the given {@link EStructuralFeature} in the given
* {@link Notifier}. If the handler does not exist yet, it is also initialized, before being returned.
*
* <p>
* The required matcher is initialized using the pattern fully qualified name passed as a parameter.
*
* @param notifier
* the exact notifier to use for the handler initialization
* @param feature
* the feature that is managed by the handler
* @param patternFQN
* the fully qualified name of the pattern used by the handler
* @param sourceParamName
* the name of the parameter in the pattern that represents the source end of the feature
* @param targetParamName
* the name of the parameter in the pattern that represents the target end of the feature
* @param kind
* the {@link QueryBasedFeatureKind} that is used by the handler
* @param keepCache
* specifies whether the handler uses an internal cache for feature values. Only possible with single and
* many reference kinds
* @return the query-based feature handler that manages the feature values
*/
public static IQueryBasedFeatureHandler getQueryBasedFeatureHandler(Notifier notifier, EStructuralFeature feature,
String patternFQN, String sourceParamName, String targetParamName, QueryBasedFeatureKind kind,
boolean keepCache) {
Map<EStructuralFeature, WeakReference<IQueryBasedFeatureHandler>> features = FEATURE_MAP.get(notifier);
if (features == null) {
features = new HashMap<EStructuralFeature, WeakReference<IQueryBasedFeatureHandler>>();
FEATURE_MAP.put(notifier, features);
}
WeakReference<IQueryBasedFeatureHandler> weakReference = features.get(feature);
IQueryBasedFeatureHandler derivedFeature = weakReference == null ? null : weakReference.get();
if (derivedFeature != null) {
return derivedFeature;
}
QueryBasedFeatureHandler newDerivedFeature = new QueryBasedFeatureHandler(feature, kind, keepCache);
features.put(feature, new WeakReference<IQueryBasedFeatureHandler>(newDerivedFeature));
IMatcherFactory<?> matcherFactory = MatcherFactoryRegistry.getMatcherFactory(patternFQN);
if (matcherFactory != null) {
try {
IncQueryMatcher<?> matcher = matcherFactory.getMatcher(notifier);
newDerivedFeature.initialize(matcher, sourceParamName, targetParamName);
newDerivedFeature.startMonitoring();
} catch (IncQueryException e) {
IncQueryEngine.getDefaultLogger().error("Handler initialization failed", e);
return null;
}
} else {
IncQueryEngine
.getDefaultLogger()
.error("Handler initialization failed, matcher factory is null. Make sure to include your EMF-IncQuery project with the query definitions in the configuration.");
}
return newDerivedFeature;
}
/**
* Returns the {@link IQueryBasedFeatureHandler} for the given {@link EStructuralFeature} in the given
* {@link Notifier}. If the handler does not exist yet, it is also initialized, before being returned.
*
* <p>
* The required matcher is initialized using the pattern fully qualified name passed as a parameter.
*
* <p>
* Calls
* {@link #getQueryBasedFeatureHandler(Notifier, EStructuralFeature, String, String, String, QueryBasedFeatureKind, boolean)}
* with keepCache = true.
*
* @param notifier
* the exact notifier to use for the handler initialization
* @param feature
* the feature that is managed by the handler
* @param patternFQN
* the fully qualified name of the pattern used by the handler
* @param sourceParamName
* the name of the parameter in the pattern that represents the source end of the feature
* @param targetParamName
* the name of the parameter in the pattern that represents the target end of the feature
* @param kind
* the {@link QueryBasedFeatureKind} that is used by the handler
* @return the query-based feature handler that manages the feature values
*/
public static IQueryBasedFeatureHandler getQueryBasedFeatureHandlerOnNotifier(Notifier notifier,
EStructuralFeature feature, String patternFQN, String sourceParamName, String targetParamName,
QueryBasedFeatureKind kind) {
return getQueryBasedFeatureHandler(notifier, feature, patternFQN, sourceParamName, targetParamName, kind, true);
}
/**
* Returns the {@link IQueryBasedFeatureHandler} for the given {@link EStructuralFeature} on the source or the
* topmost {@link Notifier} reachable from the source. If the handler does not exist yet, it is also initialized,
* before being returned.
*
* <p>
* The required matcher is initialized using the pattern fully qualified name passed as a parameter.
*
* <p>
* Calls
* {@link #getQueryBasedFeatureHandler(Notifier, EStructuralFeature, String, String, String, QueryBasedFeatureKind, boolean)}.
*
* @param source
* the source object used for the handler initialization (used for determining the notifier for the
* underlying matcher)
* @param feature
* the feature that is managed by the handler
* @param patternFQN
* the fully qualified name of the pattern used by the handler
* @param sourceParamName
* the name of the parameter in the pattern that represents the source end of the feature
* @param targetParamName
* the name of the parameter in the pattern that represents the target end of the feature
* @param kind
* the {@link QueryBasedFeatureKind} that is used by the handler
* @param keepCache
* specifies whether the handler uses an internal cache for feature values. Only possible with single and
* many reference kinds
* @param useSourceAsNotifier
* if true, the source is used as the notifier for the matcher initialization
* @return the query-based feature handler that manages the feature values
*/
public static IQueryBasedFeatureHandler getQueryBasedFeatureHandler(EObject source, EStructuralFeature feature,
String patternFQN, String sourceParamName, String targetParamName, QueryBasedFeatureKind kind,
boolean keepCache, boolean useSourceAsNotifier) {
Notifier notifier = source;
if (!useSourceAsNotifier) {
notifier = prepareNotifierForSource(source);
}
return getQueryBasedFeatureHandler(notifier, feature, patternFQN, sourceParamName, targetParamName, kind,
keepCache);
}
/**
* Returns the {@link IQueryBasedFeatureHandler} for the given {@link EStructuralFeature} on the topmost
* {@link Notifier} reachable from the source. If the handler does not exist yet, it is also initialized, before
* being returned.
*
* <p>
* The required matcher is initialized using the pattern fully qualified name passed as a parameter.
*
* <p>
* Calls
* {@link #getQueryBasedFeatureHandler(EObject, EStructuralFeature, String, String, String, QueryBasedFeatureKind, boolean, boolean)}.
*
* @param source
* the source object used for the handler initialization (used for determining the notifier for the
* underlying matcher)
* @param feature
* the feature that is managed by the handler
* @param patternFQN
* the fully qualified name of the pattern used by the handler
* @param sourceParamName
* the name of the parameter in the pattern that represents the source end of the feature
* @param targetParamName
* the name of the parameter in the pattern that represents the target end of the feature
* @param kind
* the {@link QueryBasedFeatureKind} that is used by the handler
* @return the query-based feature handler that manages the feature values
*/
public static IQueryBasedFeatureHandler getQueryBasedFeatureHandler(EObject source, EStructuralFeature feature,
String patternFQN, String sourceParamName, String targetParamName, QueryBasedFeatureKind kind) {
return getQueryBasedFeatureHandler(source, feature, patternFQN, sourceParamName, targetParamName, kind, true,
false);
}
}