Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIModel.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIModel.java	(revision 1181)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIModel.java	(revision 1260)
@@ -20,6 +20,8 @@
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.Stack;
 import java.util.logging.Level;
@@ -52,8 +54,40 @@
     /**
      * <p>
-     * A list with all nodes currently known
-     * </p>
-     */
-    private List<TreeNode> allNodes = new ArrayList<TreeNode>();
+     * A map with all nodes currently known
+     * </p>
+     */
+    private Map<IGUIElement, TreeNode> allNodes = new HashMap<IGUIElement, TreeNode>();
+    
+    /**
+     * <p>
+     * true, if internal validation is switched on, false else
+     * </p>
+     */
+    private boolean validate = false;
+
+    /**
+     * <p>
+     * Default constructor to create a GUI model without internal validation
+     * </p>
+     *
+     */
+    public GUIModel() {
+        this(false);
+    }
+
+    /**
+     * <p>
+     * creates a GUI model, that internally validates itself by checking on access to nodes,
+     * if several GUI elements pretend to be equal or if several distinct GUI elements have the
+     * same child.
+     * </p>
+     *
+     * @param validate
+     *            true if internal validation shall be switched on (bad performance), false else
+     *
+     */
+    public GUIModel(boolean validate) {
+        this.validate = validate;
+    }
 
     /**
@@ -107,9 +141,5 @@
         }
 
-        List<IGUIElementSpec> remainingPath = new LinkedList<IGUIElementSpec>();
-
-        for (IGUIElementSpec spec : guiElementPath) {
-            remainingPath.add(spec);
-        }
+        List<IGUIElementSpec> remainingPath = new LinkedList<IGUIElementSpec>(guiElementPath);
 
         return integratePath(root, remainingPath, guiElementFactory);
@@ -128,25 +158,16 @@
      */
     public List<IGUIElement> getChildren(IGUIElement guiElement) {
+        TreeNode node = findNode(guiElement);
+        
         List<IGUIElement> result = null;
-        for (TreeNode node : allNodes) {
-            if (node.guiElement.equals(guiElement)) {
-                if (result == null) {
-                    result = new ArrayList<IGUIElement>();
-
-                    if (node.children != null) {
-                        for (TreeNode child : node.children) {
-                            result.add(child.guiElement);
-                        }
-                    }
+        if (node != null) {
+            result = new LinkedList<IGUIElement>();
+            if (node.children != null) {
+                for (TreeNode child : node.children) {
+                    result.add(child.guiElement);
                 }
-                else {
-                    Console
-                        .traceln(Level.SEVERE,
-                                 "Multiple nodes in the internal GUI model match the same GUI element. "
-                                     + "This should not be the case and the GUI model is probably invalid.");
-                }
-            }
-        }
-
+            }
+        }
+ 
         return result;
     }
@@ -166,10 +187,13 @@
         IGUIElement parent = null;
 
-        for (TreeNode node : allNodes) {
-            if (node.children != null) {
-                for (TreeNode child : node.children) {
+        for (Map.Entry<IGUIElement, TreeNode> entry : allNodes.entrySet()) {
+            if (entry.getValue().children != null) {
+                for (TreeNode child : entry.getValue().children) {
                     if (child.guiElement.equals(guiElement)) {
-                        if (parent != null) {
-                            parent = node.guiElement;
+                        if (parent == null) {
+                            parent = entry.getKey();
+                            if (!validate) {
+                                break;
+                            }
                         }
                         else {
@@ -269,4 +293,81 @@
     /**
      * <p>
+     * This method groups the provided GUI elements under a common parent GUI element. The current
+     * parent GUI element of the GUI elements to group must be the same. If the GUI elements to
+     * be grouped are the whole list of children of the same parent, nothing is changed. 
+     * </p>
+     * 
+     * @param guiElements the list of GUI elements to be grouped
+     * @param groupName   the name of the GUI element group to be created
+     * 
+     * @return the GUI element representing the group, or null, if the provided list of GUI elements
+     *         is empty
+     * 
+     * @throws IllegalArgumentException
+     *             if not all GUI elements to be merged share the same parent, if one of the
+     *             parameters is null, or if one of the provided GUI elements does not belong to
+     *             the model
+     */
+    public IGUIElement groupGUIElements(List<IGUIElement> guiElements, String groupName)
+        throws IllegalArgumentException
+    {
+        if ((guiElements == null) || (groupName == null)) {
+            throw new IllegalArgumentException("parameters must not be null");
+        }
+        
+        if (guiElements.size() <= 0) {
+            // do nothing
+            return null;
+        }
+        
+        TreeNode parent = findNode(guiElements.get(0).getParent());
+        
+        List<TreeNode> nodesToGroup = new LinkedList<TreeNode>();
+        
+        for (IGUIElement element : guiElements) {
+            if (!(element instanceof AbstractDefaultGUIElement)) {
+                throw new IllegalArgumentException
+                    ("can only group nodes of type AbstractDefaultGUIElement");
+            }
+            
+            TreeNode node = findNode(element);
+            if (node == null) {
+                throw new IllegalArgumentException
+                    ("GUI element " + element + " is not part of the model");
+            }
+            
+            if (!nodesToGroup.contains(node)) {
+                nodesToGroup.add(node);
+            }
+            
+            TreeNode parentNode = findNode(element.getParent());
+            
+            if (!parent.equals(parentNode)) {
+                throw new IllegalArgumentException("GUI elements do not share the same parent: " +
+                                                   parent + " <> " + parentNode);
+            }
+        }
+        
+        TreeNode replacement = new TreeNode();
+        replacement.guiElement = new GUIElementGroup(groupName, parent.guiElement);
+        
+        for (TreeNode child : nodesToGroup) {
+            ((GUIElementGroup) replacement.guiElement).addToGroup(child.guiElement);
+            replacement.addChildNode(child);
+            ((AbstractDefaultGUIElement) child.guiElement).setParent(replacement.guiElement);
+            parent.children.remove(child);
+        }
+
+        parent.children.add(replacement);
+
+        // finally, update the known nodes list
+        // if you don't do this getChildren will return wrong things and very bad things happen!
+        allNodes.put(replacement.guiElement, replacement);
+        
+        return replacement.guiElement;
+    }
+    
+    /**
+     * <p>
      * By calling this method, the GUIModel is traversed and similar nodes are merged.
      * </p>
@@ -280,4 +381,5 @@
      * <p>
      * Merges the tree nodes of two GUI elements. The GUI elements need to have the same parent.
+     * They are merged recursively, i.e. also their children are merged. 
      * </p>
      * 
@@ -286,8 +388,40 @@
      * @param guiElement2
      *            the second merge GUI element
+     *            
+     * @return the result of the merge
+     *            
      * @throws IllegalArgumentException
      *             thrown if the two GUI elements do not have the same parent
      */
-    public void mergeGUIElements(IGUIElement guiElement1, IGUIElement guiElement2)
+    public IGUIElement mergeGUIElements(IGUIElement guiElement1, IGUIElement guiElement2)
+        throws IllegalArgumentException
+    {
+        return mergeGUIElements(guiElement1, guiElement2, true);
+    }
+    
+    /**
+     * <p>
+     * Merges the tree nodes of two GUI elements. The GUI elements need to have the same parent.
+     * If the <code>recursively</code> parameter is set to true, the children of the GUI elements
+     * are merged, as well, as long as they are similar. If the parameter is false, the children
+     * are not merged. In this case the resulting GUI element has all children of both merged GUI
+     * elements.
+     * </p>
+     * 
+     * @param guiElement1
+     *            the first merge GUI element
+     * @param guiElement2
+     *            the second merge GUI element
+     * @param recursively
+     *            if true, the merge is done also for similar children, if false, not.
+     *            
+     * @return the result of the merge
+     *            
+     * @throws IllegalArgumentException
+     *             thrown if the two GUI elements do not have the same parent
+     */
+    public IGUIElement mergeGUIElements(IGUIElement guiElement1,
+                                        IGUIElement guiElement2,
+                                        boolean     recursively)
         throws IllegalArgumentException
     {
@@ -310,5 +444,5 @@
         }
 
-        TreeNode replacement = mergeTreeNodes(node1, node2);
+        TreeNode replacement = mergeTreeNodes(node1, node2, recursively);
 
         if (parent != null) {
@@ -321,4 +455,5 @@
         }
 
+        return replacement.guiElement;
     }
 
@@ -356,5 +491,5 @@
 
             child = parentNode.addChild(newElement);
-            allNodes.add(child);
+            allNodes.put(child.guiElement, child);
         }
 
@@ -429,6 +564,6 @@
                         subTreeRoot.children.get(j).guiElement.getSpecification();
                     if (elemSpec1.getSimilarity(elemSpec2)) {
-                        TreeNode replacement =
-                            mergeTreeNodes(subTreeRoot.children.get(i), subTreeRoot.children.get(j));
+                        TreeNode replacement = mergeTreeNodes
+                            (subTreeRoot.children.get(i), subTreeRoot.children.get(j), true);
 
                         subTreeRoot.children.set(i, replacement);
@@ -447,6 +582,7 @@
      * <p>
      * merges two nodes with each other. Merging means registering the GUI element objects with each
-     * other for equality checks. Further it add all children of both nodes to a new replacing node.
-     * Afterwards, all similar nodes of the replacement node are merged as well.
+     * other for equality checks. Further it adds all children of both nodes to a new replacing
+     * node. Afterwards, all similar nodes of the replacement node are merged as well as long
+     * the recursive parameter is set to true.
      * </p>
      * 
@@ -455,7 +591,35 @@
      * @param treeNode2
      *            the second of the two nodes to be merged
+     * @param recursively
+     *            if true, the merging also merges child nodes
+     *            
      * @return a tree node being the merge of the two provided nodes.
      */
-    private TreeNode mergeTreeNodes(TreeNode treeNode1, TreeNode treeNode2) {
+    private TreeNode mergeTreeNodes(TreeNode treeNode1, TreeNode treeNode2, boolean recursively) {
+        // and now a replacement node that is the merge of treeNode1 and treeNode2 is created
+        TreeNode replacement = new TreeNode();
+        replacement.guiElement = treeNode1.guiElement;
+        if (treeNode1.children != null) {
+            for (TreeNode child : treeNode1.children) {
+                replacement.addChildNode(child);
+            }
+        }
+        if (treeNode2.children != null) {
+            for (TreeNode child : treeNode2.children) {
+                replacement.addChildNode(child);
+            }
+        }
+
+        if (recursively) {
+            mergeSubTree(replacement);
+        }
+
+        replacement.guiElement.updateSpecification(treeNode2.guiElement.getSpecification());
+
+        // finally, update the known nodes list
+        // if you don't do this getChildren will return wrong things and very bad things happen!
+        allNodes.remove(treeNode1.guiElement);
+        allNodes.remove(treeNode2.guiElement);
+
         // the following two lines are needed to preserve the references to the existing GUI
         // elements. If two elements are the same, one should be deleted to make the elements
@@ -466,27 +630,5 @@
         treeNode2.guiElement.addEqualGUIElement(treeNode1.guiElement);
         
-        // and now a replacement node that is the merge of treeNode1 and treeNode2 is created
-        TreeNode replacement = new TreeNode();
-        replacement.guiElement = treeNode1.guiElement;
-        if (treeNode1.children != null) {
-            for (TreeNode child : treeNode1.children) {
-                replacement.addChildNode(child);
-            }
-        }
-        if (treeNode2.children != null) {
-            for (TreeNode child : treeNode2.children) {
-                replacement.addChildNode(child);
-            }
-        }
-
-        mergeSubTree(replacement);
-
-        replacement.guiElement.updateSpecification(treeNode2.guiElement.getSpecification());
-
-        // finally, update the known nodes list
-        // if you don't do this getChildren will return wrong things and very bad things happen!
-        allNodes.remove(treeNode1);
-        allNodes.remove(treeNode2);
-        allNodes.add(replacement);
+        allNodes.put(replacement.guiElement, replacement);
 
         return replacement;
@@ -541,14 +683,19 @@
 
         TreeNode result = null;
-        for (TreeNode node : allNodes) {
-            if (node.guiElement.equals(element)) {
-                if (result == null) {
-                    result = node;
-                }
-                else {
-                    Console
-                        .traceln(Level.SEVERE,
-                                 "Multiple nodes in the internal GUI model match the same GUI element. "
-                                     + "This should not be the case and the GUI model is probably invalid.");
+        
+        if (!validate) {
+            result = allNodes.get(element);
+        }
+        else {
+            for (Map.Entry<IGUIElement, TreeNode> entry : allNodes.entrySet()) {
+                if (entry.getKey().equals(element)) {
+                    if (result == null) {
+                        result = entry.getValue();
+                    }
+                    else {
+                        Console.traceln(Level.SEVERE, "Multiple nodes in the internal GUI model " +
+                        		"match the same GUI element. This should not be the case " +
+                        		"and the GUI model is probably invalid.");
+                    }
                 }
             }
@@ -820,4 +967,5 @@
             return guiElement.toString();
         }
+
     }
 }
