Index: trunk/quest-core-events/src/main/java/de/ugoe/cs/quest/eventcore/guimodel/GUIModel.java
===================================================================
--- trunk/quest-core-events/src/main/java/de/ugoe/cs/quest/eventcore/guimodel/GUIModel.java	(revision 603)
+++ trunk/quest-core-events/src/main/java/de/ugoe/cs/quest/eventcore/guimodel/GUIModel.java	(revision 611)
@@ -42,4 +42,9 @@
         throws GUIModelException
     {
+        if ((guiElementPath == null) || (guiElementPath.size() <= 0)) {
+            throw new IllegalArgumentException
+                ("GUI element path must contain at least one element");
+        }
+        
         List<IGUIElementSpec> remainingPath = new LinkedList<IGUIElementSpec>();
         
@@ -103,7 +108,11 @@
     public List<IGUIElement> getRootElements() {
         List<IGUIElement> roots = new ArrayList<IGUIElement>();
-        for (TreeNode rootChild : root.children) {
-            roots.add(rootChild.guiElement);
-        }
+        
+        if (root.children != null) {
+            for (TreeNode rootChild : root.children) {
+                roots.add(rootChild.guiElement);
+            }
+        }
+        
         return roots;
     }
@@ -126,36 +135,199 @@
     {
         IGUIElementSpec specToIntegrateElementFor = remainingPath.remove(0);
-        
-        List<TreeNode> matchingChildren = new ArrayList<TreeNode>();
+
+        List<TreeNode> similarNodes =
+            determineSimilarChildNodes(parentNode, specToIntegrateElementFor);
+        
+        if (similarNodes.size() > 1) {
+            // this may happen, if the GUI elements changed over time (e.g. their name is usually
+            // set later in the program execution) and if they now match because of the changes.
+            // So perform a merge of all similar children of the current parent node to reduce the
+            // model and then try the determination of matching children again.
+            mergeSimilarChildren(parentNode);
+            similarNodes = determineSimilarChildNodes(parentNode, specToIntegrateElementFor);
+            
+            if (similarNodes.size() > 1) {
+                // this can happen, because the similarity check is not transitive. The new GUI
+                // element may be similar to two or more existing ones, but the existing ones
+                // may not be similar to each other. As an example, the new GUI element may
+                // not yet provide sufficient information (such as all names it will have during
+                // the execution of the program). Therefore the similarity check with GUI elements
+                // that already contain more information may return true. But the similarity check
+                // of two GUI elements that already carry a lot of information may return false,
+                // although both are similar to the new GUI element. Therefore, we try a selection
+                // based on the children of the existing and new GUI elements. The one for which
+                // the existing children match best is selected to be the right one.
+                similarNodes =
+                    considerSubChildren(similarNodes, specToIntegrateElementFor, remainingPath);
+
+                if (similarNodes.size() > 1) {
+                    System.out.println("TODO: implement handling to many similar children: " +
+                                       specToIntegrateElementFor);
+                    for (TreeNode similarNode : similarNodes) {
+                        System.out.println("    " + similarNode.guiElement);
+                    }
+                    System.out.println();
+                }
+            }
+        }
+        else if (similarNodes.size() == 1) {
+            similarNodes.get(0).guiElement.updateSpecification(specToIntegrateElementFor);
+        }
+        else if (similarNodes.size() == 0) {
+            // if we get here, the corresponding path does not exist yet. So create it
+            IGUIElement newElement = guiElementFactory.instantiateGUIElement
+                (specToIntegrateElementFor, parentNode.guiElement);
+            
+            similarNodes.add(parentNode.addChild(newElement));
+        }
+        
+        if (remainingPath.size() > 0) {
+            return integratePath(similarNodes.get(0), remainingPath, guiElementFactory);
+        }
+        else {
+            return similarNodes.get(0).guiElement;
+        }
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param parentNode
+     * @param specToIntegrateElementFor
+     * @return
+     */
+    private List<TreeNode> determineSimilarChildNodes(TreeNode        parentNode,
+                                                      IGUIElementSpec specToMatch)
+    {
+        List<TreeNode> similarChildren = new ArrayList<TreeNode>();
         
         if (parentNode.children != null) {
             for (TreeNode child : parentNode.children) {
-                if (specToIntegrateElementFor.getSimilarity(child.guiElement.getSpecification())) {
-                    matchingChildren.add(child);
-                }
-            }
-        }
-        
-        // if we get here, the corresponding path does not exist yet. So create it
-        if (matchingChildren.size() == 0) {
-            IGUIElement newElement = guiElementFactory.instantiateGUIElement
-                (specToIntegrateElementFor, parentNode.guiElement);
-            
-            matchingChildren.add(parentNode.addChild(newElement));
-        }
-        else if (matchingChildren.size() > 1) {
-            throw new GUIModelException
-              ("several children of gui element " + parentNode.guiElement +
-               " match the specification " + specToIntegrateElementFor + " at the same level. " +
-               "Can not decide which is the right one.");
-        }
-        
-        if (remainingPath.size() > 0) {
-            matchingChildren.get(0).guiElement.updateSpecification(specToIntegrateElementFor);
-            return integratePath(matchingChildren.get(0), remainingPath, guiElementFactory);
+                if (specToMatch.getSimilarity(child.guiElement.getSpecification())) {
+                    similarChildren.add(child);
+                }
+            }
+        }
+        
+        return similarChildren;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param similarChildren
+     * @param remove
+     * @return
+     */
+    private List<TreeNode> considerSubChildren(List<TreeNode>                  similarNodes,
+                                               IGUIElementSpec                 specToMatch,
+                                               List<? extends IGUIElementSpec> remainingPath)
+    {
+        List<TreeNode> reducedList = new ArrayList<TreeNode>();
+        
+        // check, if there are any children to consider and remove any similar node, that has
+        // further children
+        if (remainingPath.size() <= 0) {
+            for (TreeNode similarNode : similarNodes) {
+                if ((similarNode.children == null) || (similarNode.children.size() == 0)) {
+                    reducedList.add(similarNode);
+                }
+            }
         }
         else {
-            return matchingChildren.get(0).guiElement;
-        }
+            // if there are further children to consider, then check if there is already a child
+            // node that has an appropriate child
+            IGUIElementSpec subChildSpec = remainingPath.get(0);
+            for (TreeNode similarNode : similarNodes) {
+                if (similarNode.children != null) {
+                    for (TreeNode subchild : similarNode.children) {
+                        if (subchild.guiElement.getSpecification().getSimilarity(subChildSpec)) {
+                            reducedList.add(similarNode);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        
+        return reducedList;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param parentNode
+     */
+    private void mergeSimilarChildren(TreeNode parentNode) {
+        boolean performedMerge;
+        
+        do {
+            performedMerge = false;
+            if (parentNode.children != null) {
+                for (int i = 0; (!performedMerge) && (i < parentNode.children.size()); i++) {
+                    for (int j = i + 1; j < parentNode.children.size(); j++) {
+                        IGUIElement elem1 = parentNode.children.get(i).guiElement;
+                        IGUIElement elem2 = parentNode.children.get(j).guiElement;
+                        if (elem1.getSpecification().getSimilarity(elem2.getSpecification())) {
+                            TreeNode replacement = mergeTreeNodes(parentNode.children.get(i),
+                                                                  parentNode.children.get(j));
+                            
+                            parentNode.children.set(i, replacement);
+                            parentNode.children.remove(j);
+                            performedMerge = true;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        while (performedMerge);
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param treeNode
+     * @param treeNode2
+     * @return
+     */
+    private TreeNode mergeTreeNodes(TreeNode treeNode1, TreeNode treeNode2) {
+        TreeNode replacement = new TreeNode();
+        
+        replacement.guiElement = treeNode1.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
+        // singletons again. However, there may exist references to both objects. To preserve
+        // these, we simply register the equal GUI elements with each other so that an equals
+        // check can return true.
+        replacement.guiElement.addEqualGUIElement(treeNode2.guiElement);
+        treeNode2.guiElement.addEqualGUIElement(replacement.guiElement);
+        
+        if (treeNode1.children != null) {
+            for (TreeNode child : treeNode1.children) {
+                replacement.addChild(child.guiElement);
+            }
+        }
+        
+        if (treeNode2.children != null) {
+            for (TreeNode child : treeNode2.children) {
+                replacement.addChild(child.guiElement);
+            }
+        }
+        
+        mergeSimilarChildren(replacement);
+        
+        replacement.guiElement.updateSpecification(treeNode2.guiElement.getSpecification());
+        
+        return replacement;
     }
 
