Index: trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/commands/CMDcondenseHTMLGUIModel.java
===================================================================
--- trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/commands/CMDcondenseHTMLGUIModel.java	(revision 1286)
+++ trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/commands/CMDcondenseHTMLGUIModel.java	(revision 1286)
@@ -0,0 +1,918 @@
+//   Copyright 2012 Georg-August-Universität Göttingen, Germany
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+
+package de.ugoe.cs.autoquest.plugin.html.commands;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementSpec;
+import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLDocument;
+import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLServer;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * This command condenses an HTML GUI model. For HTML GUI models, this is a special task, as, e.g.,
+ * menu structures resisting on several pages of a website are identified separately. However, for
+ * subsequent analysis, they need to be treated as identical. Therefore, this command identifies
+ * pages (i.e. documents) with equal structures and merges them. For differences in the pages, it
+ * adds groups of GUI elements indicating, which GUI element belongs to which pages and not to
+ * others.
+ * </p>
+ * <p>
+ * An example for clarification. Consider the following two pages:
+ * <pre>
+ * server
+ *   |-- document1
+ *   |     \-- html
+ *   |           \-- body
+ *   |                 |-- div (id = menu)
+ *   |                 |     \-- ...
+ *   |                 |-- div (id = textcontent)
+ *   |                 |     \-- ...
+ *   |                 \-- div (id = footer)
+ *   |                       \-- ...
+ *   \-- document2
+ *         \-- html
+ *               \-- body
+ *                     |-- div (id = menu)
+ *                     |     \-- ...
+ *                     |-- div (id = imagecontent)
+ *                     |     \-- ...
+ *                     \-- div (id = footer)
+ *                           \-- ...
+ * </pre>
+ * They only differ in the central div which has the id textcontent for the first document and 
+ * imagecontent for the second. The above GUI model is the result of the monitoring and parsing of
+ * the log files. This command condenses the GUI model to the following structure:
+ * <pre>
+ * server
+ *   \-- document1
+ *         \-- html
+ *               \-- body
+ *                     |-- div (id = menu)
+ *                     |     \-- ...
+ *                     |-- group (document1)
+ *                     |     \-- div (id = textcontent)
+ *                     |           \-- ...
+ *                     |-- group (document2)
+ *                     |     \-- div (id = imagecontent)
+ *                     |           \-- ...
+ *                     \-- div (id = footer)
+ *                           \-- ...
+ * </pre>
+ * This now allows the menu and the footer to be treated as identical over several pages.
+ * </p>
+ * <p>
+ * If several but not all pages share similarities, the created groups refer to several documents
+ * at once.
+ * </p>
+ * 
+ * @author Patrick Harms
+ * @version 1.0
+ */
+public class CMDcondenseHTMLGUIModel implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @Override
+    public void run(List<Object> parameters) {
+        String sequencesName;
+
+        try {
+            sequencesName = (String) parameters.get(0);
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException("illegal parameters provided: " + e);
+        }
+
+        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName + "_targets");
+        if (dataObject == null) {
+            CommandHelpers.objectNotFoundMessage(sequencesName + "_targets");
+            return;
+        }
+        if (!(dataObject instanceof GUIModel)) {
+            CommandHelpers.objectNotType(sequencesName, "GUIModel");
+            return;
+        }
+
+        GUIModel model = (GUIModel) dataObject;
+
+        for (IGUIElement root : model.getRootElements()) {
+            if (root instanceof HTMLServer) {
+                mergeServer((HTMLServer) root, model);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Condenses all documents resisting on the same server. For this, the method first identifies
+     * a hierarchy of clusters of similar GUI elements. This is then used to merge similar GUI
+     * elements and to create subgroups for indicating differences between the documents.
+     * </p>
+     *
+     * @param server the server of which all documents shall be condensed
+     * @param model  the GUI model in which the server is referenced
+     */
+    private void mergeServer(HTMLServer server, GUIModel model) {
+        Console.traceln(Level.INFO, "condensing documents of " + server);
+        Console.traceln(Level.FINE, "creating cluster hierarchy of GUI elements for " + server);
+        GUIElementsCluster rootCluster = getSimilarElementClusterHierarchy(server, model);
+
+        //rootCluster.dump(System.out, "");
+            
+        Console.traceln(Level.FINE, "merging GUI elements in same clusters and creating groups");
+        mergeGUIElementsAccordingToClusters(rootCluster, model, "");
+            
+        //model.dump(System.out, "UTF-8");
+        Console.traceln(Level.INFO, "condensed documents of " + server);
+    }
+
+    /**
+     * <p>
+     * determines clusters of similar GUI elements being children of the provided parent. For this,
+     * the method creates a cluster containing all children of the provided parent as similar
+     * GUI elements. Usually, these are all documents on the same server. It then initiates
+     * creating the cluster hierarchy by calling the recursive method
+     * {@link #addChildClusters(GUIElementsCluster, GUIModel, String)}.
+     * </p>
+     *
+     * @param parent the parent GUI element for which the clusters of similar children shall be
+     *               determined (usually an HTML server)
+     * @param model  the GUI model required for identifying children and sub children
+     * 
+     * @return a list of GUI element clusters of the children.
+     */
+    private GUIElementsCluster getSimilarElementClusterHierarchy(IGUIElement parent, GUIModel model)
+    {
+        GUIElementsCluster cluster = new GUIElementsCluster();
+        
+        List<IGUIElement> children = model.getChildren(parent);
+        
+        SimilarGUIElements similarGuiElements = new SimilarGUIElements();
+        
+        for (IGUIElement child : children) {
+            // when starting with the root, the cluster parent is the child itself
+            similarGuiElements.add(new SimilarGUIElement(child, child));
+        }
+        
+        cluster.addSimilarGUIElements(similarGuiElements);
+        addChildClusters(cluster, model, "");
+        
+        return cluster;
+    }
+    
+
+    /**
+     * <p>
+     * Determines child clusters of a cluster of given GUI elements. This method calls itself
+     * recursively for each directly identified child cluster. Furthermore, it moves child clusters
+     * to children of other child clusters if they are a subset of the clustered GUI elements.
+     * </p>
+     *
+     * @param cluster the cluster for which child clusters shall be identified
+     * @param model   the model required to be able to determine child GUI elements
+     */
+    private void addChildClusters(GUIElementsCluster cluster, GUIModel model, String indent) {
+        for (SimilarGUIElements similarGuiElements : cluster.similarChildrenGUIElements) {
+            createSubClustersForChildren(similarGuiElements, cluster, model, indent + "  ");
+        }
+        
+        for (GUIElementsCluster childCluster : cluster.childClusters) {
+            addChildClusters(childCluster, model, indent + "  ");
+        }
+        
+        createClusterHierachies(cluster.childClusters);
+    }
+    
+    /**
+     * <p>
+     * for a set of similar GUI elements, it identifies similar children and clusters them. For
+     * each identified cluster, it adds a new child cluster to the provided parent cluster. GUI
+     * elements having no further children are added to a default cluster.
+     * </p>
+     *
+     * @param similarGuiElements the similar GUI elements of which the children shall be clustered
+     * @param parentCluster      the parent cluster to which newly identified cluster are added
+     *                           as children
+     * @param model              the model required to be able to determine child GUI elements
+     */
+    private void createSubClustersForChildren(SimilarGUIElements similarGuiElements,
+                                              GUIElementsCluster parentCluster,
+                                              GUIModel           model,
+                                              String             indent)
+    {
+        for (SimilarGUIElement similarGuiElement : similarGuiElements) {
+            List<IGUIElement> children = model.getChildren(similarGuiElement.similarGUIElement);
+        
+            if (children.size() > 0) {
+                for (IGUIElement child : children) {
+                    addToClusterOfSimilarElements
+                        (child, similarGuiElement.mainClusterParent, parentCluster.childClusters,
+                         similarGuiElements, model);
+                }
+            }
+            else {
+                // search for a default cluster to add all elements to, which have no children
+                GUIElementsCluster defaultCluster = null;
+                
+                for (GUIElementsCluster candidate : parentCluster.childClusters) {
+                    if (candidate.similarChildrenGUIElements.size() == 0) {
+                        defaultCluster = candidate;
+                        break;
+                    }
+                }
+                
+                if (defaultCluster == null) {
+                    defaultCluster = new GUIElementsCluster();
+                    parentCluster.addChildCluster(defaultCluster);
+                }
+                
+                defaultCluster.clusteredGUIElements.add(similarGuiElement);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * for a given GUI element, searches the list of known clusters and adds the GUI element to
+     * the cluster, if the cluster already contains a similar GUI element. If not, a new cluster is
+     * created and added to the list of known clusters.
+     * </p>
+     *
+     * @param child         the child for which the cluster is to be determined
+     * @param clusterParent the GUI element for 
+     * @param parents
+     * @param model
+     */
+    private void addToClusterOfSimilarElements(IGUIElement              child,
+                                               IGUIElement              clusterParent,
+                                               List<GUIElementsCluster> knownClusters,
+                                               SimilarGUIElements       similarGuiElements,
+                                               GUIModel                 model)
+    {
+        SimilarGUIElements matchingParents = new SimilarGUIElements();
+        
+        // determine matching parents
+        for (SimilarGUIElement similarGuiElement : similarGuiElements) {
+            for (IGUIElement candidate : model.getChildren(similarGuiElement.similarGUIElement)) {
+                IGUIElementSpec similarityCandidate = candidate.getSpecification();
+                if (similarityCandidate.getSimilarity(child.getSpecification())) {
+                    matchingParents.add(similarGuiElement);
+                    break;
+                }
+            }
+        }
+        
+        // check if an appropriate cluster exists
+        GUIElementsCluster cluster = null;
+        
+        for (GUIElementsCluster clusterCandidate : knownClusters) {
+            if (clusterCandidate.isClusterOf(matchingParents)) {
+                cluster = clusterCandidate;
+                break;
+            }
+        }
+        
+        if (cluster == null) {
+            cluster = new GUIElementsCluster();
+            knownClusters.add(cluster);
+            cluster.setClusteredGUIElements(matchingParents);
+        }
+        
+        cluster.addSimilarChild(child, clusterParent);
+    }
+
+    /**
+     * <p>
+     * clusters of similar children identified for a GUI element may be logical subclusters of
+     * each other. This is, e.g., the case if one cluster contains all elements being part of
+     * document1 and document2 and a further cluster contains all elements of document 1 only. In
+     * this case, the cluster of document1 is added as a child to the other cluster.
+     * </p>
+     *
+     * @param clusters the clusters, for which the child hierarchies shall be created
+     *                 (in-out parameter, as it is changed)
+     */
+    private void createClusterHierachies(List<GUIElementsCluster> clusters) {
+        GUIElementsCluster[] clustersCopy =
+            clusters.toArray(new GUIElementsCluster[clusters.size()]);
+        
+        // sort the array starting with the shortest cluster and ending with the longest
+        Arrays.sort(clustersCopy, new Comparator<GUIElementsCluster>() {
+            @Override
+            public int compare(GUIElementsCluster c1, GUIElementsCluster c2) {
+                return c1.clusteredGUIElements.size() - c2.clusteredGUIElements.size();
+            }
+        });
+        
+        List<GUIElementsCluster> subClustersToHandle = new LinkedList<GUIElementsCluster>();
+        
+        // now add smaller clusters to larger ones, if they are parents
+        for (int i = 0; i < (clustersCopy.length - 1); i++) {
+            GUIElementsCluster potentialChild = clustersCopy[i];
+            
+            // search for the next cluster and add the child
+            for (int j = i + 1; j < clustersCopy.length; j++) {
+                if (clustersCopy[j].isSubCluster(potentialChild)) {
+                    
+                    clustersCopy[j].addChildCluster(potentialChild);
+                    subClustersToHandle.add(clustersCopy[j]);
+                    
+                    for (int k = 0; k < clusters.size(); k++) {
+                        if (clusters.get(k) == potentialChild) {
+                            clusters.remove(k);
+                            break;
+                        }
+                    }
+                    
+                    break;
+                }
+            }
+        }
+        
+        // finally, for all subclusters that were changed, ensure the creation of their internal
+        // hierarchy as well
+        for (GUIElementsCluster subClusterToHandle : subClustersToHandle) {
+            createClusterHierachies(subClusterToHandle.childClusters);
+        }
+    }
+
+    /**
+     * <p>
+     * called for each cluster to merge similar GUI elements depending on the clusters and to
+     * create GUI element groups if required. Calls itself recursively to be also applied on
+     * child clusters.
+     * </p>
+     *
+     * @param cluster the cluster of which the similar children shall be merged
+     * @param model   the model to be adapted through the merge
+     */
+    private void mergeGUIElementsAccordingToClusters(GUIElementsCluster cluster,
+                                                     GUIModel           model,
+                                                     String             indent)
+    {
+        //System.out.println(indent + "handling " + cluster);
+        
+        for (SimilarGUIElements similarGUIElements : cluster.similarChildrenGUIElements) {
+            mergeGUIElements(similarGUIElements, model, indent + "  ");
+        }
+        
+        if (cluster.childClusters.size() > 0) {
+            //System.out.println(indent + "  handling child clusters");
+            
+            for (GUIElementsCluster childCluster : cluster.childClusters) {
+                if (cluster.isDefault() || cluster.clusterParentsMatch(childCluster)) {
+                    // for default cluster or children not creating subgroups, just traverse the
+                    // cluster hierarchy
+                    mergeGUIElementsAccordingToClusters(childCluster, model, indent + "    ");
+                }
+                else {
+                    createClusterGroup(childCluster, model, indent + "    ");
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * merges similar GUI elements using the provided model
+     * </p>
+     *
+     * @param similarGUIElements the GUI elements to merge
+     * @param model              the model to be used for merging the GUI elements
+     */
+    private void mergeGUIElements(SimilarGUIElements similarGUIElements,
+                                  GUIModel           model,
+                                  String             indent)
+    {
+        IGUIElement mergeResult = similarGUIElements.get(0).similarGUIElement;
+
+        while (similarGUIElements.size() > 1) {
+            //System.out.println(indent + "merging " + mergeResult + " and " +
+            //                   similarGUIElements.get(1).similarGUIElement);
+            mergeResult = model.mergeGUIElements
+                (mergeResult, similarGUIElements.remove(1).similarGUIElement, false);
+        }
+    }
+
+    /**
+     * <p>
+     * creates a group of GUI elements to represent a cluster. Uses the provided model for group
+     * creation.
+     * </p>
+     *
+     * @param cluster the cluster for which the group shall be created
+     * @param model   the model to be used for creating the groups
+    */
+    private void createClusterGroup(GUIElementsCluster cluster, GUIModel model, String indent) {
+        //System.out.println(indent + "creating group for " + cluster);
+
+        List<IGUIElement> guiElementsToGroup = new LinkedList<IGUIElement>();
+        
+        for (SimilarGUIElements similarGUIElements : cluster.similarChildrenGUIElements) {
+            mergeGUIElements(similarGUIElements, model, indent);
+            guiElementsToGroup.addAll(similarGUIElements.toGUIElementList());
+        }
+        
+        //System.out.println(indent + "  iterating child clusters of " + cluster);
+        for (GUIElementsCluster childCluster : cluster.childClusters) {
+            if (cluster.isDefault() || cluster.clusterParentsMatch(childCluster)) {
+                // for default cluster or children not creating subgroups, just traverse the
+                // cluster hierarchy
+                mergeGUIElementsAccordingToClusters(childCluster, model, indent + "  ");
+            }
+            else {
+                createClusterGroup(childCluster, model, indent + "  ");
+            }
+            
+            if (childCluster.getGroup() != null) {
+                if (cluster.isSubCluster(childCluster)) {
+                    guiElementsToGroup.add(childCluster.getGroup());
+                }
+                else {
+                    guiElementsToGroup.add(childCluster.getGroup().getParent());
+                }
+            }
+        }
+        
+        //System.out.println(indent + "grouping: " + guiElementsToGroup);
+        IGUIElement group = model.groupGUIElements(guiElementsToGroup, getGroupName(cluster));
+        //System.out.println(indent + "  created group for " + cluster + ": " + group);
+        
+        cluster.setGroup(group);
+    }
+
+    /**
+     * <p>
+     * determines a name for a group to be created for the provided cluster
+     * </p>
+     *
+     * @param cluster the cluster for which a group name shall be determined
+     * 
+     * @return an appropriate name
+     */
+    private String getGroupName(GUIElementsCluster cluster) {
+        StringBuffer name = new StringBuffer();
+        
+        name.append("group_");
+        
+        for (SimilarGUIElement guiElement : cluster.clusteredGUIElements) {
+            if (guiElement.mainClusterParent instanceof HTMLDocument) {
+                name.append(((HTMLDocument) guiElement.mainClusterParent).getPath());
+            }
+            else {
+                name.append(guiElement.mainClusterParent.getStringIdentifier());
+            }
+        }
+        
+        return name.toString();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "condenseHTMLGUIModel <sequence>";
+    }
+
+    /**
+     * <p>
+     * represents a cluster of similar GUI elements. It consists of a list of similar GUI elements
+     * represented by the cluster somewhere in the GUI element hierarchy. It contains, furthermore,
+     * a list of GUI elements that belong to the root cluster of the cluster hierarchy, which are
+     * usually the similar documents. It also refers to child clusters, and if created, a GUI
+     * element group representing the cluster.
+     * </p>
+     */
+    private static class GUIElementsCluster {
+        
+        /**
+         * <p>
+         * the similar children on the same level in the GUI model represented through the cluster
+         * </p>
+         */
+        private List<SimilarGUIElements> similarChildrenGUIElements =
+            new LinkedList<SimilarGUIElements>();
+        
+        /**
+         * <p>
+         * the similar root GUI elements on the first level of the created hierarchy (usually
+         * the documents on a website) to which the similar GUI elements belong
+         * </p>
+         */
+        private SimilarGUIElements clusteredGUIElements = new SimilarGUIElements();
+        
+        /**
+         * <p>
+         * reference to the child clusters.
+         * </p>
+         */
+        private List<GUIElementsCluster> childClusters = new LinkedList<GUIElementsCluster>();
+        
+        /**
+         * <p>
+         * reference to the GUI element group if one is created for the cluster
+         * </p>
+         */
+        private IGUIElement group = null;
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return getName();
+        }
+
+        /**
+         * <p>
+         * checks, if the main cluster parents, i.e., the documents of this and the provided cluster
+         * match
+         * </p>
+         *
+         * @param other the other cluster of which the main cluster parents shall be compared to
+         *              this
+         *              
+         * @return true if they match, false else
+         */
+        private boolean clusterParentsMatch(GUIElementsCluster other) {
+            // cluster parent may already be merged and therefore equals --> use system identity
+            // hash code for uniqueness
+            Set<Integer> mainClusterParents1 = new HashSet<Integer>();
+            for (SimilarGUIElement clusteredElem1 : clusteredGUIElements) {
+                mainClusterParents1.add(System.identityHashCode(clusteredElem1.mainClusterParent));
+            }
+            
+            Set<Integer> mainClusterParents2 = new HashSet<Integer>();
+            for (SimilarGUIElement clusteredElem2 : other.clusteredGUIElements) {
+                mainClusterParents2.add(System.identityHashCode(clusteredElem2.mainClusterParent));
+            }
+            
+            return mainClusterParents1.equals(mainClusterParents2);
+        }
+
+        /**
+         * <p>
+         * returns true, if this cluster is a default cluster
+         * </p>
+         *
+         * @return
+         */
+        public boolean isDefault() {
+            return clusteredGUIElements.size() <= 0;
+        }
+
+        /**
+         * <p>
+         * sets the GUI element group created for this cluster
+         * </p>
+         *
+         * @param group the GUI element group created for this cluster
+         */
+        private void setGroup(IGUIElement group) {
+            this.group = group;
+        }
+
+        /**
+         * <p>
+         * returns the GUI element group created for this cluster
+         * </p>
+         *
+         * @return the GUI element group created for this cluster
+         */
+        private IGUIElement getGroup() {
+            return group;
+        }
+
+        /**
+         * <p>
+         * checks if the given cluster should be a child of this cluster. It should be a child
+         * if its list of clustered documents is a subset of the list of clustered documents of
+         * this cluster.
+         * </p>
+         *
+         * @param potentialChild the cluster to check
+         * 
+         * @return true, if the cluster should be a child, false else
+         */
+        private boolean isSubCluster(GUIElementsCluster potentialChild) {
+            return 
+                (potentialChild.clusteredGUIElements.size() < clusteredGUIElements.size()) &&
+                (clusteredGUIElements.containsAll(potentialChild.clusteredGUIElements));
+        }
+
+        /**
+         * <p>
+         * sets the list of clustered GUI elements, i.e., documents of which this cluster contains
+         * similar GUI elements
+         * </p>
+         *
+         * @param clusteredGUIElements the new list of clustered GUI elements
+         */
+        private void setClusteredGUIElements(SimilarGUIElements clusteredGUIElements) {
+            this.clusteredGUIElements = clusteredGUIElements;
+        }
+
+        /**
+         * <p>
+         * adds a child cluster to this cluster
+         * </p>
+         *
+         * @param cluster the new child cluster
+         */
+        private void addChildCluster(GUIElementsCluster cluster) {
+            childClusters.add(cluster);
+        }
+
+        /**
+         * <p>
+         * adds similar GUI elements to the list of similar GUI elements
+         * </p>
+         */
+        private void addSimilarGUIElements(SimilarGUIElements similarGuiElements) {
+            similarChildrenGUIElements.add(similarGuiElements);
+        }
+
+        /**
+         * <p>
+         * determines a list of similar GUI elements to which the provided GUI element belongs.
+         * If it finds one, it adds the GUI element to that list. If not, it creates a new one.
+         * </p>
+         *
+         * @param child         the child to to be added to the list of similar GUI elements
+         * @param clusterParent the main parent of the cluster to which the GUI element belongs
+         *                      (usually the document)
+         */
+        private void addSimilarChild(IGUIElement child, IGUIElement clusterParent) {
+            SimilarGUIElements similarGUIElements = null;
+            
+            for (SimilarGUIElements candidate : similarChildrenGUIElements) {
+                if (candidate.elementsMatch(child)) {
+                    similarGUIElements = candidate;
+                }
+            }
+            
+            if (similarGUIElements == null) {
+                similarGUIElements = new SimilarGUIElements();
+                similarChildrenGUIElements.add(similarGUIElements);
+            }
+            
+            similarGUIElements.add(new SimilarGUIElement(child, clusterParent));
+        }
+
+        /**
+         * <p>
+         * checks, if this cluster is a cluster representing the provided main cluster parents
+         * </p>
+         */
+        private boolean isClusterOf(SimilarGUIElements checkedClusterParents) {
+            return (clusteredGUIElements.size() == checkedClusterParents.size()) &&
+                   (clusteredGUIElements.containsAll(checkedClusterParents));
+        }
+
+        /**
+         * <p>
+         * returns a name for this cluster
+         * </p>
+         */
+        private String getName() {
+            StringBuffer ret = new StringBuffer("cluster(");
+            
+            if (clusteredGUIElements.size() > 0) {
+                for (SimilarGUIElement similarGUIElement : clusteredGUIElements) {
+                    if (ret.length() > "cluster(".length()) {
+                        ret.append(", ");
+                    }
+
+                    ret.append(similarGUIElement);
+                }
+            }
+            else {
+                ret.append("default");
+            }
+            
+            ret.append(")");
+            
+            return ret.toString();
+        }
+        
+        /**
+         * <p>
+         * dumps infos of this cluster to the provided stream
+         * </p>
+         */
+//        private void dump(PrintStream out, String indent) {
+//            out.print(indent);
+//            out.print(getName());
+//            out.print(" { ");
+//            
+//            if (similarChildrenGUIElements.size() > 0) {
+//                out.println();
+//                out.print(indent);
+//                out.println("  similar children {");
+//
+//                for (SimilarGUIElements similarGuiElements : similarChildrenGUIElements) {
+//                    similarGuiElements.dump(out, indent + "    ");
+//                }
+//
+//                out.print(indent);
+//                out.println("  }");
+//
+//                if (childClusters.size() > 0) {
+//                    out.print(indent);
+//                    out.println("  child clusters {");
+//
+//                    for (GUIElementsCluster childCluster : childClusters) {
+//                        childCluster.dump(out, indent + "    ");
+//                    }
+//
+//                    out.print(indent);
+//                    out.println("  }");
+//                }
+//
+//                out.print(indent);
+//            }
+//            else if (clusteredGUIElements.size() > 0) {
+//                out.println();
+//                out.print(indent);
+//                out.println("  clustered GUIElements {");
+//
+//                for (SimilarGUIElement clusteredGUIElement : clusteredGUIElements) {
+//                    clusteredGUIElement.dump(out, indent + "    ");
+//                }
+//
+//                out.print(indent);
+//                out.println("  }");
+//                out.print(indent);
+//            }
+//            
+//            out.println("}");
+//        }
+
+    }
+
+    /**
+     * <p>
+     * represents a list of similar GUI elements
+     * </p>
+     */
+    private static class SimilarGUIElements extends LinkedList<SimilarGUIElement> {
+
+        /** 
+         * <p>
+         * default serial version UID
+         * </p>
+         */
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * <p>
+         * checks if the provided GUI element is similar to at least on of the GUI elements
+         * represented by this list.
+         * </p>
+         */
+        private boolean elementsMatch(IGUIElement otherGUIElement) {
+            // it is sufficient, if one of the elements (if any) matches, as the similarity 
+            // check must be transitive
+            if (size() > 0) {
+                return get(0).similarGUIElement.getSpecification().getSimilarity
+                           (otherGUIElement.getSpecification());
+            }
+            else {
+                return true;
+            }
+        }
+    
+        /**
+         * <p>
+         * transforms this list to a list of GUI elements
+         * </p>
+         */
+        private List<IGUIElement> toGUIElementList() {
+            List<IGUIElement> guiElementList = new LinkedList<IGUIElement>();
+            
+            for (SimilarGUIElement similarGUIElement : this) {
+                guiElementList.add(similarGUIElement.similarGUIElement);
+            }
+
+            return guiElementList;
+        }
+
+        /**
+         * <p>
+         * dumps infos of this list to the provided stream
+         * </p>
+         */
+//        private void dump(PrintStream out, String indent) {
+//            out.print(indent);
+//            out.print("{ ");
+//            
+//            for (int i = 0; i < super.size(); i++) {
+//                if (i > 0) {
+//                    out.print(", ");
+//                }
+//                out.print(super.get(i));
+//            }
+//            
+//            out.println(" }");
+//        }
+    }
+    
+    /**
+     * <p>
+     * represents a single similar GUI element and the main cluster parent, i.e., the document to
+     * which it belongs.
+     * </p>
+     */
+    private static class SimilarGUIElement {
+        
+        /**
+         * <p>
+         * the represented GUI element
+         * </p>
+         */
+        private IGUIElement similarGUIElement;
+        
+        /**
+         * <p>
+         * the main cluster parent, i.e., usually the document of the represented GUI element
+         * </p>
+         */
+        private IGUIElement mainClusterParent;
+        
+        /**
+         * <p>
+         * simple constructor to initialize the objects
+         * </p>
+         */
+        private SimilarGUIElement(IGUIElement similarGUIElement, IGUIElement clusterParent) {
+            this.similarGUIElement = similarGUIElement;
+            this.mainClusterParent = clusterParent;
+        }
+
+        /**
+         * <p>
+         * dumps infos about this object to the provided stream
+         * </p>
+         */
+//        private void dump(PrintStream out, String indent) {
+//            out.print(indent);
+//            out.println(this);
+//        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
+            }
+            else if (obj instanceof SimilarGUIElement) {
+                return (similarGUIElement.equals(((SimilarGUIElement) obj).similarGUIElement));
+            }
+            else {
+                return false;
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return similarGUIElement + " (" + mainClusterParent + ")";
+        }
+    }
+        
+}
