Index: /trunk/quest-core-tasktrees/src/main/java/de/ugoe/cs/quest/tasktrees/temporalrelation/DefaultGuiElementSequenceDetectionRule.java
===================================================================
--- /trunk/quest-core-tasktrees/src/main/java/de/ugoe/cs/quest/tasktrees/temporalrelation/DefaultGuiElementSequenceDetectionRule.java	(revision 799)
+++ /trunk/quest-core-tasktrees/src/main/java/de/ugoe/cs/quest/tasktrees/temporalrelation/DefaultGuiElementSequenceDetectionRule.java	(revision 799)
@@ -0,0 +1,345 @@
+package de.ugoe.cs.quest.tasktrees.temporalrelation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.ugoe.cs.quest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.quest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.quest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.quest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.quest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.quest.tasktrees.treeifc.ITaskTreeNodeFactory;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 18.03.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class DefaultGuiElementSequenceDetectionRule implements TemporalRelationshipRule {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.temporalrelation.TemporalRelationshipRule#apply(TaskTreeNode,
+     * TaskTreeBuilder, TaskTreeNodeFactory)
+     */
+    @Override
+    public RuleApplicationResult apply(ITaskTreeNode        parent,
+                                       ITaskTreeBuilder     builder,
+                                       ITaskTreeNodeFactory nodeFactory,
+                                       boolean              finalize)
+    {
+        if (!(parent instanceof ISequence)) {
+            return null;
+        }
+
+        RuleApplicationResult result = new RuleApplicationResult();
+        
+        IGUIElement lastGuiElement = null;
+        int index = 0;
+        while (index < parent.getChildren().size()) {
+            ITaskTreeNode child = parent.getChildren().get(index);
+            IGUIElement currentGuiElement = getGUIElement(child);
+            if ((index > 0) && (!lastGuiElement.equals(currentGuiElement))) {
+                ReducableCommonDenominator commonDenominator =
+                    getNextReducableCommonDenominator(parent, index - 1);
+                    
+                if (commonDenominator != null) {
+                    // condense only if not all children would be condensed or if we can be sure,
+                    // that there will be no further child that should be included in the condensed
+                    // sequence
+                    if ((commonDenominator.noOfTasks < parent.getChildren().size()) &&
+                        (!isOnGuiElementPath(commonDenominator.commonGuiElement, currentGuiElement)))
+                    {
+                        condenseTasksToSequence(parent, index, commonDenominator.noOfTasks,
+                                                builder, nodeFactory, result);
+
+                        result.setRuleApplicationStatus
+                            (RuleApplicationStatus.RULE_APPLICATION_FINISHED);
+                        return result;
+                    }
+                    else {
+                        // the common denominator is on the parent path of the next GUI element.
+                        // Therefore, the current sequences is not finished yet. So break up.
+                        result.setRuleApplicationStatus
+                            (RuleApplicationStatus.RULE_APPLICATION_FEASIBLE);
+                    }
+                }
+            }
+
+            lastGuiElement = currentGuiElement;
+            index++;
+        }
+
+        ReducableCommonDenominator commonDenominator =
+            getNextReducableCommonDenominator(parent, parent.getChildren().size() - 1);
+        
+        if ((commonDenominator != null) &&
+            (commonDenominator.noOfTasks < parent.getChildren().size()))
+        {
+            if (finalize) {
+                condenseTasksToSequence
+                    (parent, index, commonDenominator.noOfTasks, builder, nodeFactory, result);
+                
+                result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FINISHED);
+                
+                return result;
+            }
+            else {
+                result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FEASIBLE);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param guiElement
+     * @param detectedTasks
+     * @param parent
+     * @param index
+     * @param builder
+     * @param nodeFactory
+     * @return
+     */
+    private void condenseTasksToSequence(ITaskTreeNode         parent,
+                                         int                   parentIndex,
+                                         int                   noOfTasks,
+                                         ITaskTreeBuilder      builder,
+                                         ITaskTreeNodeFactory  nodeFactory,
+                                         RuleApplicationResult result)
+    {
+        ISequence newSequence = nodeFactory.createNewSequence();
+        for (int i = 0; i < noOfTasks; i++) {
+            builder.addChild(newSequence, parent.getChildren().get(parentIndex - noOfTasks));
+            // remove exactly the same number of children from the parent.
+            builder.removeChild((ISequence) parent, parentIndex - noOfTasks);
+        }
+                
+        builder.addChild((ISequence) parent, parentIndex - noOfTasks, newSequence);
+        result.addNewlyCreatedParentNode(newSequence);
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param detectedTasks
+     * @return
+     */
+    private ReducableCommonDenominator getNextReducableCommonDenominator(ITaskTreeNode parent,
+                                                                         int           childIndex)
+    {
+        ReducableCommonDenominator commonDenominator = null;
+        
+        // a common denominator can only exist for at least two task tree nodes
+        if (childIndex > 0) {
+            // start with the last one
+            int pos = childIndex;
+
+            commonDenominator = new ReducableCommonDenominator();
+            
+            // check for further predecessors, if they match the same common denominator
+            IGUIElement currentCommonDenominator = null;
+            do {
+                if (--pos < 0) {
+                    currentCommonDenominator = null;
+                }
+                else {
+                    currentCommonDenominator = getCommonDenominator
+                        (getGUIElement(parent.getChildren().get(pos)),
+                         getGUIElement(parent.getChildren().get(pos + 1)));
+                }
+                
+                if (commonDenominator.commonGuiElement == null) {
+                    commonDenominator.commonGuiElement = currentCommonDenominator;
+                }
+            }
+            while ((commonDenominator.commonGuiElement != null) &&
+                   (commonDenominator.commonGuiElement.equals(currentCommonDenominator)));
+            
+            if (commonDenominator.commonGuiElement != null) {
+                // pos points to the last element, that has not the same common denominator.
+                // This one must be subtracted from the task number as well
+                commonDenominator.noOfTasks = childIndex - pos;
+            }
+            else {
+                commonDenominator = null;
+            }
+        }
+        
+        return commonDenominator;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child
+     * @return
+     */
+    private IGUIElement getGUIElement(ITaskTreeNode node) {
+        List<IGUIElement> terminalGUIElements = new ArrayList<IGUIElement>();
+        getTerminalGUIElements(node, terminalGUIElements);
+        return getCommonDenominator(terminalGUIElements);
+    }
+        
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param detectedTaskGroups
+     * @return
+     */
+    /*private IGUIElement getCommonDenominator(Stack<Task> detectedTasks, int start) {
+        List<IGUIElement> allGUIElements = new ArrayList<IGUIElement>();
+        
+        for (int i = start; i < detectedTasks.size(); i++) {
+            allGUIElements.add(detectedTasks.get(i).commonGuiElement);
+        }
+        
+        return getCommonDenominator(allGUIElements);
+    }*/
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child
+     * @return
+     */
+    private IGUIElement getCommonDenominator(IGUIElement guiElement1, IGUIElement guiElement2) {
+        List<IGUIElement> allGUIElements = new ArrayList<IGUIElement>();
+        allGUIElements.add(guiElement1);
+        allGUIElements.add(guiElement2);
+        return getCommonDenominator(allGUIElements);
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child
+     * @return
+     */
+    private IGUIElement getCommonDenominator(List<IGUIElement> guiElements) {
+        IGUIElement commonDenominator = null;
+        
+        if (guiElements.size() > 0) {
+            List<IGUIElement> commonDenominatorPath = new ArrayList<IGUIElement>();
+            
+            // create a reference list using the first GUI element
+            IGUIElement guiElement = guiElements.get(0);
+            while (guiElement != null) {
+                commonDenominatorPath.add(0, guiElement);
+                guiElement = guiElement.getParent();
+            }
+            
+            // for each other GUI element, check the reference list for the first element in the
+            // path, that is not common to the current one, and delete it as well as it subsequent
+            // siblings
+            List<IGUIElement> currentPath = new ArrayList<IGUIElement>();
+            for (int i = 1; i < guiElements.size(); i++) {
+                currentPath.clear();
+                guiElement = guiElements.get(i);
+                while (guiElement != null) {
+                    currentPath.add(0, guiElement);
+                    guiElement = guiElement.getParent();
+                }
+                
+                // determine the index of the first unequal path element
+                int index = 0;
+                while ((index < commonDenominatorPath.size()) && (index < currentPath.size()) &&
+                        commonDenominatorPath.get(index).equals(currentPath.get(index)))
+                {
+                    index++;
+                }
+                
+                // remove all elements from the common denonimator path, that do not match
+                while (index < commonDenominatorPath.size()) {
+                    commonDenominatorPath.remove(index);
+                }
+            }
+            
+            if (commonDenominatorPath.size() > 0) {
+                commonDenominator = commonDenominatorPath.get(commonDenominatorPath.size() - 1);
+            }
+        }
+        
+        return commonDenominator;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child
+     * @return
+     */
+    private void getTerminalGUIElements(ITaskTreeNode node, List<IGUIElement> terminalGUIElements) {
+        if (node instanceof IEventTask) {
+            if (((IEventTask) node).getEventTarget() instanceof IGUIElement) {
+                terminalGUIElements.add((IGUIElement) ((IEventTask) node).getEventTarget());
+            }
+        }
+        else {
+            for (ITaskTreeNode child : node.getChildren()) {
+                getTerminalGUIElements(child, terminalGUIElements);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param currentCommonDenominator
+     * @param guiElement
+     * @return
+     */
+    private boolean isOnGuiElementPath(IGUIElement potentialPathElement, IGUIElement child) {
+        IGUIElement guiElement = child;
+        
+        while (guiElement != null) {
+            if (guiElement.equals(potentialPathElement)) {
+                return true;
+            }
+            guiElement = guiElement.getParent();
+        }
+        
+        return false;
+    }
+
+    /**
+     * 
+     */
+    private static class ReducableCommonDenominator {
+        
+        /** the GUI element being the common denominator */
+        private IGUIElement commonGuiElement;
+        
+        /** the number of tasks that match the common denominator */
+        private int noOfTasks;
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return noOfTasks + " tasks on " + commonGuiElement;
+        }
+        
+    }
+}
Index: /trunk/quest-core-tasktrees/src/main/java/de/ugoe/cs/quest/tasktrees/temporalrelation/TemporalRelationshipRuleManager.java
===================================================================
--- /trunk/quest-core-tasktrees/src/main/java/de/ugoe/cs/quest/tasktrees/temporalrelation/TemporalRelationshipRuleManager.java	(revision 798)
+++ /trunk/quest-core-tasktrees/src/main/java/de/ugoe/cs/quest/tasktrees/temporalrelation/TemporalRelationshipRuleManager.java	(revision 799)
@@ -39,4 +39,5 @@
      */
     public void init() {
+        ruleIndex.add(new DefaultGuiElementSequenceDetectionRule());
         ruleIndex.add(new DefaultEventTargetSequenceDetectionRule());
         ruleIndex.add(new TrackBarSelectionDetectionRule(nodeEqualityRuleManager));
