Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/EventTaskComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/EventTaskComparisonRule.java	(revision 1119)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/EventTaskComparisonRule.java	(revision 1125)
@@ -28,25 +28,43 @@
 public class EventTaskComparisonRule implements NodeComparisonRule {
     
-    /*
-     * (non-Javadoc)
-     * 
-     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#isApplicable(ITaskTreeNode, ITaskTreeNode)
      */
     @Override
-    public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
-        if ((!(node1 instanceof IEventTask)) || (!(node2 instanceof IEventTask))) {
-            return null;
-        }
+    public boolean isApplicable(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return (node1 instanceof IEventTask) && (node2 instanceof IEventTask);
+    }
 
-        if (node1 == node2) {
-            return NodeEquality.IDENTICAL;
-        }
-
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areLexicallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areLexicallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
         IEventTask task1 = (IEventTask) node1;
         IEventTask task2 = (IEventTask) node2;
         
-        if (task1.getEventType().equals(task2.getEventType()) &&
-            task1.getEventTarget().equals(task2.getEventTarget()))
-        {
+        return (task1.getEventType().equals(task2.getEventType()) &&
+                task1.getEventTarget().equals(task2.getEventTarget()));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSyntacticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSyntacticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return areLexicallyEqual(node1, node2);
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSemanticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSemanticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return areLexicallyEqual(node1, node2);
+    }
+
+    @Override
+    public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
+        if (areLexicallyEqual(node1, node2)) {
             return NodeEquality.LEXICALLY_EQUAL;
         }
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/GUIEventTaskComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/GUIEventTaskComparisonRule.java	(revision 1119)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/GUIEventTaskComparisonRule.java	(revision 1125)
@@ -17,7 +17,15 @@
 import de.ugoe.cs.autoquest.eventcore.IEventTarget;
 import de.ugoe.cs.autoquest.eventcore.gui.IInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyPressed;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyReleased;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyTyped;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonDown;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonUp;
 import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
 import de.ugoe.cs.autoquest.eventcore.gui.MouseDoubleClick;
 import de.ugoe.cs.autoquest.eventcore.gui.MouseDragAndDrop;
+import de.ugoe.cs.autoquest.eventcore.gui.Scroll;
 import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
 import de.ugoe.cs.autoquest.eventcore.gui.ValueSelection;
@@ -27,4 +35,5 @@
 import de.ugoe.cs.autoquest.eventcore.guimodel.IImage;
 import de.ugoe.cs.autoquest.eventcore.guimodel.IListBox;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IMenu;
 import de.ugoe.cs.autoquest.eventcore.guimodel.IMenuButton;
 import de.ugoe.cs.autoquest.eventcore.guimodel.IRadioButton;
@@ -51,28 +60,60 @@
 public class GUIEventTaskComparisonRule implements NodeComparisonRule {
     
-    /*
-     * (non-Javadoc)
-     * 
-     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#isApplicable(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean isApplicable(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return
+            ((node1 instanceof IEventTask) && (node2 instanceof IEventTask) &&
+            (((IEventTask) node1).getEventType() instanceof IInteraction) &&
+            (((IEventTask) node2).getEventType() instanceof IInteraction));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areLexicallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areLexicallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.LEXICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.LEXICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSyntacticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSyntacticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.SYNTACTICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSemanticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSemanticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.SEMANTICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.SEMANTICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#compare(ITaskTreeNode, ITaskTreeNode)
      */
     @Override
     public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
-        if ((!(node1 instanceof IEventTask)) || (!(node2 instanceof IEventTask))) {
-            return null;
-        }
-        
+        return getEquality(node1, node2, null);
+    }
+
+    /**
+     * 
+     */
+    private NodeEquality getEquality(ITaskTreeNode node1,
+                                     ITaskTreeNode node2,
+                                     NodeEquality  requiredEqualityLevel)
+    {
         IEventTask task1 = (IEventTask) node1;
         IEventTask task2 = (IEventTask) node2;
         
-        if ((!(task1.getEventType() instanceof IInteraction)) ||
-            (!(task2.getEventType() instanceof IInteraction)))
-        {
-            return null;
-        }
-
-        if (node1 == node2) {
-            return NodeEquality.IDENTICAL;
-        }
-
         if (!task1.getEventTarget().equals(task2.getEventTarget())) {
             return NodeEquality.UNEQUAL;
@@ -82,5 +123,6 @@
         IInteraction interaction2 = (IInteraction) task2.getEventType();
         
-        return compareInteractions(interaction1, interaction2, task1.getEventTarget());
+        return compareInteractions
+            (interaction1, interaction2, task1.getEventTarget(), requiredEqualityLevel);
     }
 
@@ -105,11 +147,35 @@
     private NodeEquality compareInteractions(IInteraction interaction1,
                                              IInteraction interaction2,
-                                             IEventTarget eventTarget)
-    {
+                                             IEventTarget eventTarget,
+                                             NodeEquality equalityLevel)
+    {
+        NodeEquality level = equalityLevel;
+        
+        if (level == null) {
+            level = NodeEquality.LEXICALLY_EQUAL;
+        }
+        
         if (interaction1 == interaction2) {
             return NodeEquality.LEXICALLY_EQUAL;
         }
+        else if ((interaction1 instanceof KeyInteraction) &&
+                 (interaction2 instanceof KeyInteraction))
+        {
+            return compareKeyInteractions
+                ((KeyInteraction) interaction1, (KeyInteraction) interaction2, level);
+        }
+        else if ((interaction1 instanceof MouseButtonInteraction) &&
+                 (interaction2 instanceof MouseButtonInteraction))
+        {
+            return compareMouseButtonInteractions
+                ((MouseButtonInteraction) interaction1, (MouseButtonInteraction) interaction2,
+                 eventTarget, level);
+        }
+        else if ((interaction1 instanceof Scroll) && (interaction2 instanceof Scroll)) {
+            return compareScrolls((Scroll) interaction1, (Scroll) interaction2, level);
+        }
         else if ((interaction1 instanceof TextInput) && (interaction2 instanceof TextInput)) {
-            return compareTextInputs((TextInput) interaction1, (TextInput) interaction2);
+            return compareTextInputs
+                ((TextInput) interaction1, (TextInput) interaction2, level);
         }
         else if ((interaction1 instanceof ValueSelection) &&
@@ -117,23 +183,5 @@
         {
             return compareValueSelections
-                ((ValueSelection<?>) interaction1, (ValueSelection<?>) interaction2);
-        }
-        else if ((interaction1 instanceof MouseClick) &&
-                 (interaction2 instanceof MouseClick))
-        {
-            return compareMouseClicks
-                ((MouseClick) interaction1, (MouseClick) interaction2, eventTarget);
-        }
-        else if ((interaction1 instanceof MouseDoubleClick) &&
-                 (interaction2 instanceof MouseDoubleClick))
-        {
-            return compareMouseDoubleClicks
-                ((MouseDoubleClick) interaction1, (MouseDoubleClick) interaction2, eventTarget);
-        }
-        else if ((interaction1 instanceof MouseDragAndDrop) &&
-                 (interaction2 instanceof MouseDragAndDrop))
-        {
-            return compareMouseDragAndDrops
-                ((MouseDragAndDrop) interaction1, (MouseDragAndDrop) interaction2);
+                ((ValueSelection<?>) interaction1, (ValueSelection<?>) interaction2, level);
         }
         else if (interaction1.equals(interaction2)) {
@@ -143,4 +191,175 @@
             return NodeEquality.UNEQUAL;
         }
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param interaction1
+     * @param interaction2
+     * @param eventTarget
+     * @param level
+     * @return
+     */
+    private NodeEquality compareKeyInteractions(KeyInteraction interaction1,
+                                                KeyInteraction interaction2,
+                                                NodeEquality   equalityLevel)
+    {
+        if (((interaction1 instanceof KeyPressed) && (interaction2 instanceof KeyPressed)) ||
+            ((interaction1 instanceof KeyReleased) && (interaction2 instanceof KeyReleased)) ||
+            ((interaction1 instanceof KeyTyped) && (interaction2 instanceof KeyTyped)))
+        {
+            if ((equalityLevel.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL)) &&
+                (interaction1.getKey() == interaction2.getKey()))
+            {
+                return NodeEquality.LEXICALLY_EQUAL;
+            }
+            else {
+                return NodeEquality.SEMANTICALLY_EQUAL;
+            }
+        }
+        
+        return NodeEquality.UNEQUAL;
+    }
+    
+    /**
+     * <p>
+     * compares two mouse drag and drops. If both drag and drops have the same start and end
+     * coordinates, they are lexically equal. Otherwise, they are semantically equal.
+     * </p>
+     *
+     * @param interaction1 the first mouse drag and drop to compare
+     * @param interaction2 the second mouse drag and drop to compare
+     * 
+     * @return as described
+     */
+    private NodeEquality compareMouseDragAndDrops(MouseDragAndDrop interaction1,
+                                                  MouseDragAndDrop interaction2,
+                                                  NodeEquality     equalityLevel)
+    {
+        if (interaction1.getButton() != interaction2.getButton()) {
+            return NodeEquality.UNEQUAL;
+        }
+        
+        if (equalityLevel.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL)) {
+            int x1 = interaction1.getX();
+            int x1Start = interaction1.getXStart();
+            int x2 = interaction2.getX();
+            int x2Start = interaction2.getXStart();
+            int y1 = interaction1.getY();
+            int y1Start = interaction1.getYStart();
+            int y2 = interaction2.getY();
+            int y2Start = interaction2.getYStart();
+        
+            if ((x1Start == x2Start) && (x1 == x2) && (y1Start == y2Start) && (y1 == y2)) {
+                return NodeEquality.LEXICALLY_EQUAL;
+            }
+        }
+        
+        return NodeEquality.SEMANTICALLY_EQUAL;
+    }
+
+    /**
+     * <p>
+     * compares two mouse button interactions such as clicks, mouse button down, or double clicks.
+     * If both interactions have the same coordinates, they are lexically equal. Otherwise, they
+     * are semantically equal. Mouse clicks for which the coordinates make no lexical difference
+     * (see {@link #clickCoordinatesMakeLexicalDifference(IEventTarget)}) are treated as
+     * lexically equal.
+     * </p>
+     *
+     * @param interaction1 the first mouse button interaction to compare
+     * @param interaction2 the second mouse button interaction to compare
+     * @param eventTarget  the event target on which the interactions happened (used within
+     *                     special comparisons like mouse clicks on buttons, where the coordinates
+     *                     can be ignored)
+     * 
+     * @return as described
+     */
+    private NodeEquality compareMouseButtonInteractions(MouseButtonInteraction interaction1,
+                                                        MouseButtonInteraction interaction2,
+                                                        IEventTarget           eventTarget,
+                                                        NodeEquality           equalityLevel)
+    {
+        boolean coordinatesMatch = true;
+        
+        if ((interaction1 instanceof MouseDragAndDrop) &&
+            (interaction2 instanceof MouseDragAndDrop))
+        {
+            return compareMouseDragAndDrops
+                ((MouseDragAndDrop) interaction1, (MouseDragAndDrop) interaction2, equalityLevel);
+        }
+        else if (interaction1.getButton() != interaction2.getButton()) {
+            return NodeEquality.UNEQUAL;
+        }
+        else if (equalityLevel.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL) &&
+                 clickCoordinatesMakeLexicalDifference(eventTarget))
+        {
+            int x1 = interaction1.getX();
+            int x2 = interaction2.getX();
+            int y1 = interaction1.getY();
+            int y2 = interaction2.getY();
+
+            if ((x1 != x2) || (y1 != y2)) {
+                coordinatesMatch = false;
+            }
+        }
+        
+        // up to now, they can be equal. Now check the types. Do it as last action as these
+        // checks take the most time and should, therefore, only be done latest
+        if (((interaction1 instanceof MouseClick) && (interaction2 instanceof MouseClick)) ||
+            ((interaction1 instanceof MouseDoubleClick) &&
+             (interaction2 instanceof MouseDoubleClick)) ||
+            ((interaction1 instanceof MouseButtonDown) &&
+             (interaction2 instanceof MouseButtonDown)) ||
+            ((interaction1 instanceof MouseButtonUp) &&
+             (interaction2 instanceof MouseButtonUp)))
+        {
+            if (coordinatesMatch) {
+                return NodeEquality.LEXICALLY_EQUAL;
+            }
+            else {
+                return NodeEquality.SEMANTICALLY_EQUAL;
+            }
+        }
+        
+        return NodeEquality.UNEQUAL;
+    }
+
+    /**
+     * <p>
+     * compares two mouse button interactions such as clicks, mouse button down, or double clicks.
+     * If both interactions have the same coordinates, they are lexically equal. Otherwise, they
+     * are semantically equal. Mouse clicks for which the coordinates make no lexical difference
+     * (see {@link #clickCoordinatesMakeLexicalDifference(IEventTarget)}) are treated as
+     * lexically equal.
+     * </p>
+     *
+     * @param interaction1 the first mouse button interaction to compare
+     * @param interaction2 the second mouse button interaction to compare
+     * @param eventTarget  the event target on which the interactions happened (used within
+     *                     special comparisons like mouse clicks on buttons, where the coordinates
+     *                     can be ignored)
+     * 
+     * @return as described
+     */
+    private NodeEquality compareScrolls(Scroll       interaction1,
+                                        Scroll       interaction2,
+                                        NodeEquality equalityLevel)
+    {
+        if (equalityLevel.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL)) {
+            int x1 = interaction1.getXPosition();
+            int x2 = interaction2.getXPosition();
+            int y1 = interaction1.getYPosition();
+            int y2 = interaction2.getYPosition();
+        
+            if ((x1 == x2) && (y1 == y2)) {
+                return NodeEquality.LEXICALLY_EQUAL;
+            }
+        }
+        
+        return NodeEquality.SEMANTICALLY_EQUAL;
     }
 
