blob: 98d9cd993d30a4ba85a9807d1a3bb19004af0c6d [file] [log] [blame]
/**
* <copyright>
* Copyright (c) 2010-2014 Henshin developers. 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
* </copyright>
*/
package org.eclipse.emf.henshin.interpreter.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Stack;
import javax.script.ScriptEngine;
import org.eclipse.emf.henshin.interpreter.ApplicationMonitor;
import org.eclipse.emf.henshin.interpreter.Assignment;
import org.eclipse.emf.henshin.interpreter.EGraph;
import org.eclipse.emf.henshin.interpreter.Engine;
import org.eclipse.emf.henshin.interpreter.InterpreterFactory;
import org.eclipse.emf.henshin.interpreter.RuleApplication;
import org.eclipse.emf.henshin.interpreter.UnitApplication;
import org.eclipse.emf.henshin.model.ConditionalUnit;
import org.eclipse.emf.henshin.model.HenshinPackage;
import org.eclipse.emf.henshin.model.IndependentUnit;
import org.eclipse.emf.henshin.model.IteratedUnit;
import org.eclipse.emf.henshin.model.LoopUnit;
import org.eclipse.emf.henshin.model.Parameter;
import org.eclipse.emf.henshin.model.ParameterKind;
import org.eclipse.emf.henshin.model.ParameterMapping;
import org.eclipse.emf.henshin.model.PriorityUnit;
import org.eclipse.emf.henshin.model.Rule;
import org.eclipse.emf.henshin.model.SequentialUnit;
import org.eclipse.emf.henshin.model.Unit;
/**
* Default {@link UnitApplication} implementation.
*
* @author Enrico Biermann, Gregor Bonifer, Christian Krause
*/
public class UnitApplicationImpl extends AbstractApplicationImpl {
// Parameter assignments:
protected Assignment assignment, resultAssignment;
// Applied and undone rules:
protected final Stack<RuleApplication> appliedRules, undoneRules;
/**
* Default constructor.
* @param engine Engine to be used.
*/
public UnitApplicationImpl(Engine engine) {
super(engine);
appliedRules = new Stack<RuleApplication>();
undoneRules = new Stack<RuleApplication>();
}
/**
* Convenience constructor.
* @param engine Engine to be used.
* @param graph Target graph.
* @param unit Unit to be used.
* @param assignment Assignment.
*/
public UnitApplicationImpl(Engine engine, EGraph graph, Unit unit, Assignment assignment) {
this(engine);
setEGraph(graph);
setUnit(unit);
setAssignment(assignment);
}
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.interpreter.UnitApplication#execute(org.eclipse.emf.henshin.interpreter.ApplicationMonitor)
*/
@Override
public boolean execute(ApplicationMonitor monitor) {
if (monitor==null) {
monitor = InterpreterFactory.INSTANCE.createApplicationMonitor();
}
appliedRules.clear();
undoneRules.clear();
resultAssignment = (assignment!=null) ?
new AssignmentImpl(assignment, true) :
new AssignmentImpl(unit, true);
return doExecute(monitor);
}
/*
* Do execute a unit. Assumes that the monitor and the result assignment is set.
*/
protected boolean doExecute(ApplicationMonitor monitor) {
if (unit.isActivated()) {
switch (unit.eClass().getClassifierID()) {
case HenshinPackage.RULE:
return executeRule(monitor);
case HenshinPackage.INDEPENDENT_UNIT:
return executeIndependentUnit(monitor);
case HenshinPackage.SEQUENTIAL_UNIT:
return executeSequentialUnit(monitor);
case HenshinPackage.CONDITIONAL_UNIT:
return executeConditionalUnit(monitor);
case HenshinPackage.PRIORITY_UNIT:
return executePriorityUnit(monitor);
case HenshinPackage.ITERATED_UNIT:
return executeIteratedUnit(monitor);
case HenshinPackage.LOOP_UNIT:
return executeLoopUnit(monitor);
default:
return false;
}
}
return true;
}
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.interpreter.UnitApplication#undo(org.eclipse.emf.henshin.interpreter.ApplicationMonitor)
*/
@Override
public boolean undo(ApplicationMonitor monitor) {
if (appliedRules.isEmpty()) {
return true;
}
if (monitor==null) {
monitor = InterpreterFactory.INSTANCE.createApplicationMonitor();
}
boolean success = true;
while (!appliedRules.isEmpty()) {
RuleApplication ruleApplication = appliedRules.pop();
if (!ruleApplication.undo(monitor)) {
success = false;
break;
}
undoneRules.push(ruleApplication);
}
monitor.notifyUndo(this, success);
return success;
}
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.interpreter.UnitApplication#redo(org.eclipse.emf.henshin.interpreter.ApplicationMonitor)
*/
@Override
public boolean redo(ApplicationMonitor monitor) {
if (undoneRules.isEmpty()) {
return true;
}
if (monitor==null) {
monitor = InterpreterFactory.INSTANCE.createApplicationMonitor();
}
boolean success = true;
while (!undoneRules.isEmpty()) {
RuleApplication ruleApplication = undoneRules.pop();
if (!ruleApplication.redo(monitor)) {
success = false;
break;
}
appliedRules.push(ruleApplication);
}
monitor.notifyRedo(this, success);
return success;
}
/*
* Execute a Rule.
*/
protected boolean executeRule(ApplicationMonitor monitor) {
Rule rule = (Rule) unit;
RuleApplication ruleApp = new RuleApplicationImpl(engine, graph, rule, resultAssignment);
if (ruleApp.execute(monitor)) {
resultAssignment = new AssignmentImpl(ruleApp.getResultMatch(), true);
appliedRules.push(ruleApp);
return true; // notification is done in the rule application
} else {
return false;
}
}
/*
* Execute an IndependentUnit.
*/
protected boolean executeIndependentUnit(ApplicationMonitor monitor) {
IndependentUnit indepUnit = (IndependentUnit) unit;
List<Unit> subUnits = new ArrayList<Unit>(indepUnit.getSubUnits());
boolean success = false;
while (!subUnits.isEmpty()) {
if (monitor.isCanceled()) {
if (monitor.isUndo()) undo(monitor);
break;
}
int index = new Random().nextInt(subUnits.size());
UnitApplicationImpl unitApp = createApplicationFor(subUnits.remove(index));
if (unitApp.execute(monitor)) {
updateParameterValues(unitApp);
appliedRules.addAll(unitApp.appliedRules);
success = true;
break;
}
}
monitor.notifyExecute(this, success);
return success;
}
/*
* Execute a SequentialUnit.
*/
protected boolean executeSequentialUnit(ApplicationMonitor monitor) {
SequentialUnit seqUnit = (SequentialUnit) unit;
boolean success = false;
for (Unit subUnit : seqUnit.getSubUnits()) {
if (monitor.isCanceled()) {
if (monitor.isUndo()) undo(monitor);
success = false;
break;
}
UnitApplicationImpl unitApp = createApplicationFor(subUnit);
if (unitApp.execute(monitor)) {
success = true;
updateParameterValues(unitApp);
appliedRules.addAll(unitApp.appliedRules);
} else {
if (seqUnit.isStrict()) {
success = false;
if (seqUnit.isRollback()) {
undo(monitor);
}
}
break;
}
}
if (seqUnit.getSubUnits().isEmpty())
success = true;
monitor.notifyExecute(this, success);
return success;
}
/*
* Execute a ConditionalUnit.
*/
protected boolean executeConditionalUnit(ApplicationMonitor monitor) {
boolean success = false;
ConditionalUnit condUnit = (ConditionalUnit) unit;
UnitApplicationImpl ifUnitApp = createApplicationFor(condUnit.getIf());
if (ifUnitApp.execute(monitor)) {
updateParameterValues(ifUnitApp);
appliedRules.addAll(ifUnitApp.appliedRules);
UnitApplicationImpl thenUnitApp = createApplicationFor(condUnit.getThen());
success = thenUnitApp.execute(monitor);
if (success) {
updateParameterValues(thenUnitApp);
}
appliedRules.addAll(thenUnitApp.appliedRules);
} else {
if (condUnit.getElse() != null) {
UnitApplicationImpl elseUnitApp = createApplicationFor(condUnit.getElse());
success = elseUnitApp.execute(monitor);
if (success) {
updateParameterValues(elseUnitApp);
}
appliedRules.addAll(elseUnitApp.appliedRules);
} else {
success = true;
}
}
if (monitor.isCanceled()) {
if (monitor.isUndo()) undo(monitor);
monitor.notifyExecute(this, false);
return false;
}
if (!success) {
undo(monitor);
}
monitor.notifyExecute(this, success);
return success;
}
/*
* Execute a PriorityUnit.
*/
protected boolean executePriorityUnit(ApplicationMonitor monitor) {
PriorityUnit priUnit = (PriorityUnit) unit;
boolean success = false;
for (Unit subUnit : priUnit.getSubUnits()) {
if (monitor.isCanceled()) {
if (monitor.isUndo()) undo(monitor);
break;
}
UnitApplicationImpl unitApp = createApplicationFor(subUnit);
if (unitApp.execute(monitor)) {
updateParameterValues(unitApp);
appliedRules.addAll(unitApp.appliedRules);
success = true;
break;
}
}
monitor.notifyExecute(this, success);
return success;
}
/*
* Execute an IteratedUnit.
*/
protected boolean executeIteratedUnit(ApplicationMonitor monitor) {
IteratedUnit iteratedUnit = (IteratedUnit) unit;
// Determine the number of iterations:
String itersString = iteratedUnit.getIterations();
if (itersString==null) {
return false;
}
itersString = itersString.trim();
int iterations = 0;
boolean ok = false;
// Constant?
try {
iterations = Integer.parseInt(itersString);
ok = true;
} catch (NumberFormatException e) {}
// Parameter?
if (!ok) {
for (Parameter param : unit.getParameters()) {
if (itersString.equals(param.getName())) {
Object v = resultAssignment.getParameterValue(param);
if (v instanceof Number) {
iterations = ((Number) v).intValue();
ok = true;
break;
} else {
return false;
}
}
}
}
// We need the script engine...
if (!ok) {
try {
ScriptEngine scriptEngine = engine.getScriptEngine();
for (Parameter param : unit.getParameters()) {
scriptEngine.put(param.getName(), resultAssignment.getParameterValue(param));
}
Object value = scriptEngine.eval(itersString);
if (value==null) {
throw new RuntimeException("Error determining number of iterations for unit '" + iteratedUnit.getName() + "'");
}
String valueString = value.toString();
int index = valueString.indexOf('.');
if (index==0) {
valueString = "0";
} else if (index>0) {
valueString = valueString.substring(0, index);
}
iterations = Integer.parseInt(valueString);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
// Now apply the subunit n times:
boolean success = false;
for (int i=0; i<iterations; i++) {
if (monitor.isCanceled()) {
if (monitor.isUndo()) undo(monitor);
success = false;
break;
}
UnitApplicationImpl unitApp = createApplicationFor(iteratedUnit.getSubUnit());
if (unitApp.execute(monitor)) {
success = true;
updateParameterValues(unitApp);
appliedRules.addAll(unitApp.appliedRules);
} else {
if (iteratedUnit.isStrict()) {
success = false;
if (iteratedUnit.isRollback()) {
undo(monitor);
}
}
break;
}
}
monitor.notifyExecute(this, success);
return success;
}
/*
* Execute a LoopUnit.
*/
protected boolean executeLoopUnit(ApplicationMonitor monitor) {
LoopUnit loopUnit = (LoopUnit) unit;
boolean success = true;
while (true) {
if (monitor.isCanceled()) {
if (monitor.isUndo()) undo(monitor);
success = false;
break;
}
UnitApplicationImpl unitApp = createApplicationFor(loopUnit.getSubUnit());
if (unitApp.execute(monitor)) {
updateParameterValues(unitApp);
appliedRules.addAll(unitApp.appliedRules);
} else {
break;
}
}
monitor.notifyExecute(this, success);
return success;
}
/*
* Create an ApplicationUnit for a given Unit.
*/
protected UnitApplicationImpl createApplicationFor(Unit subUnit) {
if (resultAssignment==null) {
resultAssignment = new AssignmentImpl(unit);
}
Assignment assign = new AssignmentImpl(subUnit);
for (ParameterMapping mapping : unit.getParameterMappings()) {
Parameter source = mapping.getSource();
Parameter target = mapping.getTarget();
if (target.getUnit()==subUnit) {
assign.setParameterValue(target, resultAssignment.getParameterValue(source));
}
}
return new UnitApplicationImpl(engine, graph, subUnit, assign);
}
/*
* Update the parameter values.
*/
protected void updateParameterValues(UnitApplicationImpl subUnitApp) {
if (resultAssignment==null) {
resultAssignment = new AssignmentImpl(unit);
}
for (ParameterMapping mapping : unit.getParameterMappings()) {
Parameter source = mapping.getSource();
Parameter target = mapping.getTarget();
if (source.getUnit()==subUnitApp.getUnit()) {
Parameter param = subUnitApp.getUnit().getParameter(source.getName());
if (param!=null) {
Object value = subUnitApp.getResultAssignment().getParameterValue(param);
if (value!=null) {
resultAssignment.setParameterValue(target, value);
}
}
}
}
}
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.interpreter.UnitApplication#getAssignment()
*/
@Override
public Assignment getAssignment() {
return assignment;
}
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.interpreter.UnitApplication#setAssignment(org.eclipse.emf.henshin.interpreter.Assignment)
*/
@Override
public void setAssignment(Assignment assignment) {
this.assignment = assignment;
}
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.interpreter.UnitApplication#getResultAssignment()
*/
@Override
public Assignment getResultAssignment() {
return resultAssignment;
}
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.interpreter.UnitApplication#getParameterValue(java.lang.String)
*/
@Override
public Object getResultParameterValue(String paramName) {
if (unit==null) {
throw new RuntimeException("Transformation unit not set");
}
Parameter param = unit.getParameter(paramName);
if (param==null) {
throw new RuntimeException("No parameter \"" + paramName + "\" in transformation unit \"" + unit.getName() + "\" found" );
}
if (resultAssignment!=null) {
return resultAssignment.getParameterValue(param);
}
return null;
}
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.interpreter.UnitApplication#setParameterValue(java.lang.String, java.lang.Object)
*/
@Override
public void setParameterValue(String paramName, Object value) {
if (unit==null) {
throw new RuntimeException("Unit not set");
}
Parameter param = unit.getParameter(paramName);
if (param==null) {
throw new RuntimeException("No parameter \"" + paramName + "\" in unit \"" + unit.getName() + "\" found" );
}
ParameterKind paramKind = param.getKind();
if (paramKind == ParameterKind.OUT || paramKind == ParameterKind.VAR) {
throw new RuntimeException(paramKind.getAlias() + " parameter \"" + paramName + "\" may not be set before the execution of u \"" + unit.getName() + "\"");
}
if (assignment==null) {
assignment = new AssignmentImpl(unit);
}
assignment.setParameterValue(param, value);
}
/**
* Get the applied rules of this unit application.
* @return List of applied rules.
*/
public List<RuleApplication> getAppliedRules() {
return new ArrayList<RuleApplication>(appliedRules);
}
/**
* Get the undone rules of this unit application.
* @return List of undone rules.
*/
public List<RuleApplication> getUndoneRules() {
return new ArrayList<RuleApplication>(undoneRules);
}
}