blob: 75849d29b0feb627bbfa0ad08bc305bcca1bf22c [file] [log] [blame]
package org.eclipse.emf.henshin.interpreter.matching.conditions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Observer;
import java.util.StringJoiner;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IBreakpointManager;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.henshin.HenshinModelPlugin;
import org.eclipse.emf.henshin.diagram.providers.HenshinMarkerNavigationProvider;
import org.eclipse.emf.henshin.interpreter.EGraph;
import org.eclipse.emf.henshin.interpreter.debug.DebugValueEObject;
import org.eclipse.emf.henshin.interpreter.debug.DebugValueList;
import org.eclipse.emf.henshin.interpreter.debug.DebugValueObject;
import org.eclipse.emf.henshin.interpreter.debug.HenshinDebugTarget;
import org.eclipse.emf.henshin.interpreter.debug.HenshinDebugThread;
import org.eclipse.emf.henshin.interpreter.debug.HenshinDebugValue;
import org.eclipse.emf.henshin.interpreter.debug.HenshinDebugVariable;
import org.eclipse.emf.henshin.interpreter.debug.HenshinStackFrame;
import org.eclipse.emf.henshin.interpreter.info.RuleInfo;
import org.eclipse.emf.henshin.interpreter.matching.constraints.AttributeConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.BinaryConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.ContainmentConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.DanglingConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.DomainSlot;
import org.eclipse.emf.henshin.interpreter.matching.constraints.PathConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.ReferenceConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.Solution;
import org.eclipse.emf.henshin.interpreter.matching.constraints.UnaryConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.Variable;
import org.eclipse.emf.henshin.model.Node;
public class DebugApplicationCondition extends ApplicationCondition {
/**
* The Debug Levels from high to low (Variable > Value > Constraint Type > Constraint)
* ("step into" tries to go down a level, "step return" tries to go up a level,
* "step over" tries to stay on the same level)
*/
public enum DebugLevel {
NONE, VARIABLE, VALUE, CONSTRAINT_TYPE, CONSTRAINT;
@Override
public String toString() {
return name();
}
}
/**
* A debug code to signal different results in the stepping methods.<br>
* TERMINATED_TRUE - match found successfully.<br>
* TERMINATED_FALSE - no match found.<br>
*
*/
public enum DebugState {
RUNNING, STEPPING, SUSPENDED, TERMINATED_TRUE, TERMINATED_FALSE
}
/**
* The Constraint Types that are checked by the DomainSlot
* (e.g. TypeConstraint, DanglingConstraint, AttributeConstraint, ...). <br>
* IMPORTANT: the order has to be the same as they are checked in
* {@link DomainSlot#instantiate(Variable, Map, EGraph)}.
*/
public enum ConstraintType {
NONE, TYPE, DANGLING, ATTRIBUTE, CONTAINMENT, REFERENCE, PATH, USER;
public ConstraintType next() {
if (isLast()) {
return this;
}
return values()[(ordinal()+1) % values().length];
}
public boolean isLast() {
return ordinal()+1 == values().length;
}
@Override
public String toString() {
// first letter uppercase
String nameString = name().substring(0, 1);
// rest lowercase, replace underscores with spaces
nameString += name().substring(1, name().length()).toLowerCase().replace("_", " ");
return nameString;
}
}
/**
* the associated debug target
*/
private HenshinDebugTarget debugTarget;
/**
* The current debug state
*/
private DebugState currentDebugState;
private DebugLevel currentDebugLevel;
private int currentVariableIndex;
private ConstraintType currentConstraintType = ConstraintType.NONE;
private int currentConstraintIndex;
private Variable currentVariable;
private DomainSlot currentSlot;
private IStackFrame[] stackFrames;
private Observer matchObserver;
// used to compare currentVariable to HenshinBreakpoints
private RuleInfo ruleInfo;
private static DebugApplicationCondition instance;
// needed for value breakpoints
private EGraph graph;
public EGraph getGraph() {
return this.graph;
}
public DebugApplicationCondition(HenshinDebugTarget debugTarget, List<Variable> variables,
Map<Variable, DomainSlot> domainMap, EGraph graph, IFormula formula, Observer matchObserver, RuleInfo ruleInfo) {
super(graph, domainMap,null); //added PerformanceMonitor as parameter null=no monitoring
this.debugTarget = debugTarget;
this.variables = variables;
this.formula = formula;
this.matchObserver = matchObserver;
this.ruleInfo = ruleInfo;
currentDebugLevel = DebugLevel.NONE;
currentVariableIndex = -1;
currentConstraintType = ConstraintType.NONE;
currentConstraintIndex = -1;
currentDebugState = DebugState.SUSPENDED;
// for value breakpoints
instance = this;
this.graph = graph;
}
// TODO: refactor, DebugApplicationCondition should not be static
public static DebugApplicationCondition getInstance() {
return instance;
}
public void initNextVariable() {
// for the first call (maybe extract to a initFirstVariable() method)
if (currentVariableIndex == -1) {
updateDebugState(DebugLevel.VARIABLE, 0, ConstraintType.NONE, -1);
if (debugTarget != null) {
debugTarget.fireCreationEvent();
}
}
// Matched all variables?
if (currentVariableIndex == variables.size()) {
// Final variable re-checks:
for (Variable variable : variables) {
if (variable.requiresFinalCheck) {
DomainSlot slot = domainMap.get(variable);
if (!slot.recheck(variable, domainMap)) {
// recheck turned out invalid --> go back
updateDebugState(DebugLevel.VARIABLE, currentVariableIndex-1, ConstraintType.NONE, -1);
tryNextValue();
/* // recheck turned out invalid --> terminate (no match found)
updateDebugState(DebugLevel.NONE, -1, ConstraintType.NONE, -1);
setCurrentDebugState(DebugState.TERMINATED_FALSE);
if (debugTarget != null) {
debugTarget.fireTerminateEvent();
}*/
return;
}
}
}
// Evaluate formula:
if (formula.eval()) {
// final evaluation was successful --> terminate (match found)
updateDebugState(DebugLevel.VARIABLE, currentVariableIndex-1, ConstraintType.NONE, -1);
setCurrentDebugState(DebugState.TERMINATED_TRUE);
// create a solution
Solution solution = new Solution(variables, domainMap, currentSlot.getConditionHandler());
// notify the observer that we have a solution
if (matchObserver != null) {
matchObserver.update(null, solution);
}
if (debugTarget != null) {
debugTarget.fireTerminateEvent();
}
return;
}
// else formula did not work, go back to last variable
updateDebugState(DebugLevel.VARIABLE, currentVariableIndex-1, ConstraintType.NONE, -1);
tryNextValue();
/* // else: final evaluation was not successful --> terminate (no match found)
updateDebugState(DebugLevel.NONE, -1, ConstraintType.NONE, -1);
setCurrentDebugState(DebugState.TERMINATED_FALSE);
if (debugTarget != null) {
debugTarget.fireTerminateEvent();
}*/
return;
}
// Otherwise select the last variable:
currentVariable = variables.get(currentVariableIndex);
currentSlot = domainMap.get(currentVariable);
// initialize the domain slot
if (!currentSlot.initialize(currentVariable, graph)) {
//this can happen and is not a problem
}
if (debugTarget != null) {
debugTarget.fireChangeEvent(DebugEvent.CONTENT);
debugTarget.fireSuspendEvent(DebugEvent.STEP_END);
}
}
/**
* uses the current constraint type & index to check the current constraint
* @return <code>true</code> if the current constraint is valid, <code>false</code> otherwise
*/
private boolean checkCurrentConstraint() {
switch (currentConstraintType) {
case TYPE:
return currentSlot.checkTypeConstraint(currentVariable);
case DANGLING:
DanglingConstraint danglingConstraint = currentVariable.danglingConstraints.get(currentConstraintIndex);
return currentSlot.checkDanglingConstraint(danglingConstraint, graph);
case ATTRIBUTE:
AttributeConstraint attributeConstraint = currentVariable.attributeConstraints.get(currentConstraintIndex);
UnaryConstraint unaryUserConstraint = currentVariable.attributeUserConstraints.get(attributeConstraint);
return currentSlot.checkAttributeConstraint(attributeConstraint, unaryUserConstraint);
case CONTAINMENT:
ContainmentConstraint containmentConstraint = currentVariable.containmentConstraints.get(currentConstraintIndex);
return currentSlot.checkContainmentConstraint(containmentConstraint, domainMap);
case REFERENCE:
ReferenceConstraint referenceConstraint = currentVariable.referenceConstraints.get(currentConstraintIndex);
BinaryConstraint binaryUserConstraint = currentVariable.binaryUserConstraints.get(referenceConstraint);
return currentSlot.checkReferenceConstraint(referenceConstraint, binaryUserConstraint, domainMap);
case PATH:
PathConstraint pathConstraint = currentVariable.pathConstraints.get(currentConstraintIndex);
return currentSlot.checkPathConstraint(pathConstraint, domainMap);
case USER:
return currentSlot.checkUserConstraint(currentVariable.userConstraints.get(currentConstraintIndex));
case NONE:
throw new IllegalStateException("trying to check the current constraint with currentConstraintType NONE");
default:
throw new IllegalStateException("currentConstraintType is \"" + currentConstraintType
+ "\", but has to be of the enum type ConstraintType");
}
}
/**
* Tries to find another constraint type that has to be checked.
* If not, tries to continue to the next variable because all constraints for this variable are valid
* @return
*/
private synchronized void tryNextConstraintType() {
//are there any constraint types (with constraints) left that we have to check?
do {
currentConstraintType = currentConstraintType.next();
if (currentTypeHasConstraints()) {
// continue with that constraint type
updateDebugState(DebugLevel.CONSTRAINT_TYPE, currentVariableIndex, currentConstraintType, -1);
return;
}
} while (!currentConstraintType.isLast());
// there are no other constraint types left => all constraints are valid => go to the next variable
updateDebugState(DebugLevel.VARIABLE, currentVariableIndex + 1, ConstraintType.NONE, -1);
initNextVariable();
}
/**
* Tries to go to the next value. Calls {@link #tryLowerIndexVariable()} if no value is left
* @return
*/
private void tryNextValue() {
// are any other values left for this variable?
currentSlot.unlock(currentVariable);
if (currentSlot.instantiationPossible()) {
// go to next value
currentSlot.setValueAndLock();
updateDebugState(DebugLevel.VALUE, currentVariableIndex, ConstraintType.NONE, -1);
if (debugTarget != null) {
debugTarget.fireSuspendEvent(DebugEvent.STEP_END);
}
} else {
tryLowerIndexVariable();
}
}
/*
* Checks if the current variable has a next value.
*/
private boolean hasNextValue() {
currentSlot.unlock(currentVariable);
return currentSlot.instantiationPossible();
}
/**
* Tries to go to the next Variable with a lower index (shallower recursion depth)
* to continue the search for a match.
* @return
*/
private void tryLowerIndexVariable() {
// clear (+ unlock) the current domain slot
currentSlot.clear(currentVariable);
// is there a lower index where a match might be found?
if (currentVariableIndex > 0) {
updateDebugState(DebugLevel.VARIABLE, currentVariableIndex - 1, ConstraintType.NONE, -1);
initNextVariable();
return;
}
// this is the "shallowest" recursion level (index 0)
// --> terminate (no match found)
updateDebugState(DebugLevel.NONE, -1, ConstraintType.NONE, -1);
setCurrentDebugState(DebugState.TERMINATED_FALSE);
if (debugTarget != null) {
debugTarget.fireTerminateEvent();
}
}
/**
* Checks if the current variable has constraints of the currently active constraint type
* @return the maximum index for the current constraint type (i.e # of constraints -1),
* or -1 if there are no constraints of this type
*/
private int maxCurrentConstraintIndex() {
switch (currentConstraintType) {
case TYPE:
// a variable always has one type constraint
return 0;
case DANGLING:
return currentVariable.danglingConstraints.size()-1;
case ATTRIBUTE:
return currentVariable.attributeConstraints.size()-1;
case CONTAINMENT:
return currentVariable.containmentConstraints.size()-1;
case REFERENCE:
return currentVariable.referenceConstraints.size()-1;
case PATH:
return currentVariable.pathConstraints.size()-1;
case USER:
return currentVariable.userConstraints.size()-1;
case NONE:
throw new IllegalStateException("trying to get number of constraints with currentConstraintType NONE");
default:
throw new IllegalStateException("currentConstraintType is \"" + currentConstraintType
+ "\", but has to be of the enum type ConstraintType");
}
}
private boolean currentTypeHasConstraints() {
return maxCurrentConstraintIndex() >= 0;
}
/**
* updates the internal debug state
*
* @param newDebugLevel the new {@link DebugLevel}
* @param newVariableIndex the new variable index (see {@link ApplicationCondition#findMatch(int)}
* @param newConstraintType the new constraint type (see {@link constraint#getClass()} or null if no constraint is active)
* @param newConstraintIndex the new iteration index for multiple constraints of the same type
* (for example: {@link Variable#attributeConstraints}), or 0 if there is only one constraint or no constraint is active
*/
private synchronized void updateDebugState(
DebugLevel newDebugLevel,
int newVariableIndex,
ConstraintType newConstraintType,
int newConstraintIndex) {
currentDebugLevel = newDebugLevel;
currentVariableIndex = newVariableIndex;
currentConstraintType = newConstraintType;
currentConstraintIndex = newConstraintIndex;
}
private void setCurrentDebugState(DebugState debugState) {
if (!isTerminated()) {
this.currentDebugState = debugState;
}
}
public boolean canResume() {
return isSuspended();
}
public boolean canSuspend() {
return !isTerminated() && !isSuspended();
}
public boolean isSuspended() {
return getCurrentDebugState() == DebugState.SUSPENDED;
}
public void resume() throws DebugException {
synchronized (this) {
if (getCurrentDebugState() == DebugState.SUSPENDED) {
setCurrentDebugState(DebugState.RUNNING);
}
// if the currentDebugState is not terminated oder suspended we continue stepping
while(!isTerminated() && getCurrentDebugState() != DebugState.SUSPENDED) {
step();
}
}
}
public void suspend() throws DebugException {
synchronized (this) {
setCurrentDebugState(DebugState.SUSPENDED);
}
}
public boolean canStep() {
return isSuspended();
}
public boolean canStepInto() {
return isSuspended();
}
public boolean canStepOver() {
return isSuspended();
}
public boolean canStepReturn() {
return isSuspended() && currentDebugLevel != DebugLevel.VARIABLE;
}
public boolean isStepping() {
return getCurrentDebugState() == DebugState.STEPPING;
}
/*
* Copy of the stepInto() method but without the static suspends after every step.
* We use this method to dive into the deeper logic without suspending the application while we do so.
*/
public void step() throws DebugException {
if (currentVariable == null) {
throw new IllegalStateException(
"currentVariable is null - variableInitNext() has to be called before");
}
if (debugTarget != null) {
debugTarget.fireResumeEvent(DebugEvent.STEP_INTO);
}
switch (currentDebugLevel) {
case VARIABLE:
// first unlock the variable because we might come from a higher index and have the slot still locked.
// This was added because otherwise, when coming from a higher index while backtracking,
// currentSlot.instantiate() would select the old slot value again instead of the next one.
if (currentSlot.getValue() != null) {
currentSlot.unlock(currentVariable);
}
// set the value
if (!currentSlot.setValueAndLock()) {
//if no furthe valuer, try lower index variable
tryLowerIndexVariable();
break;
}
// update the debug state (current index does not change)
updateDebugState(DebugLevel.VALUE, currentVariableIndex, ConstraintType.NONE, -1);
break;
case VALUE:
// go to the "type" constraint type as it is always the first constraint.
updateDebugState(DebugLevel.CONSTRAINT_TYPE, currentVariableIndex, ConstraintType.TYPE, -1);
break;
case CONSTRAINT_TYPE:
// in this state there should be a constraint of the current type left
// (this has to be ensured by the steps that lead to the "constraint type" level)
if (!currentTypeHasConstraints()) {
throw new IllegalStateException("called stepInto for constraint type \"" + currentConstraintType + "\", but this type has no constraint to step into");
}
// if there is a constraint, step into it
updateDebugState(DebugLevel.CONSTRAINT, currentVariableIndex, currentConstraintType, 0);
break;
case CONSTRAINT:
if (currentConstraintIndex > maxCurrentConstraintIndex()) {
throw new IllegalStateException("called stepOver for constraint type \"" + currentConstraintType + "\", but this type has no unchecked constraint left and should have been skipped");
}
boolean constraintIsValid = checkCurrentConstraint();
if (constraintIsValid) {
// are there any constraints of the current type left that we have to check?
if (currentConstraintIndex < maxCurrentConstraintIndex()) {
// go to the next constraint of the current type
updateDebugState(DebugLevel.CONSTRAINT, currentVariableIndex, currentConstraintType, currentConstraintIndex + 1);
break;
}
// else: see if there is a constraint type left that we have to check
tryNextConstraintType();
break;
}
if (!constraintIsValid) {
// try to go to the next value
tryNextValue();
}
break;
default:
throwExceptionInvalidDebugLevel();
}
// initially get all henshin breakpoints
ArrayList<HenshinBreakpoint> henshinBreakpoints = getHenshinBreakpoints();
// This is the node representation for the variable we're currently at.
// Compare this node with the node we received (when the user sets a HenshinBreakpoint with right click) when setting HenshinBreakpoints
Node currentNode = ruleInfo.getVariableInfo().getVariableForNode(currentVariable);
// Get relative path to node - unique representation of the node. Looks something like this: '@units.1/@lhs/@nodes.0'
String currentNodePath = EcoreUtil.getRelativeURIFragmentPath(null, currentNode);
// get the current type constraint to compare in shouldStopAtVariableBreakpoint later
String currentTypeName = currentVariable.typeConstraint.type.getName();
// handle variable breakpoints
ArrayList<VariableBreakpoint> variableBreakpoints = filterVariableBreakpoints(henshinBreakpoints);
if (variableBreakpoints.size() > 0 && currentDebugLevel == DebugLevel.VARIABLE) {
for (VariableBreakpoint variableBreakpoint : variableBreakpoints) {
// pass all three parameters necessary to clearly determine whether to suspend the application or not
if (shouldStopAtVariableBreakpoint(variableBreakpoint, currentTypeName, currentNodePath)) {
suspendApplication();
}
}
}
// handle value breakpoints
if (currentSlot.getValue() != null) {
// get complete domain (e.g. domain for :Client could be 'charles', 'bob', 'alice')
List<EObject> domain = graph.getDomain(currentSlot.getValue().eClass(), false);
//List<EObject> domain = currentSlot.getDomain();
EObject currentValue = currentSlot.getValue();
int index = domain.indexOf(currentValue);
// get the current domain
// List<EObject> currentDomain = currentSlot.getDomain();
// get all value breakpoints
ArrayList<ValueBreakpoint> valueBreakpoints = filterValueBreakpoints(henshinBreakpoints);
if (valueBreakpoints.size() > 0 && currentDebugLevel == DebugLevel.VALUE) {
for (ValueBreakpoint valueBreakpoint : valueBreakpoints) {
if (shouldStopAtValueBreakpoint(valueBreakpoint, currentValue, index)) {
suspendApplication();
}
}
}
}
// handle constraint type breakpoints
ArrayList<ConstraintTypeBreakpoint> constraintTypeBreakpoints = filterConstraintTypeBreakpoints(henshinBreakpoints);
if (constraintTypeBreakpoints.size() > 0 && currentDebugLevel == DebugLevel.CONSTRAINT_TYPE) {
for (ConstraintTypeBreakpoint constraintTypeBreakpoint : constraintTypeBreakpoints) {
if (shouldStopAtConstraintTypeBreakpoint(constraintTypeBreakpoint)) {
suspendApplication();
}
}
}
// handle constraint instance breakpoints
ArrayList<ConstraintInstanceBreakpoint> constraintInstanceBreakpoints = filterConstraintInstanceBreakpoints(henshinBreakpoints);
if (constraintInstanceBreakpoints.size() > 0 && currentDebugLevel == DebugLevel.CONSTRAINT) {
for (ConstraintInstanceBreakpoint constraintInstanceBreakpoint : constraintInstanceBreakpoints) {
if (shouldStopAtConstraintInstanceBreakpoint(constraintInstanceBreakpoint)) {
suspendApplication();
}
}
}
if (debugTarget != null) {
debugTarget.fireSuspendEvent(DebugEvent.STEP_END);
}
}
/*
* Step into
*/
public void stepInto() throws DebugException {
if (currentVariable == null) {
throw new IllegalStateException(
"currentVariable is null - variableInitNext() has to be called before");
}
if (debugTarget != null) {
debugTarget.fireResumeEvent(DebugEvent.STEP_INTO);
}
switch (currentDebugLevel) {
case VARIABLE:
// first unlock the variable because we might come from a higher index and have the slot still locked.
// This was added because otherwise, when coming from a higher index while backtracking,
// currentSlot.instantiate() would select the old slot value again instead of the next one.
if (currentSlot.getValue() != null) {
currentSlot.unlock(currentVariable);
}
// set the value
if (!currentSlot.setValueAndLock()) {
tryLowerIndexVariable();
}
else {
// update the debug state (current index does not change)
updateDebugState(DebugLevel.VALUE, currentVariableIndex, ConstraintType.NONE, -1);
setCurrentDebugState(DebugState.SUSPENDED);
}
break;
case VALUE:
// go to the "type" constraint type as it is always the first constraint.
updateDebugState(DebugLevel.CONSTRAINT_TYPE, currentVariableIndex, ConstraintType.TYPE, -1);
setCurrentDebugState(DebugState.SUSPENDED);
break;
case CONSTRAINT_TYPE:
// in this state there should be a constraint of the current type left
// (this has to be ensured by the steps that lead to the "constraint type" level)
if (!currentTypeHasConstraints()) {
throw new IllegalStateException("called stepInto for constraint type \""
+ currentConstraintType + "\", but this type has no constraint to step into");
}
// if there is a constraint, step into it
updateDebugState(DebugLevel.CONSTRAINT, currentVariableIndex, currentConstraintType, 0);
setCurrentDebugState(DebugState.SUSPENDED);
break;
case CONSTRAINT:
// identical to stepOver
step();
setCurrentDebugState(DebugState.SUSPENDED);
break;
default:
throwExceptionInvalidDebugLevel();
}
if (debugTarget != null) {
debugTarget.fireSuspendEvent(DebugEvent.STEP_END);
}
}
/*
* New step over
*/
public void stepOver() throws DebugException {
if (currentVariable == null) {
throw new IllegalStateException(
"currentVariable is null - variableInitNext() has to be called before");
}
if (debugTarget != null) {
debugTarget.fireResumeEvent(DebugEvent.STEP_OVER);
}
switch (currentDebugLevel) {
case VARIABLE:
// first unlock the variable because we might come from a higher index and have the slot still locked.
// This was added because otherwise, when coming from a higher index (i.e. going up in the "recursion"),
// currentSlot.instantiate() would select the old slot value again instead of the next one.
if (currentSlot.getValue() != null) {
currentSlot.unlock(currentVariable);
}
// keep track of the variable we are currently at
Variable tempVariable = currentVariable;
// simulates an ordinary step over but with "looking" at all intermediate steps in the debugger as well
while(checkForChange(tempVariable, currentVariable)) {
step();
}
break;
case VALUE:
// stores the current value as we enter the VALUE case
EObject tempValue = currentSlot.getValue();
while (checkForChange(tempValue, currentSlot.getValue())) {
step();
}
break;
case CONSTRAINT_TYPE:
// keeps track of the constraint type we started from
ConstraintType tempConstraintType = currentConstraintType;
while(checkForChange(tempConstraintType, currentConstraintType)) {
step();
}
break;
case CONSTRAINT:
step();
setCurrentDebugState(DebugState.SUSPENDED);
break;
default:
throwExceptionInvalidDebugLevel();
}
if (debugTarget != null) {
debugTarget.fireChangeEvent(DebugEvent.CONTENT);
debugTarget.fireSuspendEvent(DebugEvent.STEP_END);
}
}
public void stepReturn() throws DebugException {
if (currentVariable == null) {
throw new IllegalStateException(
"currentVariable is null - variableInitNext() has to be called before");
}
if (debugTarget != null) {
debugTarget.fireResumeEvent(DebugEvent.STEP_RETURN);
}
switch (currentDebugLevel) {
case VARIABLE:
// identical to stepOver
stepOver();
break;
case VALUE:
// keep track of the variable we are currently at
Variable tempVariable = currentVariable;
// boolean flag to determine whether we should keep stepping or not
while(checkForChange(tempVariable, currentVariable)) {
step();
}
break;
case CONSTRAINT_TYPE:
// stores the current value as we enter the VALUE case
EObject tempValue = currentSlot.getValue();
while (checkForChange(tempValue, currentSlot.getValue())) {
step();
}
break;
case CONSTRAINT:
// keeps track of the constraint type we started from
ConstraintType tempConstraintType = currentConstraintType;
while(checkForChange(tempConstraintType, currentConstraintType)) {
step();
}
break;
default:
throwExceptionInvalidDebugLevel();
}
if (debugTarget != null) {
debugTarget.fireSuspendEvent(DebugEvent.STEP_END);
}
}
public boolean canTerminate() {
return !isTerminated();
}
public boolean isTerminated() {
return getCurrentDebugState() == DebugState.TERMINATED_FALSE
|| getCurrentDebugState() == DebugState.TERMINATED_TRUE;
}
public void terminate() throws DebugException {
updateDebugState(DebugLevel.NONE, -1, ConstraintType.NONE, -1);
setCurrentDebugState(DebugState.TERMINATED_FALSE);
if (debugTarget != null) {
debugTarget.fireTerminateEvent();
}
}
public String getName() throws DebugException {
// if the matching has terminated successfully, display the match to the user
if (getCurrentDebugState() == DebugState.TERMINATED_TRUE) {
return "["+variables.size() + "/" + variables.size() + " variables matched]";
} else if (getCurrentDebugState() == DebugState.TERMINATED_FALSE) {
return "[no match found]";
}
// if the matching is still in process, display the number of matched variables
return currentVariableIndex + "/" + variables.size() + " variables matched";
}
/*
* Custom methods start here
*/
public IBreakpointManager getManager() {
return DebugPlugin.getDefault().getBreakpointManager();
}
public IBreakpoint[] getBreakpoints() {
// get manager and its breakpoints
IBreakpointManager manager = getManager();
IBreakpoint[] breakpoints = manager.getBreakpoints();
if (breakpoints.length > 0) {
manager.getTypeName(breakpoints[0]);
}
return breakpoints;
}
/**
* Returns all henshin breakpoints we currently have
*/
public ArrayList<HenshinBreakpoint> getHenshinBreakpoints() {
IBreakpoint[] breakpoints = getBreakpoints();
ArrayList<HenshinBreakpoint> henshinBreakpoints = filterHenshinBreakpoints(breakpoints);
return henshinBreakpoints;
}
/**
* Sets an example variable breakpoint.
*/
public void setVariableBreakpoint(Variable variable) {
// get all breakpoints
IBreakpointManager manager = getManager();
// create breakpoint
IResource moduleFile = debugTarget.getModuleResource();
IMarker marker = HenshinMarkerNavigationProvider.addMarker(moduleFile, HenshinModelPlugin.PLUGIN_ID, "/variable", "Sample VariableBreakpoint", IStatus.OK);
VariableBreakpoint breakpoint = new VariableBreakpoint();
try {
// set marker for breakpoint
breakpoint.setMarker(marker);
// This is the node representation for the variable we're currently at.
// Compare this node with the node we received (when the user sets a HenshinBreakpoint with right click) when setting HenshinBreakpoints
Node currentNode = ruleInfo.getVariableInfo().getVariableForNode(variable);
// Get relative path to node - unique representation of the node. Looks something like this: '@units.1/@lhs/@nodes.0'
String nodePath = EcoreUtil.getRelativeURIFragmentPath(null, currentNode);
// get the current type constraint to compare in shouldStopAtVariableBreakpoint later
String typeName = variable.typeConstraint.type.getName();
// uncomment the lines below to stop at from:Account in transferMoney rule or at constraintType 'Manager' in createAccount
breakpoint.setPath(nodePath); // equals node of 'from: Account' in transferMoney rule
breakpoint.setTypeName(typeName); // the exact string representation of the current constraint type (in this case 'Manager' from createAccount rule) - set this via right click by using constraintType.type.getName()
breakpoint.setEnabled(true);
// configure breakpoint
breakpoint.setDebugLevel(DebugLevel.VARIABLE);
// add breakpoint to manager to keep track
manager.addBreakpoint(breakpoint);
} catch (CoreException e1) {
System.out.println("Unable to create custom VariableBreakpoint.");
e1.printStackTrace();
}
}
/**
* Sets breakpoint for a given value.
* This is determined by the value itself and its corresponding index within the domain.
*/
public void setValueBreakpoint(HenshinDebugValue value, int index) {
// get all breakpoints
IBreakpointManager manager = getManager();
// create breakpoint
IResource moduleFile = debugTarget.getModuleResource();
IMarker marker = HenshinMarkerNavigationProvider.addMarker(moduleFile, HenshinModelPlugin.PLUGIN_ID, "/value", "Sample ValueBreakpoint", IStatus.OK);
ValueBreakpoint breakpoint = new ValueBreakpoint();
try {
// set marker for breakpoint
breakpoint.setMarker(marker);
breakpoint.setEnabled(true);
// configure breakpoint
breakpoint.setType(value.getReferenceTypeName());
breakpoint.setValueString(value.getValueString());
breakpoint.setIndex(index);
breakpoint.setDebugLevel(DebugLevel.VALUE);
// add breakpoint to manager to keep track
manager.addBreakpoint(breakpoint);
} catch (CoreException e1) {
System.out.println("Unable to create custom ValueBreakpoint.");
e1.printStackTrace();
}
}
public void setConstraintTypeBreakpoint(String constraintType) {
// get all breakpoints
IBreakpointManager manager = getManager();
// create breakpoint
IResource moduleFile = debugTarget.getModuleResource();
IMarker marker = HenshinMarkerNavigationProvider.addMarker(moduleFile, HenshinModelPlugin.PLUGIN_ID, "/constraintType", "Sample ConstraintTypeBreakpoint", IStatus.OK);
ConstraintTypeBreakpoint breakpoint = new ConstraintTypeBreakpoint();
try {
// set marker for breakpoint
breakpoint.setMarker(marker);
breakpoint.setEnabled(true);
// configure breakpoint
breakpoint.setType(ConstraintType.valueOf(constraintType));
breakpoint.setDebugLevel(DebugLevel.CONSTRAINT_TYPE);
// add breakpoint to manager to keep track
manager.addBreakpoint(breakpoint);
} catch (CoreException e1) {
System.out.println("Unable to create custom ConstraintTypeBreakpoint.");
e1.printStackTrace();
}
}
public void setConstraintInstanceBreakpoint(String constraintInstance) {
// get all breakpoints
IBreakpointManager manager = getManager();
// create breakpoint
IResource moduleFile = debugTarget.getModuleResource();
IMarker marker = HenshinMarkerNavigationProvider.addMarker(moduleFile, HenshinModelPlugin.PLUGIN_ID, "/constraintInstance", "Sample ConstraintInstanceBreakpoint", IStatus.OK);
ConstraintInstanceBreakpoint breakpoint = new ConstraintInstanceBreakpoint();
try {
// set marker for breakpoint
breakpoint.setMarker(marker);
breakpoint.setEnabled(true);
// configure breakpoint
breakpoint.setConstraintInstance(removeRuntimeValuesFromConstraintInstance(constraintInstance));
breakpoint.setDebugLevel(DebugLevel.CONSTRAINT);
// add breakpoint to manager to keep track
manager.addBreakpoint(breakpoint);
} catch (CoreException e1) {
System.out.println("Unable to create custom ConstraintInstanceBreakpoint.");
e1.printStackTrace();
}
}
protected String removeRuntimeValuesFromConstraintInstance(String constraintInstance) {
// Remove runtime values from the constraint instance string
final int indexOfParenthesis = constraintInstance.indexOf('(');
if (indexOfParenthesis > 0) {
constraintInstance = constraintInstance.substring(0, indexOfParenthesis - 1);
}
return constraintInstance;
}
/**
* Returns an array list containing all HenshinBreakpoints currently available
*/
public ArrayList<HenshinBreakpoint> filterHenshinBreakpoints(IBreakpoint[] breakpoints) {
// array list of HenshinBreakpoints to be returned
ArrayList<HenshinBreakpoint> henshinBreakpoints = new ArrayList<HenshinBreakpoint>();
// loop through all breakpoints and return the ones which are of type henshin breakpoints
for (IBreakpoint breakpoint : breakpoints) {
// local variable
HenshinBreakpoint henshinBreakpoint;
if (isHenshinBreakpoint(breakpoint)) {
// cast to henshin breakpoint
henshinBreakpoint = (HenshinBreakpoint) breakpoint;
henshinBreakpoints.add(henshinBreakpoint);
}
}
return henshinBreakpoints;
}
/**
* Checks whether a given breakpoint is a subclass of HenshinBreakpoint
*/
public boolean isHenshinBreakpoint(IBreakpoint breakpoint) {
return HenshinBreakpoint.class.isAssignableFrom(breakpoint.getClass());
}
/**
* Returns an array list containing all VariableBreakpoints currently available
*/
public ArrayList<VariableBreakpoint> filterVariableBreakpoints(ArrayList<HenshinBreakpoint> henshinBreakpoints) {
ArrayList<VariableBreakpoint> variableBreakpoints = new ArrayList<VariableBreakpoint>();
// loop through all breakpoints and return the ones which are of type VariableBreakpoint
for (HenshinBreakpoint henshinBreakpoint : henshinBreakpoints) {
// local variable
if (isVariableBreakpoint(henshinBreakpoint)) {
// cast to VariableBreakpoint
VariableBreakpoint variableBreakpoint = (VariableBreakpoint) henshinBreakpoint;
variableBreakpoints.add(variableBreakpoint);
}
}
return variableBreakpoints;
}
/**
* Checks whether a given breakpoint is of type VariableBreakpoint
*/
public boolean isVariableBreakpoint(HenshinBreakpoint breakpoint) {
return breakpoint instanceof VariableBreakpoint;
}
/**
* Filter all constraint type breakpoints and returns an array list.
* @param henshinBreakpoints
* @return
*/
public ArrayList<ConstraintTypeBreakpoint> filterConstraintTypeBreakpoints(ArrayList<HenshinBreakpoint> henshinBreakpoints) {
ArrayList<ConstraintTypeBreakpoint> constraintTypeBreakpoints = new ArrayList<ConstraintTypeBreakpoint>();
// loop through all breakpoints and return the ones which are of type ConstraintTypeBreakpoint
for (HenshinBreakpoint henshinBreakpoint : henshinBreakpoints) {
// local variable
if (isConstraintTypeBreakpoint(henshinBreakpoint)) {
// cast to VariableBreakpoint
ConstraintTypeBreakpoint constraintTypeBreakpoint = (ConstraintTypeBreakpoint) henshinBreakpoint;
constraintTypeBreakpoints.add(constraintTypeBreakpoint);
}
}
return constraintTypeBreakpoints;
}
/**
* Checks whether a given breakpoint is of type ConstraintTypeBreakpoint
*/
public boolean isConstraintTypeBreakpoint(HenshinBreakpoint breakpoint) {
return breakpoint instanceof ConstraintTypeBreakpoint;
}
/**
* Filter all constraint instance breakpoints and returns an array list.
* @param henshinBreakpoints
* @return
*/
public ArrayList<ConstraintInstanceBreakpoint> filterConstraintInstanceBreakpoints(ArrayList<HenshinBreakpoint> henshinBreakpoints) {
ArrayList<ConstraintInstanceBreakpoint> constraintInstanceBreakpoints = new ArrayList<ConstraintInstanceBreakpoint>();
// loop through all breakpoints and return the ones which are of type ConstraintTypeBreakpoint
for (HenshinBreakpoint henshinBreakpoint : henshinBreakpoints) {
// local variable
if (isConstraintInstanceBreakpoint(henshinBreakpoint)) {
// cast to VariableBreakpoint
ConstraintInstanceBreakpoint constraintInstanceBreakpoint = (ConstraintInstanceBreakpoint) henshinBreakpoint;
constraintInstanceBreakpoints.add(constraintInstanceBreakpoint);
}
}
return constraintInstanceBreakpoints;
}
/**
* Checks whether a given breakpoint is of type ConstraintTypeBreakpoint
*/
public boolean isConstraintInstanceBreakpoint(HenshinBreakpoint breakpoint) {
return breakpoint instanceof ConstraintInstanceBreakpoint;
}
/**
* Checks all the necessary parameters to determine whether to stop at this variable or not.
* @param variableBreakpoint
* @param currentTypeName
* @param currentNodePath
* @return true if any of the criteria of the breakpoint match the current variable
*/
public boolean shouldStopAtVariableBreakpoint(VariableBreakpoint variableBreakpoint, String currentTypeName, String currentNodePath) {
try {
// check if enabled
if (variableBreakpoint.isEnabled()) {
// check if we want to stop at every breakpoint anyway
if (variableBreakpoint.isGeneric()) {
return true;
}
// check if we want to stop at a specific variable
if (variableBreakpoint.isSpecificVariable()) {
if (currentNodePath.equals(variableBreakpoint.getPath())) {
return true;
}
}
// check if we want to stop at a specific variable type
if (variableBreakpoint.isSpecificType()) {
if (currentTypeName.equals(variableBreakpoint.getTypeName())) {
return true;
}
}
}
// if none of the above match we just return false to keep stepping
return false;
} catch (CoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
/**
* Checks if the application should suspend at the given ValueBreakpoint.
* Therefore we check if the value we saved in the ValueBreakpoint is a member of the domain
* and if so we additionally check if the saved index matches the index of the value within the domain.
*
* @param variableBreakpoint
* @param domain
* @return boolean
*/
public boolean shouldStopAtValueBreakpoint(ValueBreakpoint valueBreakpoint, EObject value, int index) {
try {
if (valueBreakpoint.isEnabled()) {
// breakpoint data
String breakpointType = valueBreakpoint.getType();
int breakpointIndex = valueBreakpoint.getIndex();
// current data
String currentType = value.eClass().getName();
int currentIndex = index;
// check
if (breakpointType.equals(currentType) && breakpointIndex == currentIndex) {
return true;
}
}
} catch (CoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
/**
*
* @param constraintTypeBreakpoint
* @return
*/
public boolean shouldStopAtConstraintTypeBreakpoint(ConstraintTypeBreakpoint constraintTypeBreakpoint) {
return constraintTypeBreakpoint.getType() == currentConstraintType;
}
/**
*
* @param constraintTypeBreakpoint
* @return
*/
public boolean shouldStopAtConstraintInstanceBreakpoint(ConstraintInstanceBreakpoint constraintInstanceBreakpoint) {
return removeRuntimeValuesFromConstraintInstance(retrieveConstraintLabel()).equals(constraintInstanceBreakpoint.getConstraintInstance());
}
/**
* Checks if there are any variables left.
*
* @return true if current variable is last variable
*/
public boolean isLastVariable() {
return currentVariableIndex == variables.size() - 1;
}
/**
* Returns an array list containing all ValueBreakpoints currently available
*/
public ArrayList<ValueBreakpoint> filterValueBreakpoints(ArrayList<HenshinBreakpoint> henshinBreakpoints) {
ArrayList<ValueBreakpoint> valueBreakpoints = new ArrayList<ValueBreakpoint>();
// loop through all breakpoints and return the ones which are of type VariableBreakpoint
for (HenshinBreakpoint henshinBreakpoint : henshinBreakpoints) {
// local variable
if (isValueBreakpoint(henshinBreakpoint)) {
// cast to VariableBreakpoint
ValueBreakpoint valueBreakpoint = (ValueBreakpoint) henshinBreakpoint;
valueBreakpoints.add(valueBreakpoint);
}
}
return valueBreakpoints;
}
/**
* Checks whether a given breakpoint is of type ValueBreakpoint
*/
public boolean isValueBreakpoint(IBreakpoint breakpoint) {
return breakpoint instanceof ValueBreakpoint;
}
/*
* Suspends the application if certain criteria for the breakpoint are met.
*/
public void handleDebugState(HenshinBreakpoint henshinBreakpoint) {
// TODO:
// get debug level of current henshinBreakpoint
DebugLevel henshinBreakpointDebugLevel = henshinBreakpoint.getDebugLevel();
if (currentDebugLevel.equals(henshinBreakpointDebugLevel)) {
// TODO: Suspend when criteria met
// we want to suspend here
try {
debugTarget.suspend();
} catch (DebugException e) {
System.out.println("Application could not be suspended.");
e.printStackTrace();
}
} else {
// resume the application in case we are suspended
try {
debugTarget.resume();
} catch (DebugException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/*
* Suspend the application
*/
public void setSuspended() {
try {
debugTarget.suspend();
} catch (DebugException e) {
System.out.println("Application could not be suspended.");
e.printStackTrace();
}
}
/*
* Resume the application
*/
public void setResumed() {
// resume the application in case we are suspended
try {
debugTarget.resume();
} catch (DebugException e) {
System.out.println("Application could not be resumed.");
e.printStackTrace();
}
}
/*
* Checks for deeper and debug levels and returns a boolean to indicate
* whether the application should be suspended or not.
*/
public boolean shouldSuspendApplication(HenshinBreakpoint henshinBreakpoint) {
switch (currentDebugLevel) {
case VARIABLE:
if (henshinBreakpoint instanceof VariableBreakpoint) {
// TODO: Implement - Check for VariableBreakpoint attributes
System.out.println("Matched VariableBreakpoint");
}
break;
case VALUE:
if (henshinBreakpoint instanceof ValueBreakpoint) {
// TODO: Implement
System.out.println("Matched ValueBreakpoint");
}
break;
default:
break;
}
return false;
}
/*
* Custom methods end here
*/
/**
* returns the current array of stackFrames, each containing variables.
* NOTE: maybe we could store the stackframes that do not change
* @return
*/
public synchronized IStackFrame[] getStackFrames(HenshinDebugThread debugThread) {
// calculate the number of stackframes we need:
// we need one stackframe for every matched variable as well as one for the current one.
int lines = currentVariableIndex + 1;
// each debuglevel, if present, gets an extra stackframe
// (current value, constraint type, constraint instance)
if (currentDebugLevel.ordinal() > 1) {
lines += (currentDebugLevel.ordinal() - 1);
}
stackFrames = new IStackFrame[lines];
StringBuilder labelBuilder = new StringBuilder();
// first loop over all matched variables + the current one.
for (int i = 0; i <= currentVariableIndex; i++) {
Variable var = variables.get(i);
DomainSlot slot = domainMap.get(var);
labelBuilder.append("Variable ").append(retrieveVariableLabel(var));
if (!isTerminated() && i != currentVariableIndex || isTerminated()) {
labelBuilder.append(" (Match: ")
.append(retrieveValueLabel(slot.getValue(), graph))
.append(")");
}
HenshinDebugVariable debugVariable =
new HenshinDebugVariable(
debugTarget,
"Variable",
retrieveVariableLabel(var),
var);
String declaredType = var.typeConstraint.type.getName();
// when the domainSlot is null, it is necessary to add the "[]" postfix to the "declared type" string beforehand.
// in all other cases, the postfix is added inside the HenshinDebugValue constructor
if (slot.getDomain() == null) {
declaredType += "[]";
}
HenshinDebugVariable debugDomain =
new HenshinDebugVariable(
debugTarget,
"Domain",
new DebugValueList(
debugTarget,
graph,
declaredType,
(slot.getDomain() != null ? slot.getDomain() : new ArrayList<EObject>()) // if the domain is empty we pass an empty array list, else we pass the domain
)
);
IVariable[] stackVariables = new IVariable[]{debugVariable, debugDomain};
stackFrames[i] = new HenshinStackFrame(debugThread, stackVariables, labelBuilder.toString(), i);
// clear the string builder
labelBuilder.setLength(0);
}
// then add the extra stackframes for...
// ...value
if (currentDebugLevel.ordinal() > 1) {
/*
* Get whole domain (e.g. domain for :Client could be 'charles', 'bob', 'alice').
* Domain may also be null, then we use an empty array list.
*/
List<EObject> domain;
if (currentSlot.getValue() != null) {
domain = graph.getDomain(currentSlot.getValue().eClass(), false);
} else {
domain = new ArrayList<EObject>();
}
int index = domain.indexOf(currentSlot.getValue());
HenshinDebugVariable debugValue =
new HenshinDebugVariable(
debugTarget,
"Value",
new DebugValueEObject(
debugTarget,
graph,
currentVariable.typeConstraint.type.getName(),
currentSlot.getValue(),
index)
);
IVariable[] stackFrameVariables = new IVariable[]{debugValue};
String label = "Value: '" + retrieveValueLabel(currentSlot.getValue(), graph) + "'";
stackFrames[currentVariableIndex+1] = new HenshinStackFrame(debugThread, stackFrameVariables, label, currentVariableIndex+1);
// ... constraint type
if (currentDebugLevel.ordinal() > 2) {
HenshinDebugVariable debugConstrType =
new HenshinDebugVariable(
debugTarget,
"Constraint Type",
currentConstraintType.toString(),
ConstraintType.class.getSimpleName());
label = "Constraint Type: '" + currentConstraintType + "'";
stackFrames[currentVariableIndex+2] = new HenshinStackFrame(debugThread, new IVariable[]{debugConstrType}, label, currentVariableIndex+2);
// ...constraint instance
if (currentDebugLevel.ordinal() > 3) {
HenshinDebugVariable debugConstrIndex =
new HenshinDebugVariable(
debugTarget,
"Constraint Index",
new DebugValueObject(
debugTarget,
graph,
int.class.getName(),
currentConstraintIndex,
0)
);
label = retrieveConstraintLabel();
HenshinDebugVariable debugConstraint =
new HenshinDebugVariable(debugTarget, "Constraint", label, currentConstraintType.toString() + " Constraint");
// if (stackFrames.length >= currentVariableIndex + 4) {
stackFrames[currentVariableIndex+3] = new HenshinStackFrame(debugThread, new IVariable[]{debugConstrIndex, debugConstraint}, label, currentVariableIndex+3);
// }
}
}
}
return stackFrames;
}
private String retrieveVariableLabel(Variable var) {
// include variable name from the rule's LHS
String varNamePrefix = var.name != null ? var.name + ":" : "";
return variables.indexOf(var) + ": '" + varNamePrefix + var.typeConstraint.type.getName() + "'";
}
/**
* Attempts to find an appropriate String representation for the given value EObject.
* This is done by looking for a "name" or an "id" attribute.
* Tries to create an index from the graph if no such attributes can be found.
* Uses the EObjects hashCode, if everything else fails.
* @param value the {@link EObject} that a label should be found for
* @param graph the {@link EGraph} that contains the value, or <code>null</code>
* @return the label String.
*/
public static String retrieveValueLabel(EObject value, EGraph graph) {
if (value == null)
return "null";
// the label should start with the eClass name
String label = value.eClass().getName() + " ";
// look for a "name" or an "id" attribute to use for the label
for (EStructuralFeature feature : value.eClass().getEAllStructuralFeatures()) {
if (feature.getName().equalsIgnoreCase("name")
|| feature.getName().equalsIgnoreCase("id")) {
label += value.eGet(feature);
return label;
}
}
// no useful feature found
if (graph != null) {
List<EObject> allValues = graph.getDomain(value.eClass(), false);
int valIndex = allValues.indexOf(value);
if (valIndex != -1) {
label += "(index=" + valIndex + ")";
} else {
// value is not part of the graph -> use hashCode
label += "(hashCode=" + value.hashCode() + ")";
}
} else {
// graph is not available -> use hashCode
label += "(hashCode=" + value.hashCode() + ")";
}
return label;
}
/**
* Tries to find an appropriate label for the stackFrame to visualize the current constraint.
* This label depends on the current constraint type.
* @return a label string describing the current constraint instance
*/
@SuppressWarnings("unchecked")
private synchronized String retrieveConstraintLabel() {
String label = "";
if (currentDebugLevel != DebugLevel.CONSTRAINT) {
return "Constraint " + currentConstraintIndex;
}
switch (currentConstraintType) {
case TYPE:
label += ("Type = " + currentVariable.typeConstraint.type.getName());
break;
case DANGLING:
label += retrieveValueLabel(currentSlot.getValue(), graph);
break;
case ATTRIBUTE:
AttributeConstraint attributeConstraint = currentVariable.attributeConstraints.get(currentConstraintIndex);
String paramName = String.valueOf(attributeConstraint.getValue());
String paramValue = String.valueOf(currentSlot.getConditionHandler().getParameter(paramName));
// paramValue should be the param name for unset parameters
if (!currentSlot.getConditionHandler().isSet(paramName)) {
paramValue = paramName;
}
String attName = attributeConstraint.getAttribute().getName();
/*
* This usually won't be null but if the user steps through
* the debugging process too fast this may result in a null pointer exception.
* Therefore we check for a value first.
*/
if (currentSlot.getValue() != null) {
String attValue = String.valueOf(currentSlot.getValue().eGet(currentSlot.getValue().eClass().getEStructuralFeature(attName)));
label += attName + " = " + paramName + " (" + attValue + " = " + paramValue + ")";
}
break;
case CONTAINMENT:
ContainmentConstraint containmentConstraint = currentVariable.containmentConstraints.get(currentConstraintIndex);
Variable targetVariable = containmentConstraint.getTargetVariable();
EObject targetValue = domainMap.get(targetVariable).getValue();
// when the container has no locked value yet, targetValue is null.
// In this case, we want to display the containing variable to the user
if (targetValue == null) {
label += retrieveVariableLabel(targetVariable);
} else {
label += targetValue.eClass().getName() + " " + retrieveValueLabel(targetValue, graph);
}
break;
case REFERENCE:
ReferenceConstraint referenceConstraint = currentVariable.referenceConstraints.get(currentConstraintIndex);
EReference reference = referenceConstraint.getReference();
label += reference.getName() + ": ";
List<EObject> targetObjects;
if (reference.isMany()) {
targetObjects = (List<EObject>) currentSlot.getValue().eGet(reference);
} else {
EObject obj = (EObject) currentSlot.getValue().eGet(reference);
targetObjects = Collections.singletonList(obj);
}
StringJoiner listJoiner = new StringJoiner(", ", "[", "]");
for (EObject obj : targetObjects) {
listJoiner.add(retrieveValueLabel(obj, graph));
}
label += listJoiner.toString();
break;
case PATH:
PathConstraint pathConstraint = currentVariable.pathConstraints.get(currentConstraintIndex);
List<EReference> references = pathConstraint.getReferences();
listJoiner = new StringJoiner(", ", "[", "]");
for (EReference ref : references) {
listJoiner.add(ref.getName());
}
label += "Path to variable <" + retrieveVariableLabel(pathConstraint.getTargetVariable())
+ "> via references " + listJoiner.toString();
break;
case USER:
label += "User";
break;
case NONE:
throw new IllegalStateException("trying to find label for constraint with currentConstraintType NONE");
default:
throw new IllegalStateException("missing ConstraintType in switch statement?");
}
return label;
}
/*
* Get the current DebugState.
*/
public DebugState getCurrentDebugState() {
return currentDebugState;
}
/*
* Determines the value of the shouldStep variable used in stepOver().
*/
public <U> boolean checkForChange(Object a, Object b) {
// TODO: Check that a and b are of same type in method declaration
// if we are at DebugLevel.NONE or the suspendCondition is met we stop stepping
if (currentDebugLevel == DebugLevel.NONE || a != b || currentDebugState == DebugState.TERMINATED_TRUE || currentDebugState == DebugState.TERMINATED_FALSE) {
// suspend application when one ore more of the two conditions are met
suspendApplication();
// gets assigned to the shouldStep variable in stepOver()
return false;
}
// gets assigned to the shouldStep variable in stepOver()
return true;
}
/*
* Suspends the application by setting currentDebugState SUSPENDED.
*/
public void suspendApplication() {
if (!isTerminated())
setCurrentDebugState(DebugState.SUSPENDED);
}
/**
* checks the current debug state with the state provided by the parameters
*
* @param expectedDebugLevel the expected {@link DebugLevel}
* @param expectedVariableIndex the expected variable index (see {@link ApplicationCondition#findMatch(int)}
* @param expectedConstraintType the expected constraint type (see {@link constraint#getClass()})
* @param expectedConstraintIndex the expected iteration index for multiple constraints of the same type (for example: {@link Variable#attributeConstraints}).
*
* @return <code>true</code> if the state is the same, <code>false</code> otherwise
*/
public boolean checkDebugState(DebugLevel expectedDebugLevel, int expectedVariableIndex,
EObject expectedValue, ConstraintType expectedConstraintType, int expectedConstraintIndex) {
return (expectedDebugLevel == currentDebugLevel
&& expectedVariableIndex == currentVariableIndex
&& (currentSlot == null? expectedValue == null : expectedValue == currentSlot.getValue())
&& expectedConstraintType == currentConstraintType
&& expectedConstraintIndex == currentConstraintIndex);
}
/**
* Checks the values of the match if one was found.
* @param expectedValues The values in the same order as the corresponding variables
* @return <code>true</code> if the values of the match are the same as the given values
*/
public boolean checkMatch(List<EObject> expectedValues) {
for (int i = 0; i < variables.size(); i++) {
DomainSlot slot = domainMap.get(variables.get(i));
EObject actualValue = slot.getValue();
try {
if (expectedValues.get(i) != actualValue) {
return false;
}
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
return false;
}
}
return true;
}
/**
* Checks the values of the match if one was found.
* @param expectedMatch The expected match
* @return <code>true</code> if the values of the match are the same as the given values
*/
public boolean checkMatch(Map<Variable, EObject> expectedValues) {
for (Variable variable : expectedValues.keySet()) {
EObject expectedValue = expectedValues.get(variable);
EObject actualValue = domainMap.get(variable).getValue();
if (expectedValue != actualValue) {
return false;
}
}
return true;
}
private void throwExceptionInvalidDebugLevel() {
throw new IllegalStateException("currentDebugLevel is \"" + currentDebugLevel
+ "\", but has to be of the enum type DebugLevel");
}
@Override
public String toString() {
return "[debugLevel: " + currentDebugLevel
+ ", variableIndex: " + currentVariableIndex
+ ", value: " + retrieveValueLabel(currentSlot.getValue(), graph)
+ ", constraintType: " + currentConstraintType
+ ", " + retrieveConstraintLabel()
+ "]";
}
}