@@ -158,15 +377,23 @@
      * @return as described
      */
-    private NodeEquality compareTextInputs(TextInput interaction1, TextInput interaction2) {
-        if (interaction1.getEnteredText().equals(interaction2.getEnteredText())) {
-            if (interaction1.getTextInputEvents().equals(interaction2.getTextInputEvents())) {
-                return NodeEquality.LEXICALLY_EQUAL;
-            }
-            else {
-                return NodeEquality.SYNTACTICALLY_EQUAL;
-            }
-        }
-        else {
-            return NodeEquality.SEMANTICALLY_EQUAL;
+    private NodeEquality compareTextInputs(TextInput    interaction1,
+                                           TextInput    interaction2,
+                                           NodeEquality equalityLevel)
+    {
+        switch (equalityLevel) {
+            case LEXICALLY_EQUAL:
+                if (interaction1.getTextInputEvents().equals(interaction2.getTextInputEvents())) {
+                    return NodeEquality.LEXICALLY_EQUAL;
+                }
+                // fall through
+            case SYNTACTICALLY_EQUAL:
+                if (interaction1.getEnteredText().equals(interaction2.getEnteredText())) {
+                    return NodeEquality.SYNTACTICALLY_EQUAL;
+                }
+                // fall through
+            case SEMANTICALLY_EQUAL:
+                return NodeEquality.SEMANTICALLY_EQUAL;
+            default:
+                return NodeEquality.UNEQUAL;
         }
     }
@@ -185,133 +412,17 @@
      */
     private NodeEquality compareValueSelections(ValueSelection<?> interaction1,
-                                                ValueSelection<?> interaction2)
-    {
-        Object value1 = interaction1.getSelectedValue();
-        Object value2 = interaction2.getSelectedValue();
-        
-        if ((value1 == value2) || ((value1 != null) && (value1.equals(value2)))) {
-            return NodeEquality.LEXICALLY_EQUAL;
-        }
-        else {
-            return NodeEquality.SEMANTICALLY_EQUAL;
-        }
-    }
-
-    /**
-     * <p>
-     * compares two mouse clicks. If both clicks have the same coordinates, they are lexically
-     * equal. Otherwise, they are semantically equal. Mouse clicks for which the coordinates make
-     * no lexical difference (see {@link #clickCoordinatesMakeLexicalDifference(IEventTarget)})
-     * are treated as lexically equal.
-     * </p>
-     *
-     * @param interaction1 the first mouse click to compare
-     * @param interaction2 the second mouse click to compare
-     * @param eventTarget  the event target on which the interactions happened (used within
-     *                     special comparisons like mouse clicks on buttons, where the coordinates
-     *                     can be ignored)
-     * 
-     * @return as described
-     */
-    private NodeEquality compareMouseClicks(MouseClick   interaction1,
-                                            MouseClick   interaction2,
-                                            IEventTarget eventTarget)
-    {
-        if (interaction1.getButton() != interaction2.getButton()) {
-            return NodeEquality.UNEQUAL;
-        }
-        
-        if (!clickCoordinatesMakeLexicalDifference(eventTarget)) {
-            return NodeEquality.LEXICALLY_EQUAL;
-        }
-        
-        int x1 = interaction1.getX();
-        int x2 = interaction2.getX();
-        int y1 = interaction1.getY();
-        int y2 = interaction2.getY();
-        
-        if ((x1 == x2) && (y1 == y2)) {
-            return NodeEquality.LEXICALLY_EQUAL;
-        }
-        else {
-            return NodeEquality.SEMANTICALLY_EQUAL;
-        }
-    }
-
-    /**
-     * <p>
-     * compares two mouse double clicks. If both double clicks have the same coordinates, they are
-     * lexically equal. Otherwise, they are semantically equal. Double clicks for which the
-     * coordinates make no lexical difference
-     * (see {@link #clickCoordinatesMakeLexicalDifference(IEventTarget)}) are treated as lexically
-     * equal.
-     * </p>
-     *
-     * @param interaction1 the first mouse double click to compare
-     * @param interaction2 the second mouse double click to compare
-     * @param eventTarget  the event target on which the interactions happened (used within
-     *                     special comparisons like mouse clicks on buttons, where the coordinates
-     *                     can be ignored)
-     * 
-     * @return as described
-     */
-    private NodeEquality compareMouseDoubleClicks(MouseDoubleClick interaction1,
-                                                  MouseDoubleClick interaction2,
-                                                  IEventTarget     eventTarget)
-    {
-        if (interaction1.getButton() != interaction2.getButton()) {
-            return NodeEquality.UNEQUAL;
-        }
-        
-        if (!clickCoordinatesMakeLexicalDifference(eventTarget)) {
-            return NodeEquality.LEXICALLY_EQUAL;
-        }
-        
-        int x1 = interaction1.getX();
-        int x2 = interaction2.getX();
-        int y1 = interaction1.getY();
-        int y2 = interaction2.getY();
-        
-        if ((x1 == x2) && (y1 == y2)) {
-            return NodeEquality.LEXICALLY_EQUAL;
-        }
-        else {
-            return NodeEquality.SEMANTICALLY_EQUAL;
-        }
-    }
-
-    /**
-     * <p>
-     * compares two mouse drag and drops. If both drag and drops have the same start and end
-     * coordinates, they are lexically equal. Otherwise, they are semantically equal.
-     * </p>
-     *
-     * @param interaction1 the first mouse drag and drop to compare
-     * @param interaction2 the second mouse drag and drop to compare
-     * 
-     * @return as described
-     */
-    private NodeEquality compareMouseDragAndDrops(MouseDragAndDrop interaction1,
-                                                  MouseDragAndDrop interaction2)
-    {
-        if (interaction1.getButton() != interaction2.getButton()) {
-            return NodeEquality.UNEQUAL;
-        }
-        
-        int x1 = interaction1.getX();
-        int x1Start = interaction1.getXStart();
-        int x2 = interaction2.getX();
-        int x2Start = interaction2.getXStart();
-        int y1 = interaction1.getY();
-        int y1Start = interaction1.getYStart();
-        int y2 = interaction2.getY();
-        int y2Start = interaction2.getYStart();
-        
-        if ((x1Start == x2Start) && (x1 == x2) && (y1Start == y2Start) && (y1 == y2)) {
-            return NodeEquality.LEXICALLY_EQUAL;
-        }
-        else {
-            return NodeEquality.SEMANTICALLY_EQUAL;
-        }
+                                                ValueSelection<?> interaction2,
+                                                NodeEquality      equalityLevel)
+    {
+        if (equalityLevel.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL)) {
+            Object value1 = interaction1.getSelectedValue();
+            Object value2 = interaction2.getSelectedValue();
+        
+            if ((value1 == value2) || ((value1 != null) && (value1.equals(value2)))) {
+                return NodeEquality.LEXICALLY_EQUAL;
+            }
+        }
+        
+        return NodeEquality.SEMANTICALLY_EQUAL;
     }
 
@@ -336,4 +447,5 @@
             (eventTarget instanceof IImage) ||
             (eventTarget instanceof IListBox) ||
+            (eventTarget instanceof IMenu) ||
             (eventTarget instanceof IMenuButton) ||
             (eventTarget instanceof IRadioButton) ||
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/IterationComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/IterationComparisonRule.java	(revision 1119)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/IterationComparisonRule.java	(revision 1125)
@@ -15,6 +15,10 @@
 package de.ugoe.cs.autoquest.tasktrees.nodeequality;
 
+import java.util.List;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
 import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
 import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
 
@@ -95,29 +99,105 @@
     }
 
