/*******************************************************************************
* Copyright (c) 2010-2012, Tamas Szabo, Abel Hegedus, 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, Abel Hegedus - initial API and implementation
*******************************************************************************/
package org.eclipse.incquery.runtime.triggerengine.api;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.incquery.runtime.api.IMatcherFactory;
import org.eclipse.incquery.runtime.api.IPatternMatch;
import org.eclipse.incquery.runtime.api.IncQueryEngine;
import org.eclipse.incquery.runtime.api.IncQueryMatcher;
import org.eclipse.incquery.runtime.base.api.NavigationHelper;
import org.eclipse.incquery.runtime.exception.IncQueryException;
import org.eclipse.incquery.runtime.triggerengine.firing.AutomaticFiringStrategy;
import org.eclipse.incquery.runtime.triggerengine.firing.IQBaseCallbackUpdateCompleteProvider;
import org.eclipse.incquery.runtime.triggerengine.firing.IUpdateCompleteListener;
import org.eclipse.incquery.runtime.triggerengine.firing.IUpdateCompleteProvider;
import org.eclipse.incquery.runtime.triggerengine.firing.TimedFiringStrategy;
import org.eclipse.incquery.runtime.triggerengine.firing.TransactionUpdateCompleteProvider;
import org.eclipse.incquery.runtime.triggerengine.notification.ActivationNotificationProvider;
import org.eclipse.incquery.runtime.triggerengine.notification.IActivationNotificationListener;
/**
* An Agenda is associated to each EMF instance model (more precisely {@link IncQueryEngine} or equivalently
* {@link Notifier} in the context of EMF-IncQuery) and it is responsible for creating, managing and disposing rules in
* the AbstractRule Engine. It provides an unmodifiable view for the collection of applicable activations.
*
* <p>
* One must register an {@link IActivationNotificationListener} in order to receive notifications automatically about
* the changes in the collection of activations.
*
* <p>
* The Trigger Engine is a collection of strategies which can be used to fire these activations with pre-defined
* timings. Such strategies include the {@link AutomaticFiringStrategy} and {@link TimedFiringStrategy} at the current
* state of development.
*
* <p>
* Note that, one may instantiate an {@link ActivationMonitor} in order to process the activations on an individual
* basis, because the {@link Agenda} always reflects the most up-to-date state of the activations.
*
* <p>
* One may define whether multiple firing of the same activation is allowed; that is, only the Appeared state will be
* used from the lifecycle of {@link Activation}s and consecutive firing of a previously applied {@link Activation} is
* possible. For more information on the lifecycle see {@link Activation}. Multiple firing is used for example in Design
* Space Exploration scenarios.
*
* @author Tamas Szabo
*
*/
public class Agenda implements IAgenda {
private final IncQueryEngine iqEngine;
private final Set<IRule<? extends IPatternMatch>> rules;
private final Set<ActivationMonitor> monitors;
private final Notifier notifier;
private final TransactionalEditingDomain editingDomain;
private final boolean allowMultipleFiring;
private IRuleFactory ruleFactory;
private final Collection<Activation<? extends IPatternMatch>> activations;
private IUpdateCompleteProvider updateCompleteProvider;
private final IActivationNotificationListener activationListener;
private final ActivationNotificationProvider activationProvider;
/**
* Instantiates a new Agenda instance with the given {@link IncQueryEngine}. Multiple firing of the same activation
* is not allowed.
*
* @param iqEngine
* the {@link IncQueryEngine} instance
*/
protected Agenda(IncQueryEngine iqEngine) {
this(iqEngine, false);
}
/**
* Instantiates a new Agenda instance with the given {@link IncQueryEngine} and sets whether multiple allowing is
* allowed.
*
* @param iqEngine
* the {@link IncQueryEngine} instance
* @param allowMultipleFiring
* indicates whether multiple firing is allowed
*/
protected Agenda(IncQueryEngine iqEngine, boolean allowMultipleFiring) {
this.iqEngine = iqEngine;
this.rules = new HashSet<IRule<? extends IPatternMatch>>();
this.monitors = new HashSet<ActivationMonitor>();
this.notifier = iqEngine.getEmfRoot();
this.editingDomain = TransactionUtil.getEditingDomain(notifier);
this.allowMultipleFiring = allowMultipleFiring;
this.activations = new HashSet<Activation<? extends IPatternMatch>>();
this.activationProvider = new ActivationNotificationProvider() {
@Override
protected void listenerAdded(IActivationNotificationListener listener, boolean fireNow) {
if (fireNow) {
for (Activation<? extends IPatternMatch> activation : activations) {
listener.activationAppeared(activation);
}
}
}
};
this.activationListener = new IActivationNotificationListener() {
@Override
public void activationDisappeared(Activation<? extends IPatternMatch> activation) {
activations.remove(activation);
for (ActivationMonitor monitor : monitors) {
monitor.removeActivation(activation);
}
activationProvider.notifyActivationDisappearance(activation);
}
@Override
public void activationAppeared(Activation<? extends IPatternMatch> activation) {
activations.add(activation);
for (ActivationMonitor monitor : monitors) {
monitor.addActivation(activation);
}
activationProvider.notifyActivationAppearance(activation);
}
};
if (this.editingDomain != null) {
updateCompleteProvider = new TransactionUpdateCompleteProvider(editingDomain);
} else {
NavigationHelper helper;
try {
helper = iqEngine.getBaseIndex();
updateCompleteProvider = new IQBaseCallbackUpdateCompleteProvider(helper);
} catch (IncQueryException e) {
getLogger().error("The base index cannot be constructed for the engine!", e);
}
}
}
/**
* Sets the {@link IRuleFactory} for the Agenda.
*
* @param ruleFactory
* the {@link IRuleFactory} instance
*/
public void setRuleFactory(IRuleFactory ruleFactory) {
this.ruleFactory = ruleFactory;
}
@Override
public Notifier getNotifier() {
return notifier;
}
@Override
public TransactionalEditingDomain getEditingDomain() {
return editingDomain;
}
@Override
public boolean isAllowMultipleFiring() {
return allowMultipleFiring;
}
@Override
public <Match extends IPatternMatch, Matcher extends IncQueryMatcher<Match>> IRule<Match> createRule(
IMatcherFactory<Matcher> factory) {
return createRule(factory, false, false);
}
@Override
public <Match extends IPatternMatch, Matcher extends IncQueryMatcher<Match>> IRule<Match> createRule(
IMatcherFactory<Matcher> factory, boolean upgradedStateUsed, boolean disappearedStateUsed) {
AbstractRule<Match> rule = ruleFactory.createRule(iqEngine, factory, upgradedStateUsed, disappearedStateUsed);
rule.addActivationNotificationListener(activationListener, true);
rules.add(rule);
return rule;
}
@Override
public <MatchType extends IPatternMatch> void removeRule(AbstractRule<MatchType> rule) {
if (rules.contains(rule)) {
rule.removeActivationNotificationListener(activationListener);
rule.dispose();
rules.remove(rule);
}
}
@Override
public Collection<IRule<? extends IPatternMatch>> getRules() {
return rules;
}
@Override
public void dispose() {
if (updateCompleteProvider != null) {
updateCompleteProvider.dispose();
}
for (IRule<? extends IPatternMatch> rule : rules) {
rule.dispose();
}
}
@Override
public Logger getLogger() {
return iqEngine.getLogger();
}
@Override
public Collection<Activation<? extends IPatternMatch>> getActivations() {
return Collections.unmodifiableCollection(activations);
}
/**
* @return the updateCompleteProvider
*/
public IUpdateCompleteProvider getUpdateCompleteProvider() {
return updateCompleteProvider;
}
/**
* @param updateCompleteProvider
* the updateCompleteProvider to set
*/
public void setUpdateCompleteProvider(IUpdateCompleteProvider updateCompleteProvider) {
this.updateCompleteProvider = updateCompleteProvider;
}
@Override
public ActivationMonitor newActivationMonitor(boolean fillAtStart) {
ActivationMonitor monitor = new ActivationMonitor();
if (fillAtStart) {
for (Activation<? extends IPatternMatch> activation : activations) {
monitor.addActivation(activation);
}
}
monitors.add(monitor);
return monitor;
}
@Override
public boolean addUpdateCompleteListener(IUpdateCompleteListener listener, boolean fireNow) {
if (updateCompleteProvider != null) {
return updateCompleteProvider.addUpdateCompleteListener(listener, fireNow);
} else {
return false;
}
}
@Override
public boolean removeUpdateCompleteListener(IUpdateCompleteListener listener) {
if (updateCompleteProvider != null) {
return updateCompleteProvider.removeUpdateCompleteListener(listener);
} else {
return false;
}
}
@Override
public boolean addActivationNotificationListener(IActivationNotificationListener listener, boolean fireNow) {
return activationProvider.addActivationNotificationListener(listener, fireNow);
}
@Override
public boolean removeActivationNotificationListener(IActivationNotificationListener listener) {
return activationProvider.removeActivationNotificationListener(listener);
}
}