-    /*
-     * (non-Javadoc)
-     * 
-     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#isApplicable(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean isApplicable(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return (node1 instanceof IIteration) && (node2 instanceof IIteration);
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areLexicallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areLexicallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        List<ITaskTreeNode> children1 = node1.getChildren();
+        List<ITaskTreeNode> children2 = node2.getChildren();
+        
+        if (children1.size() == children2.size()) {
+            if (children1.size() == 0) {
+                return true;
+            }
+            else {
+                ITaskTreeNode child1 = children1.get(0);
+                ITaskTreeNode child2 = children2.get(0);
+                
+                // iterations may have 3 different structures.
+                // 1. they have one child, which is the iterated one
+                // 2. they have a sequence of children, which is iterated
+                // 3. they have a selection of different iterated variants (usually the variants
+                //    are semantically equal)
+                // check if the type of children match. If not, return false. If they match,
+                // use the equality manager to perform further comparisons
+                
+                if (((child1 instanceof ISelection) && (child2 instanceof ISelection)) ||
+                    ((child1 instanceof ISequence) && (child2 instanceof ISequence)) ||
+                    ((child1 instanceof IEventTask) && (child2 instanceof IEventTask)))
+                {
+                    return getNodeEquality
+                        (child1, child2).isAtLeast(NodeEquality.LEXICALLY_EQUAL);
+                }
+            }
+        }
+        
+        return false;
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSyntacticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSyntacticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        List<ITaskTreeNode> children1 = node1.getChildren();
+        List<ITaskTreeNode> children2 = node2.getChildren();
+        
+        if (children1.size() == children2.size()) {
+            if (children1.size() == 0) {
+                return true;
+            }
+            else {
+                ITaskTreeNode child1 = children1.get(0);
+                ITaskTreeNode child2 = children2.get(0);
+                
+                // iterations may have 3 different structures.
+                // 1. they have one child, which is the iterated one
+                // 2. they have a sequence of children, which is iterated
+                // 3. they have a selection of different iterated variants (usually the variants
+                //    are semantically equal)
+                // ignore the type of the children but check them for equality.
+                
+                return getNodeEquality(child1, child2).isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL);
+            }
+        }
+        
+        return false;
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSemanticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSemanticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return compare(node1, node2).isAtLeast(NodeEquality.SEMANTICALLY_EQUAL);
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#compare(ITaskTreeNode, ITaskTreeNode)
      */
     @Override
     public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
-        if ((!(node1 instanceof IIteration)) || (!(node2 instanceof IIteration))) {
-            return null;
-        }
-
-        if (node1 == node2) {
-            return NodeEquality.IDENTICAL;
-        }
+        List<ITaskTreeNode> children1 = node1.getChildren();
+        List<ITaskTreeNode> children2 = node2.getChildren();
 
         // if both iterations do not have children, they are equal although this doesn't make sense
-        if ((node1.getChildren().size() == 0) && (node2.getChildren().size() == 0)) {
+        if ((children1.size() == 0) && (children2.size() == 0)) {
             return NodeEquality.LEXICALLY_EQUAL;
         }
-        else if ((node1.getChildren().size() == 0) || (node2.getChildren().size() == 0)) {
+        else if ((children1.size() == 0) || (children2.size() == 0)) {
             return NodeEquality.UNEQUAL;
         }
 
-        ITaskTreeNode child1 = node1.getChildren().get(0);
-        ITaskTreeNode child2 = node2.getChildren().get(0);
+        ITaskTreeNode child1 = children1.get(0);
+        ITaskTreeNode child2 = children2.get(0);
 
         // iterations may have 3 different structures.
@@ -132,5 +212,23 @@
         // This condition matches, if both iterations are the same variants of iteration. I.e. three
         // combinations of the permutation are handled herewith.
-        NodeEquality nodeEquality = mRuleManager.applyRules(child1, child2);
+        NodeEquality nodeEquality = getNodeEquality(child1, child2);
+        
+        if (nodeEquality != null) {
+            return nodeEquality;
+        }
+
+        // compare one iteration with a single node as a child and another one with a selection of
+        // semantically equal nodes
+        return selectionChildrenSemanticallyEqualNode(child1, child2);
+        
+        // all other combinations (i.e. sequence with single child and sequence with selection)
+        // can not match
+    }
+
+    /**
+     * TODO update comment
+     */
+    private NodeEquality getNodeEquality(ITaskTreeNode child1, ITaskTreeNode child2) {
+        NodeEquality nodeEquality = callRuleManager(child1, child2, null);
 
         if (nodeEquality.isAtLeast(NodeEquality.SEMANTICALLY_EQUAL)) {
@@ -144,11 +242,6 @@
             }
         }
-
-        // compare one iteration with a single node as a child and another one with a selection of
-        // semantically equal nodes
-        return selectionChildrenSemanticallyEqualNode(child1, child2);
-        
-        // all other combinations (i.e. sequence with single child and sequence with selection)
-        // can not match
+        
+        return NodeEquality.UNEQUAL;
     }
 
@@ -189,5 +282,6 @@
 
         for (ITaskTreeNode child : selection.getChildren()) {
-            NodeEquality nodeEquality = mRuleManager.applyRules(node, child);
+            NodeEquality nodeEquality =
+                  callRuleManager(node, child, commonDenominatorForAllComparisons);
 
             if ((nodeEquality == null) || (nodeEquality == NodeEquality.UNEQUAL))
@@ -203,3 +297,27 @@
     }
 
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child1
+     * @param child2
+     * @param requiredEqualityLevel
+     * @return
+     */
+    private NodeEquality callRuleManager(ITaskTreeNode child1,
+                                         ITaskTreeNode child2,
+                                         NodeEquality  requiredEqualityLevel)
+    {
+        if (requiredEqualityLevel == null) {
+            return mRuleManager.compare(child1, child2);
+        }
+        else if (mRuleManager.areAtLeastEqual(child1, child2, requiredEqualityLevel)) {
+            return requiredEqualityLevel;
+        }
+        else {
+            return NodeEquality.UNEQUAL;
+        }
+    }
 }
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndIterationComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndIterationComparisonRule.java	(revision 1119)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndIterationComparisonRule.java	(revision 1125)
@@ -14,4 +14,6 @@
 
 package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import java.util.List;
 
 import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
@@ -48,11 +50,55 @@
     }
 
-    /*
-     * (non-Javadoc)
-     * 
-     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#isApplicable(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean isApplicable(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return ((node1 instanceof IIteration) && (!(node2 instanceof IIteration))) ||
+               ((node2 instanceof IIteration) && (!(node1 instanceof IIteration)));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areLexicallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areLexicallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.LEXICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.LEXICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSyntacticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSyntacticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.SYNTACTICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSemanticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSemanticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.SEMANTICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.SEMANTICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#compare(ITaskTreeNode, ITaskTreeNode)
      */
     @Override
     public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return getEquality(node1, node2, null);
+    }
+
+    /**
+     * 
+     */
+    private NodeEquality getEquality(ITaskTreeNode node1,
+                                     ITaskTreeNode node2,
+                                     NodeEquality  requiredEqualityLevel)
+    {
         IIteration iteration = null;
         ITaskTreeNode node = null;
@@ -80,11 +126,13 @@
         }
 
+        List<ITaskTreeNode> children = iteration.getChildren();
+        
         // now, that we found the iteration and the node, lets compare the child of the iteration
         // with the node.
-        if (iteration.getChildren().size() < 1) {
+        if (children.size() < 1) {
             return null;
         }
 
-        NodeEquality nodeEquality = mRuleManager.applyRules(iteration.getChildren().get(0), node);
+        NodeEquality nodeEquality = callRuleManager(children.get(0), node, requiredEqualityLevel);
 
         // although the subtask may be identical to the node, we can not return identical, as
@@ -98,3 +146,28 @@
 
     }
+    
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child1
+     * @param child2
+     * @param requiredEqualityLevel
+     * @return
+     */
+    private NodeEquality callRuleManager(ITaskTreeNode child1,
+                                         ITaskTreeNode child2,
+                                         NodeEquality  requiredEqualityLevel)
+    {
+        if (requiredEqualityLevel == null) {
+            return mRuleManager.compare(child1, child2);
+        }
+        else if (mRuleManager.areAtLeastEqual(child1, child2, requiredEqualityLevel)) {
+            return requiredEqualityLevel;
+        }
+        else {
+            return NodeEquality.UNEQUAL;
+        }
+    }
 }
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndSelectionComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndSelectionComparisonRule.java	(revision 1119)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndSelectionComparisonRule.java	(revision 1125)
@@ -14,4 +14,6 @@
 
 package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import java.util.List;
 
 import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
@@ -33,5 +35,5 @@
     /** the rule manager for internally comparing task tree nodes */
     private NodeEqualityRuleManager mRuleManager;
-
+    
     /**
      * <p>
@@ -47,11 +49,55 @@
     }
 
-    /*
-     * (non-Javadoc)
-     * 
-     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#isApplicable(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean isApplicable(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return ((node1 instanceof ISelection) && (!(node2 instanceof ISelection))) ||
+               ((node2 instanceof ISelection) && (!(node1 instanceof ISelection)));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areLexicallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areLexicallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.LEXICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.LEXICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSyntacticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSyntacticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.SYNTACTICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSemanticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSemanticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.SEMANTICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.SEMANTICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#compare(ITaskTreeNode, ITaskTreeNode)
      */
     @Override
     public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return getEquality(node1, node2, null);
+    }
+    
+    /**
+     * 
+     */
+    private NodeEquality getEquality(ITaskTreeNode node1,
+                                     ITaskTreeNode node2,
+                                     NodeEquality  requiredEqualityLevel)
+    {
         ISelection selection = null;
         ITaskTreeNode node = null;
@@ -59,5 +105,5 @@
         if (node1 instanceof ISelection) {
             if (node2 instanceof ISelection) {
-                // the rule is not responsible for two iterations
+                // the rule is not responsible for two selections
                 return null;
             }
@@ -68,5 +114,5 @@
         else if (node2 instanceof ISelection) {
             if (node1 instanceof ISelection) {
-                // the rule is not responsible for two iterations
+                // the rule is not responsible for two selections
                 return null;
             }
@@ -79,7 +125,9 @@
         }
 
-        // now, that we found the iteration and the node, lets compare the child of the iteration
+        // now, that we found the selection and the node, lets compare the children of the selection
         // with the node.
-        if (selection.getChildren().size() < 1) {
+        List<ITaskTreeNode> children = selection.getChildren();
+        
+        if (children.size() < 1) {
             return null;
         }
@@ -87,6 +135,6 @@
         NodeEquality mostConcreteNodeEquality = null;
         
-        for (ITaskTreeNode child : selection.getChildren()) {
-            NodeEquality nodeEquality = mRuleManager.applyRules(child, node);
+        for (ITaskTreeNode child : children) {
+            NodeEquality nodeEquality = callRuleManager(child, node, requiredEqualityLevel);
             
             if (nodeEquality != NodeEquality.UNEQUAL) {
@@ -94,7 +142,16 @@
                     mostConcreteNodeEquality = nodeEquality;
                 }
-                else {
-                    mostConcreteNodeEquality =
-                        mostConcreteNodeEquality.getCommonDenominator(nodeEquality);
+                else if (mostConcreteNodeEquality.isAtLeast(nodeEquality)) {
+                    mostConcreteNodeEquality = nodeEquality;
+                    
+                }
+                
+                if ((requiredEqualityLevel != null) &&
+                    (mostConcreteNodeEquality.isAtLeast(requiredEqualityLevel)))
+                {
+                    // if we found one child of the selection that is as equal as required, then
+                    // we can consider the selection to be sufficiently equal to the other node.
+                    // So we break up checking further children.
+                    break;
                 }
             }
@@ -111,3 +168,28 @@
 
     }
+    
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child1
+     * @param child2
+     * @param requiredEqualityLevel
+     * @return
+     */
+    private NodeEquality callRuleManager(ITaskTreeNode child1,
+                                         ITaskTreeNode child2,
+                                         NodeEquality  requiredEqualityLevel)
+    {
+        if (requiredEqualityLevel == null) {
+            return mRuleManager.compare(child1, child2);
+        }
+        else if (mRuleManager.areAtLeastEqual(child1, child2, requiredEqualityLevel)) {
+            return requiredEqualityLevel;
+        }
+        else {
+            return NodeEquality.UNEQUAL;
+        }
+    }
 }
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeComparisonRule.java	(revision 1119)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeComparisonRule.java	(revision 1125)
@@ -30,4 +30,52 @@
     /**
      * <p>
+     * checks if the rule is applicable for comparing the two provided nodes
+     * </p>
+     * 
+     * @param node1 the first task tree node to compare
+     * @param node2 the second task tree node to compare
+     * 
+     * @return true, if the rule is applicable, false else
+     */
+    public boolean isApplicable(ITaskTreeNode node1, ITaskTreeNode node2);
+
+    /**
+     * <p>
+     * checks, if the provided nodes are lexically equal
+     * </p>
+     * 
+     * @param node1 the first task tree node to compare
+     * @param node2 the second task tree node to compare
+     * 
+     * @return true, if the nodes are equal, false else
+     */
+    public boolean areLexicallyEqual(ITaskTreeNode node1, ITaskTreeNode node2);
+
+    /**
+     * <p>
+     * checks, if the provided nodes are syntactically equal
+     * </p>
+     * 
+     * @param node1 the first task tree node to compare
+     * @param node2 the second task tree node to compare
+     * 
+     * @return true, if the nodes are equal, false else
+     */
+    public boolean areSyntacticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2);
+
+    /**
+     * <p>
+     * checks, if the provided nodes are semantically equal
+     * </p>
+     * 
+     * @param node1 the first task tree node to compare
+     * @param node2 the second task tree node to compare
+     * 
+     * @return true, if the nodes are equal, false else
+     */
+    public boolean areSemanticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2);
+
+    /**
+     * <p>
      * compares two nodes with each other. The result of the method is either a node equality or
      * null. If it is null, it means, that the rule is not able to correctly compare the two given
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeEqualityRuleManager.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeEqualityRuleManager.java	(revision 1119)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeEqualityRuleManager.java	(revision 1125)
@@ -72,5 +72,5 @@
      *                               manager before a call to this method.
      */
-    public NodeEquality applyRules(ITaskTreeNode node1, ITaskTreeNode node2)
+    public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2)
         throws IllegalStateException
     {
@@ -83,9 +83,10 @@
 
         for (NodeComparisonRule rule : mRuleIndex) {
-            nodeEquality = rule.compare(node1, node2);
-
-            if (nodeEquality != null) {
-                // LOG.warning("used rule " + rule + " for equality check");
-                return nodeEquality;
+            if (rule.isApplicable(node1, node2)) {
+                nodeEquality = rule.compare(node1, node2);
+                if (nodeEquality != null) {
+                    // LOG.warning("used rule " + rule + " for equality check");
+                    return nodeEquality;
+                }
             }
         }
@@ -96,3 +97,129 @@
     }
 
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child1
+     * @param child2
+     * @param equalityLevel
+     * @return
+     */
+    public boolean areAtLeastEqual(ITaskTreeNode node1,
+                                   ITaskTreeNode node2,
+                                   NodeEquality  equalityLevel)
+    {
+        if (equalityLevel == null) {
+            throw new IllegalArgumentException("required equality level must not be null");
+        }
+        
+        switch (equalityLevel) {
+            case IDENTICAL:
+                return areIdentical(node1, node2);
+            case LEXICALLY_EQUAL:
+                return areLexicallyEqual(node1, node2);
+            case SYNTACTICALLY_EQUAL:
+                return areSyntacticallyEqual(node1, node2);
+            case SEMANTICALLY_EQUAL:
+                return areSemanticallyEqual(node1, node2);
+            case UNEQUAL:
+                return !areSemanticallyEqual(node1, node2);
+            default:
+                throw new IllegalArgumentException("unknown required equality: " + equalityLevel);
+        }
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child1
+     * @param child2
+     * @return
+     */
+    public boolean areIdentical(ITaskTreeNode node1, ITaskTreeNode node2) {
+        if (mRuleIndex == null) {
+            throw new IllegalStateException("not initialized");
+        }
+        
+        for (NodeComparisonRule rule : mRuleIndex) {
+            if (rule.isApplicable(node1, node2) && rule.areLexicallyEqual(node1, node2)) {
+                 return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child1
+     * @param child2
+     * @return
+     */
+    public boolean areLexicallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        if (mRuleIndex == null) {
+            throw new IllegalStateException("not initialized");
+        }
+        
+        for (NodeComparisonRule rule : mRuleIndex) {
+            if (rule.isApplicable(node1, node2) && rule.areLexicallyEqual(node1, node2)) {
+                 return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child1
+     * @param child2
+     * @return
+     */
+    public boolean areSyntacticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        if (mRuleIndex == null) {
+            throw new IllegalStateException("not initialized");
+        }
+        
+        for (NodeComparisonRule rule : mRuleIndex) {
+            if (rule.isApplicable(node1, node2) && rule.areSyntacticallyEqual(node1, node2)) {
+                 return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child1
+     * @param child2
+     * @return
+     */
+    public boolean areSemanticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        if (mRuleIndex == null) {
+            throw new IllegalStateException("not initialized");
+        }
+        
+        for (NodeComparisonRule rule : mRuleIndex) {
+            if (rule.isApplicable(node1, node2) && rule.areSemanticallyEqual(node1, node2)) {
+                 return true;
+            }
+        }
+
+        return false;
+    }
+
 }
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeIdentityRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeIdentityRule.java	(revision 1119)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeIdentityRule.java	(revision 1125)
@@ -29,12 +29,42 @@
 public class NodeIdentityRule implements NodeComparisonRule {
 
-    /*
-     * (non-Javadoc)
-     * 
-     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#isApplicable(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean isApplicable(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return (node1 == node2);
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areLexicallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areLexicallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return (node1 == node2);
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSyntacticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSyntacticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return (node1 == node2);
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSemanticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSemanticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return (node1 == node2);
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#compare(ITaskTreeNode, ITaskTreeNode)
      */
     @Override
     public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
-        if (node1 == node2) {
+        if (isApplicable(node1, node2)) {
             return NodeEquality.IDENTICAL;
         }
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SelectionComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SelectionComparisonRule.java	(revision 1119)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SelectionComparisonRule.java	(revision 1125)
@@ -14,4 +14,6 @@
 
 package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import java.util.List;
 
 import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
@@ -51,37 +53,113 @@
     }
 
-    /*
-     * (non-Javadoc)
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#isApplicable(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean isApplicable(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return (node1 instanceof ISelection) && (node2 instanceof ISelection);
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areLexicallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areLexicallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.LEXICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.LEXICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSyntacticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSyntacticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.SYNTACTICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSemanticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSemanticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.SEMANTICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.SEMANTICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#compare(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return getEquality(node1, node2, null);
+    }
+
+    /**
      * 
-     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
-     */
-    @Override
-    public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
-        if ((!(node1 instanceof ISelection)) || (!(node2 instanceof ISelection))) {
-            return null;
-        }
-
-        if (node1 == node2) {
-            return NodeEquality.IDENTICAL;
-        }
-
-        // if both sequences do not have children, they are identical. If only one of them has
-        // children, they are unequal.
-        if ((node1.getChildren().size() == 0) && (node2.getChildren().size() == 0)) {
+     */
+    private NodeEquality getEquality(ITaskTreeNode node1,
+                                     ITaskTreeNode node2,
+                                     NodeEquality  requiredEqualityLevel)
+    {
+        List<ITaskTreeNode> children1 = node1.getChildren();
+        List<ITaskTreeNode> children2 = node2.getChildren();
+
+        // if both selections do not have children, they are lexically equal. If only one of them
+        // has children, they are unequal.
+        if ((children1.size() == 0) && (children2.size() == 0)) {
             return NodeEquality.LEXICALLY_EQUAL;
         }
-        else if ((node1.getChildren().size() == 0) || (node2.getChildren().size() == 0)) {
+        else if ((children1.size() == 0) || (children2.size() == 0)) {
             return NodeEquality.UNEQUAL;
         }
 
-        NodeEquality selectionEquality = NodeEquality.LEXICALLY_EQUAL;
-
-        // compare each child of selection one with each child of selection two
+        NodeEquality selectionEquality;
+
+        if (requiredEqualityLevel == null) {
+            // calculate the common equality level for all children of both selections.
+            // do it in both directions to ensure commutative comparison
+            selectionEquality = getCommonEqualityLevel(children1, children2);
+            if (selectionEquality != NodeEquality.UNEQUAL) {
+                return selectionEquality.getCommonDenominator
+                    (getCommonEqualityLevel(children2, children1));
+            }
+            else {
+                return NodeEquality.UNEQUAL;
+            }
+        }
+        else {
+            // we are searching for a specific equality
+            if (checkEqualityLevel(children1, children2, requiredEqualityLevel) &&
+                checkEqualityLevel(children2, children1, requiredEqualityLevel))
+            {
+                return requiredEqualityLevel;
+            }
+            else {
+                return NodeEquality.UNEQUAL;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param children1
+     * @param children2
+     * @param requiredEqualityLevel
+     */
+    private NodeEquality getCommonEqualityLevel(List<ITaskTreeNode> children1,
+                                                List<ITaskTreeNode> children2)
+    {
+        NodeEquality listEquality = NodeEquality.LEXICALLY_EQUAL;
+        
         NodeEquality childEquality;
         NodeEquality currentEquality;
-        for (ITaskTreeNode child1 : node1.getChildren()) {
+        for (ITaskTreeNode child1 : children1) {
             childEquality = null;
-            for (ITaskTreeNode child2 : node2.getChildren()) {
-                currentEquality = mRuleManager.applyRules(child1, child2);
+            for (ITaskTreeNode child2 : children2) {
+                currentEquality = callRuleManager(child1, child2, null);
                 if ((currentEquality != null) && (currentEquality != NodeEquality.UNEQUAL)) {
                     if (childEquality == null) {
@@ -91,39 +169,88 @@
                         childEquality = childEquality.getCommonDenominator(currentEquality);
                     }
-                }
-            }
-            
-            if (childEquality != null) {
-                selectionEquality = selectionEquality.getCommonDenominator(childEquality);
-            }
-            else {
-                return NodeEquality.UNEQUAL;
-            }
-        }
-
-        // compare each child of selection two with each child of selection one
-        for (ITaskTreeNode child2 : node2.getChildren()) {
-            childEquality = null;
-            for (ITaskTreeNode child1 : node1.getChildren()) {
-                currentEquality = mRuleManager.applyRules(child1, child2);
-                if ((currentEquality != null) && (currentEquality != NodeEquality.UNEQUAL)) {
-                    if (childEquality == null) {
-                        childEquality = currentEquality;
-                    }
-                    else {
-                        childEquality = childEquality.getCommonDenominator(currentEquality);
+                    
+                    if (childEquality == NodeEquality.SEMANTICALLY_EQUAL) {
+                        // as we calculate only the common denominator, we can break up here for
+                        // the current child. We will not improve the denominator anymore
+                        break;
                     }
                 }
             }
             
-            if (childEquality != null) {
-                selectionEquality = selectionEquality.getCommonDenominator(childEquality);
+            if (childEquality == null) {
+                // we did not find any child in the second list, that is equal to the searched
+                // child
+                return NodeEquality.UNEQUAL;
             }
             else {
-                return NodeEquality.UNEQUAL;
-            }
-        }
-
-        return selectionEquality;
+                listEquality = listEquality.getCommonDenominator(childEquality);
+            }
+        }
+
+        return listEquality;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param children1
+     * @param children2
+     * @param requiredEqualityLevel
+     */
+    private boolean checkEqualityLevel(List<ITaskTreeNode> children1,
+                                       List<ITaskTreeNode> children2,
+                                       NodeEquality        requiredEqualityLevel)
+    {
+        NodeEquality childEquality;
+        NodeEquality currentEquality;
+        for (ITaskTreeNode child1 : children1) {
+            childEquality = null;
+            for (ITaskTreeNode child2 : children2) {
+                currentEquality = callRuleManager(child1, child2, requiredEqualityLevel);
+                if ((currentEquality != null) && (currentEquality.isAtLeast(requiredEqualityLevel)))
+                {
+                    // we found at least one equal child with sufficient equality in the
+                    // second list. So be can break up for this child.
+                    childEquality = currentEquality;
+                    break;
+                }
+            }
+            
+            if (childEquality == null) {
+                // we did not find any child in the second list, that is equal to the searched
+                // child
+                return false;
+            }
+        }
+
+        // for all children, we found an equality 
+        return true;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child1
+     * @param child2
+     * @param requiredEqualityLevel
+     * @return
+     */
+    private NodeEquality callRuleManager(ITaskTreeNode child1,
+                                         ITaskTreeNode child2,
+                                         NodeEquality  requiredEqualityLevel)
+    {
+        if (requiredEqualityLevel == null) {
+            return mRuleManager.compare(child1, child2);
+        }
+        else if (mRuleManager.areAtLeastEqual(child1, child2, requiredEqualityLevel)) {
+            return requiredEqualityLevel;
+        }
+        else {
+            return NodeEquality.UNEQUAL;
+        }
     }
 
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SequenceComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SequenceComparisonRule.java	(revision 1119)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SequenceComparisonRule.java	(revision 1125)
@@ -14,4 +14,6 @@
 
 package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import java.util.List;
 
 import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
@@ -47,34 +49,72 @@
     }
 
-    /*
-     * (non-Javadoc)
-     * 
-     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#isApplicable(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean isApplicable(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return (node1 instanceof ISequence) && (node2 instanceof ISequence);
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areLexicallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areLexicallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.LEXICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.LEXICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSyntacticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSyntacticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.SYNTACTICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSemanticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSemanticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.SEMANTICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.SEMANTICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#compare(ITaskTreeNode, ITaskTreeNode)
      */
     @Override
     public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
-        if ((!(node1 instanceof ISequence)) || (!(node2 instanceof ISequence))) {
-            return null;
-        }
+        return getEquality(node1, node2, null);
+    }
 
-        if (node1 == node2) {
-            return NodeEquality.IDENTICAL;
-        }
+    /**
+     * 
+     */
+    private NodeEquality getEquality(ITaskTreeNode node1,
+                                     ITaskTreeNode node2,
+                                     NodeEquality  requiredEqualityLevel)
+    {
+        List<ITaskTreeNode> children1 = node1.getChildren();
+        List<ITaskTreeNode> children2 = node2.getChildren();
 
         // if both sequences do not have children, they are equal although this doesn't make sense
-        if ((node1.getChildren().size() == 0) && (node2.getChildren().size() == 0)) {
+        if ((children1.size() == 0) && (children2.size() == 0)) {
             return NodeEquality.LEXICALLY_EQUAL;
         }
 
-        if (node1.getChildren().size() != node2.getChildren().size()) {
+        if (children1.size() != children2.size()) {
             return NodeEquality.UNEQUAL;
         }
 
         NodeEquality resultingEquality = NodeEquality.LEXICALLY_EQUAL;
-        for (int i = 0; i < node1.getChildren().size(); i++) {
-            ITaskTreeNode child1 = node1.getChildren().get(i);
-            ITaskTreeNode child2 = node2.getChildren().get(i);
+        for (int i = 0; i < children1.size(); i++) {
+            ITaskTreeNode child1 = children1.get(i);
+            ITaskTreeNode child2 = children2.get(i);
 
-            NodeEquality nodeEquality = mRuleManager.applyRules(child1, child2);
+            NodeEquality nodeEquality = callRuleManager(child1, child2, requiredEqualityLevel);
 
             if ((nodeEquality == null) || (nodeEquality == NodeEquality.UNEQUAL)) {
@@ -88,3 +128,27 @@
     }
 
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child1
+     * @param child2
+     * @param requiredEqualityLevel
+     * @return
+     */
+    private NodeEquality callRuleManager(ITaskTreeNode child1,
+                                         ITaskTreeNode child2,
+                                         NodeEquality  requiredEqualityLevel)
+    {
+        if (requiredEqualityLevel == null) {
+            return mRuleManager.compare(child1, child2);
+        }
+        else if (mRuleManager.areAtLeastEqual(child1, child2, requiredEqualityLevel)) {
+            return requiredEqualityLevel;
+        }
+        else {
+            return NodeEquality.UNEQUAL;
+        }
+    }
 }
