source: trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/commands/CMDcondenseHTMLGUIModel.java @ 2210

Last change on this file since 2210 was 2210, checked in by pharms, 7 years ago
File size: 38.3 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.plugin.html.commands;
16
17import java.util.ArrayList;
18import java.util.Arrays;
19import java.util.Comparator;
20import java.util.HashSet;
21import java.util.LinkedList;
22import java.util.List;
23import java.util.Set;
24import java.util.logging.Level;
25
26import de.ugoe.cs.autoquest.CommandHelpers;
27import de.ugoe.cs.autoquest.eventcore.guimodel.GUIElementFactory;
28import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
29import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
30import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementSpec;
31import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLDocument;
32import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLServer;
33import de.ugoe.cs.util.console.Command;
34import de.ugoe.cs.util.console.Console;
35import de.ugoe.cs.util.console.GlobalDataContainer;
36
37/**
38 * <p>
39 * This command condenses an HTML GUI model. For HTML GUI models, this is a special task, as, e.g.,
40 * menu structures resisting on several pages of a website are identified separately. However, for
41 * subsequent analysis, they need to be treated as identical. Therefore, this command identifies
42 * pages (i.e. documents) with equal structures and merges them. For differences in the pages, it
43 * adds groups of GUI elements indicating, which GUI element belongs to which pages and not to
44 * others.
45 * </p>
46 * <p>
47 * An example for clarification. Consider the following two pages:
48 * </p>
49 * <pre>
50 * server
51 *   |-- document1
52 *   |     \-- html
53 *   |           \-- body
54 *   |                 |-- div (id = menu)
55 *   |                 |     \-- ...
56 *   |                 |-- div (id = textcontent)
57 *   |                 |     \-- ...
58 *   |                 \-- div (id = footer)
59 *   |                       \-- ...
60 *   \-- document2
61 *         \-- html
62 *               \-- body
63 *                     |-- div (id = menu)
64 *                     |     \-- ...
65 *                     |-- div (id = imagecontent)
66 *                     |     \-- ...
67 *                     \-- div (id = footer)
68 *                           \-- ...
69 * </pre>
70 * They only differ in the central div which has the id textcontent for the first document and
71 * imagecontent for the second. The above GUI model is the result of the monitoring and parsing of
72 * the log files. This command condenses the GUI model to the following structure:
73 * <pre>
74 * server
75 *   \-- document1
76 *         \-- html
77 *               \-- body
78 *                     |-- div (id = menu)
79 *                     |     \-- ...
80 *                     |-- group (document1)
81 *                     |     \-- div (id = textcontent)
82 *                     |           \-- ...
83 *                     |-- group (document2)
84 *                     |     \-- div (id = imagecontent)
85 *                     |           \-- ...
86 *                     \-- div (id = footer)
87 *                           \-- ...
88 * </pre>
89 * <p>
90 * This now allows the menu and the footer to be treated as identical over several pages.
91 * </p>
92 * <p>
93 * If several but not all pages share similarities, the created groups refer to several documents
94 * at once.
95 * </p>
96 *
97 * @author Patrick Harms
98 * @version 1.0
99 */
100public class CMDcondenseHTMLGUIModel implements Command {
101
102    /*
103     * (non-Javadoc)
104     *
105     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
106     */
107    @Override
108    public void run(List<Object> parameters) {
109        String sequencesName;
110
111        try {
112            sequencesName = (String) parameters.get(0);
113        }
114        catch (Exception e) {
115            throw new IllegalArgumentException("illegal parameters provided: " + e);
116        }
117
118        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName + "_targets");
119        if (dataObject == null) {
120            CommandHelpers.objectNotFoundMessage(sequencesName + "_targets");
121            return;
122        }
123        if (!(dataObject instanceof GUIModel)) {
124            CommandHelpers.objectNotType(sequencesName, "GUIModel");
125            return;
126        }
127
128        GUIModel model = (GUIModel) dataObject;
129       
130        for (IGUIElement root : model.getRootElements()) {
131            if (root instanceof HTMLServer) {
132                try {
133                    mergeServer((HTMLServer) root, model);
134                }
135                catch (Exception e) {
136                    Console.printerrln("problems while condensing model of server " + root);
137                    Console.logException(e);
138                    break;
139                }
140            }
141        }
142    }
143
144    /**
145     * <p>
146     * Condenses all documents resisting on the same server. For this, the method first identifies
147     * a hierarchy of clusters of similar GUI elements. This is then used to merge similar GUI
148     * elements and to create subgroups for indicating differences between the documents.
149     * </p>
150     *
151     * @param server the server of which all documents shall be condensed
152     * @param model  the GUI model in which the server is referenced
153     */
154    private void mergeServer(HTMLServer server, GUIModel model) {
155        Console.traceln(Level.INFO, "condensing documents of " + server);
156        Console.traceln(Level.FINE, "creating cluster hierarchy of GUI elements for " + server);
157        GUIElementsCluster rootCluster = getSimilarElementClusterHierarchy(server, model);
158
159        //rootCluster.dump(System.out, "");
160           
161        Console.traceln(Level.FINE, "merging GUI elements in same clusters and creating groups");
162        mergeGUIElementsAccordingToClusters(rootCluster, model, "");
163           
164        //model.dump(System.out, "UTF-8");
165        Console.traceln(Level.INFO, "condensed documents of " + server);
166    }
167
168    /**
169     * <p>
170     * determines clusters of similar GUI elements being children of the provided server. For this,
171     * the method creates a cluster containing all children of the provided server as similar
172     * GUI elements. Usually, these are all documents on the server. It then initiates creating
173     * the cluster hierarchy by calling the recursive method
174     * {@link #addChildClusters(GUIElementsCluster, GUIModel, String)}.
175     * </p>
176     *
177     * @param server the server for which the clusters of similar children shall be determined
178     * @param model  the GUI model required for identifying children and sub children
179     *
180     * @return a GUI element cluster representing the server.
181     */
182    private GUIElementsCluster getSimilarElementClusterHierarchy(HTMLServer server, GUIModel model)
183    {
184        GUIElementsCluster cluster = new GUIElementsCluster();
185                List<IGUIElement> children = model.getChildren(server);
186
187       
188        SimilarGUIElements similarGuiElements = new SimilarGUIElements();
189       
190        for (IGUIElement child : children) {             // when starting with the root, the cluster parent is the child itself, i.e. the
191            // document. We expect all documents to be similar.
192
193            similarGuiElements.add(new SimilarGUIElement(child, child));
194        }
195       
196        cluster.addSimilarGUIElements(similarGuiElements);
197       
198        // create the cluster structure
199        addChildClusters(cluster, model, "");
200       
201        return cluster;
202    }
203   
204
205    /**
206     * <p>
207     * Determines child clusters of a cluster of given GUI elements. This method calls itself
208     * recursively for each directly identified child cluster. Furthermore, it moves child clusters
209     * to children of other child clusters if they are a subset of the clustered GUI elements.
210     * </p>
211     *
212     * @param cluster the cluster for which child clusters shall be identified
213     * @param model   the model required to be able to determine child GUI elements
214     */
215    private void addChildClusters(GUIElementsCluster cluster, GUIModel model, String indent) {
216        for (SimilarGUIElements similarGuiElements : cluster.similarChildrenGUIElements) {
217            createSubClustersForChildren(similarGuiElements, cluster, model, indent + "  ");
218        }
219       
220        for (GUIElementsCluster childCluster : cluster.childClusters) {
221            addChildClusters(childCluster, model, indent + "  ");
222        }
223       
224        createClusterHierachies(cluster);
225    }
226   
227    /**
228     * <p>
229     * for a set of similar GUI elements, it identifies similar children and clusters them. For
230     * each identified cluster, it adds a new child cluster to the provided parent cluster. GUI
231     * elements having no further children are ignored.
232     * </p>
233     *
234     * @param similarGuiElements the similar GUI elements of which the children shall be clustered
235     * @param parentCluster      the parent cluster to which newly identified cluster are added
236     *                           as children
237     * @param model              the model required to be able to determine child GUI elements
238     */
239    private void createSubClustersForChildren(SimilarGUIElements similarGuiElements,
240                                              GUIElementsCluster parentCluster,
241                                              GUIModel           model,
242                                              String             indent)
243    {
244        for (SimilarGUIElement similarGuiElement : similarGuiElements) {
245            List<IGUIElement> children = model.getChildren(similarGuiElement.similarGUIElement);
246       
247            if (children.size() > 0) {
248                for (IGUIElement child : children) {
249                    addToClusterOfSimilarElements
250                        (child, similarGuiElement.mainClusterParent, parentCluster,
251                         similarGuiElements, model);
252                }
253            }
254//            else {
255//                // search for a default cluster to add all elements to, which have no children
256//                GUIElementsCluster defaultCluster = null;
257//               
258//                for (GUIElementsCluster candidate : parentCluster.childClusters) {
259//                    if (candidate.similarChildrenGUIElements.size() == 0) {
260//                        defaultCluster = candidate;
261//                        break;
262//                    }
263//                }
264//               
265//                if (defaultCluster == null) {
266//                    defaultCluster = new GUIElementsCluster();
267//                    parentCluster.addChildCluster(defaultCluster);
268//                }
269//               
270//                defaultCluster.clusteredGUIElements.add(similarGuiElement);
271//            }
272        }
273    }
274
275    /**
276     * <p>
277     * for a given GUI element, searches the list of child clusters of the parent cluster and adds
278     * the GUI element to the cluster, if the cluster already contains a similar GUI element. If
279     * not, a new cluster is created and added to the list of known clusters.
280     * </p>
281     *
282     * @param child              the child for which the cluster is to be determined
283     * @param clusterParent      the root GUI element of the parent cluster
284     * @param parentCluster      the parent cluster whose child clusters shall be filled
285     * @param similarGuiElements the similar GUI elements currently matched to each other
286     * @param model              the GUI model required to determine children of GUI elements
287     */
288    private void addToClusterOfSimilarElements(IGUIElement        child,
289                                               IGUIElement        clusterParent,
290                                               GUIElementsCluster parentCluster,
291                                               SimilarGUIElements similarGuiElements,
292                                               GUIModel           model)
293    {
294        SimilarGUIElements matchingParents = new SimilarGUIElements();
295       
296        // determine matching parents
297        for (SimilarGUIElement similarGuiElement : similarGuiElements) {
298            for (IGUIElement candidate : model.getChildren(similarGuiElement.similarGUIElement)) {
299                IGUIElementSpec similarityCandidate = candidate.getSpecification();
300                if (similarityCandidate.getSimilarity(child.getSpecification())) {
301                    matchingParents.add(similarGuiElement);
302                    break;
303                }
304            }
305        }
306       
307        // check if an appropriate cluster exists
308        GUIElementsCluster cluster = null;
309       
310        for (GUIElementsCluster clusterCandidate : parentCluster.childClusters) {
311            if (clusterCandidate.isClusterOf(matchingParents)) {
312                cluster = clusterCandidate;
313                break;
314            }
315        }
316       
317        if (cluster == null) {
318            cluster = new GUIElementsCluster();
319            parentCluster.addChildCluster(cluster);
320            cluster.setClusteredGUIElements(matchingParents);
321        }
322       
323        cluster.addSimilarChild(child, clusterParent);
324    }
325
326    /**
327     * <p>
328     * clusters of similar children identified for a GUI element may be logical subclusters of
329     * each other. This is, e.g., the case if one cluster contains all elements being part of
330     * document1 and document2 and a further cluster contains all elements of document 1 only. In
331     * this case, the cluster of document1 is added as a child to the other cluster. In this case
332     * it is furthermore required, that a common cluster as child of the document1/document2 cluster
333     * is created carrying the common GUI elements for both clusters.
334     * </p>
335     *
336     * @param parentCluster the parent cluster for whose children the child hierarchies shall be
337     *                      created (in-out parameter, as it is changed)
338     */
339    private void createClusterHierachies(GUIElementsCluster parentCluster) {
340        GUIElementsCluster[] clustersCopy = parentCluster.childClusters.toArray
341            (new GUIElementsCluster[parentCluster.childClusters.size()]);
342       
343        // sort the array starting with the shortest cluster and ending with the longest
344        Arrays.sort(clustersCopy, new Comparator<GUIElementsCluster>() {
345            @Override
346            public int compare(GUIElementsCluster c1, GUIElementsCluster c2) {
347                return c1.clusteredGUIElements.size() - c2.clusteredGUIElements.size();
348            }
349        });
350       
351        Set<GUIElementsCluster> subClustersToHandle = new HashSet<GUIElementsCluster>();
352       
353        // now add smaller clusters to larger ones, if they are parents
354        for (int i = 0; i < (clustersCopy.length - 1); i++) {
355            GUIElementsCluster potentialChild = clustersCopy[i];
356           
357            // search for the next cluster and add the child
358            for (int j = i + 1; j < clustersCopy.length; j++) {
359                if (clustersCopy[j].isSubCluster(potentialChild)) {
360                   
361                    clustersCopy[j].addChildCluster(potentialChild);
362                    subClustersToHandle.add(clustersCopy[j]);
363                   
364                    for (int k = 0; k < parentCluster.childClusters.size(); k++) {
365                        if (parentCluster.childClusters.get(k) == potentialChild) {
366                            parentCluster.childClusters.remove(k);
367                            break;
368                        }
369                    }
370                   
371                    break;
372                }
373            }
374        }
375       
376        if (subClustersToHandle.size() > 0) {
377            // finally, for all subclusters that were changed, ensure the creation of their internal
378            // hierarchy as well
379            for (GUIElementsCluster subClusterToHandle : subClustersToHandle) {
380                // we need a dedicated common cluster --> add it
381                createClusterHierachies(subClusterToHandle);
382               
383                /*GUIElementsCluster commonCluster = new GUIElementsCluster();
384                //commonCluster.setClusteredGUIElements(subClusterToHandle.clusteredGUIElements);
385                commonCluster.similarChildrenGUIElements.addAll
386                    (subClusterToHandle.similarChildrenGUIElements);
387               
388                subClusterToHandle.similarChildrenGUIElements.clear();
389                subClusterToHandle.childClusters.add(0, commonCluster);*/
390            }
391        }
392    }
393
394    /**
395     * <p>
396     * called for each cluster to merge similar GUI elements depending on the clusters and to
397     * create GUI element groups if required. Calls itself recursively to be also applied on
398     * child clusters.
399     * </p>
400     *
401     * @param cluster the cluster of which the similar children shall be merged
402     * @param model   the model to be adapted through the merge
403     */
404    private void mergeGUIElementsAccordingToClusters(GUIElementsCluster cluster,
405                                                     GUIModel           model,
406                                                     String             indent)
407    {
408        //System.out.println(indent + "handling " + cluster);
409       
410        for (SimilarGUIElements similarGUIElements : cluster.similarChildrenGUIElements) {
411            mergeGUIElements(similarGUIElements, model, indent + "  ");
412        }
413       
414        if (cluster.childClusters.size() > 0) {
415            //System.out.println(indent + "  handling child clusters");
416           
417            for (GUIElementsCluster childCluster : cluster.childClusters) {
418                if (cluster.isDefault() || cluster.clusterParentsMatch(childCluster)) {
419                    // for default cluster or children not creating subgroups, just traverse the
420                    // cluster hierarchy
421                    mergeGUIElementsAccordingToClusters(childCluster, model, indent + "    ");
422                }
423                else {
424                    createClusterGroup(childCluster, model, indent + "    ");
425                }
426            }
427        }
428    }
429
430    /**
431     * <p>
432     * merges similar GUI elements using the provided model
433     * </p>
434     *
435     * @param similarGUIElements the GUI elements to merge
436     * @param model              the model to be used for merging the GUI elements
437     */
438    private void mergeGUIElements(SimilarGUIElements similarGUIElements,
439                                  GUIModel           model,
440                                  String             indent)
441    {
442        if (similarGUIElements.size() > 0) {
443            IGUIElement mergeResult = similarGUIElements.get(0).similarGUIElement;
444
445            while (similarGUIElements.size() > 1) {
446                //System.out.println(indent + "merging " + mergeResult + " and " +
447                //                   similarGUIElements.get(1).similarGUIElement);
448                mergeResult = model.mergeEventTargets
449                        (mergeResult, similarGUIElements.remove(1).similarGUIElement, false);
450            }
451        }
452    }
453
454    /**
455     * <p>
456     * creates a group of GUI elements to represent a cluster. Uses the provided model for group
457     * creation.
458     * </p>
459     *
460     * @param cluster the cluster for which the group shall be created
461     * @param model   the model to be used for creating the groups
462    */
463    private void createClusterGroup(GUIElementsCluster cluster, GUIModel model, String indent) {
464        //System.out.println(indent + "creating group for " + cluster);
465
466        List<IGUIElement> guiElementsToGroup = new LinkedList<IGUIElement>();
467       
468        for (SimilarGUIElements similarGUIElements : cluster.similarChildrenGUIElements) {
469            mergeGUIElements(similarGUIElements, model, indent);
470            guiElementsToGroup.addAll(similarGUIElements.toGUIElementList());
471        }
472       
473        //System.out.println(indent + "  iterating child clusters of " + cluster);
474        for (GUIElementsCluster childCluster : cluster.childClusters) {
475            if (cluster.isDefault() || cluster.clusterParentsMatch(childCluster)) {
476                // for default cluster or children not creating subgroups, just traverse the
477                // cluster hierarchy
478                mergeGUIElementsAccordingToClusters(childCluster, model, indent + "  ");
479            }
480            else {
481                createClusterGroup(childCluster, model, indent + "  ");
482            }
483           
484            if (childCluster.getGroup() != null) {
485                if (cluster.isSubCluster(childCluster)) {
486                    guiElementsToGroup.add(childCluster.getGroup());
487                }
488                else {
489                    guiElementsToGroup.add(childCluster.getGroup().getParent());
490                }
491            }
492        }
493       
494        //System.out.println(indent + "grouping: " + guiElementsToGroup);
495        IGUIElement group = model.groupEventTargets
496            (guiElementsToGroup, getGroupName(cluster), GUIElementFactory.getInstance());
497        //System.out.println(indent + "  created group for " + cluster + ": " + group);
498       
499        cluster.setGroup(group);
500    }
501
502    /**
503     * <p>
504     * determines a name for a group to be created for the provided cluster
505     * </p>
506     *
507     * @param cluster the cluster for which a group name shall be determined
508     *
509     * @return an appropriate name
510     */
511    private String getGroupName(GUIElementsCluster cluster) {
512        StringBuffer name = new StringBuffer();
513       
514        name.append("group_");
515       
516        if (cluster.clusteredGUIElements.size() > 0) {
517            List<List<String>> pathParts = new ArrayList<List<String>>();
518           
519            for (SimilarGUIElement guiElement : cluster.clusteredGUIElements) {
520                if (guiElement.mainClusterParent instanceof HTMLDocument) {
521                    String path = ((HTMLDocument) guiElement.mainClusterParent).getPath();
522                   
523                    if (path.startsWith("/")) {
524                        path = path.substring(1);
525                    }
526                   
527                    if (path.endsWith("/")) {
528                        path = path.substring(0, path.length() - 1);
529                    }
530                   
531                    String[] pathElements = path.split("/");
532                   
533                    for (int i = 0; i < pathElements.length; i++) {
534                        if (pathParts.size() <= i) {
535                            pathParts.add(new LinkedList<String>());
536                        }
537                        if (!pathParts.get(i).contains(pathElements[i])) {
538                            pathParts.get(i).add(pathElements[i]);
539                        }
540                    }
541                }
542                else {
543                    if (pathParts.size() < 1) {
544                        pathParts.add(new LinkedList<String>());
545                    }
546                    if (!pathParts.get(0).contains(guiElement.mainClusterParent.getStringIdentifier()))
547                    {
548                        pathParts.get(0).add(guiElement.mainClusterParent.getStringIdentifier());
549                    }
550                }
551            }
552           
553            for (List<String> pathPart : pathParts) {
554                name.append('/');
555                if (pathPart.size() > 1) {
556                    name.append('[');
557                }
558               
559                int index = 0;
560                for (String elem : pathPart) {
561                    if (index++ > 0) {
562                        name.append('|');
563                    }
564                    name.append(elem);
565                }
566               
567                if (pathPart.size() > 1) {
568                    name.append(']');
569                }
570            }
571        }
572        else {
573            name.append("common");
574        }
575       
576        return name.toString();
577    }
578
579    /*
580     * (non-Javadoc)
581     *
582     * @see de.ugoe.cs.util.console.Command#help()
583     */
584    @Override
585    public String help() {
586        return "condenseHTMLGUIModel <sequence>";
587    }
588
589    /**
590     * <p>
591     * represents a cluster of similar GUI elements. It consists of a list of similar GUI elements
592     * represented by the cluster somewhere in the GUI element hierarchy. It contains, furthermore,
593     * a list of GUI elements that belong to the root cluster of the cluster hierarchy, which are
594     * usually the similar documents. It also refers to child clusters, and if created, a GUI
595     * element group representing the cluster.
596     * </p>
597     */
598    private static class GUIElementsCluster {
599       
600        /**
601         * <p>
602         * the similar children on the same level in the GUI model represented through the cluster
603         * </p>
604         */
605        private List<SimilarGUIElements> similarChildrenGUIElements =
606            new LinkedList<SimilarGUIElements>();
607       
608        /**
609         * <p>
610         * the similar root GUI elements on the first level of the created hierarchy (usually
611         * the documents on a website) to which the similar GUI elements belong
612         * </p>
613         */
614        private SimilarGUIElements clusteredGUIElements = new SimilarGUIElements();
615       
616        /**
617         * <p>
618         * reference to the child clusters.
619         * </p>
620         */
621        private List<GUIElementsCluster> childClusters = new LinkedList<GUIElementsCluster>();
622       
623        /**
624         * <p>
625         * reference to the GUI element group if one is created for the cluster
626         * </p>
627         */
628        private IGUIElement group = null;
629
630        /* (non-Javadoc)
631         * @see java.lang.Object#toString()
632         */
633        @Override
634        public String toString() {
635            return getName();
636        }
637
638        /**
639         * <p>
640         * checks, if the main cluster parents, i.e., the documents of this and the provided cluster
641         * match
642         * </p>
643         *
644         * @param other the other cluster of which the main cluster parents shall be compared to
645         *              this
646         *             
647         * @return true if they match, false else
648         */
649        private boolean clusterParentsMatch(GUIElementsCluster other) {
650            // cluster parent may already be merged and therefore equals --> use system identity
651            // hash code for uniqueness
652            Set<Integer> mainClusterParents1 = new HashSet<Integer>();
653            for (SimilarGUIElement clusteredElem1 : clusteredGUIElements) {
654                mainClusterParents1.add(System.identityHashCode(clusteredElem1.mainClusterParent));
655            }
656           
657            Set<Integer> mainClusterParents2 = new HashSet<Integer>();
658            for (SimilarGUIElement clusteredElem2 : other.clusteredGUIElements) {
659                mainClusterParents2.add(System.identityHashCode(clusteredElem2.mainClusterParent));
660            }
661           
662            return mainClusterParents1.equals(mainClusterParents2);
663        }
664
665        /**
666         * <p>
667         * returns true, if this cluster is a default cluster
668         * </p>
669         *
670         * @return
671         */
672        public boolean isDefault() {
673            return clusteredGUIElements.size() <= 0;
674        }
675
676        /**
677         * <p>
678         * sets the GUI element group created for this cluster
679         * </p>
680         *
681         * @param group the GUI element group created for this cluster
682         */
683        private void setGroup(IGUIElement group) {
684            this.group = group;
685        }
686
687        /**
688         * <p>
689         * returns the GUI element group created for this cluster
690         * </p>
691         *
692         * @return the GUI element group created for this cluster
693         */
694        private IGUIElement getGroup() {
695            return group;
696        }
697
698        /**
699         * <p>
700         * checks if the given cluster should be a child of this cluster. It should be a child
701         * if its list of clustered documents is a subset of the list of clustered documents of
702         * this cluster.
703         * </p>
704         *
705         * @param potentialChild the cluster to check
706         *
707         * @return true, if the cluster should be a child, false else
708         */
709        private boolean isSubCluster(GUIElementsCluster potentialChild) {
710            return
711                (potentialChild.clusteredGUIElements.size() < clusteredGUIElements.size()) &&
712                (clusteredGUIElements.containsAll(potentialChild.clusteredGUIElements));
713        }
714
715        /**
716         * <p>
717         * sets the list of clustered GUI elements, i.e., documents of which this cluster contains
718         * similar GUI elements
719         * </p>
720         *
721         * @param clusteredGUIElements the new list of clustered GUI elements
722         */
723        private void setClusteredGUIElements(SimilarGUIElements clusteredGUIElements) {
724            this.clusteredGUIElements = clusteredGUIElements;
725        }
726
727        /**
728         * <p>
729         * adds a child cluster to this cluster
730         * </p>
731         *
732         * @param cluster the new child cluster
733         */
734        private void addChildCluster(GUIElementsCluster cluster) {
735            childClusters.add(cluster);
736        }
737
738        /**
739         * <p>
740         * adds similar GUI elements to the list of similar GUI elements
741         * </p>
742         */
743        private void addSimilarGUIElements(SimilarGUIElements similarGuiElements) {
744            similarChildrenGUIElements.add(similarGuiElements);
745        }
746
747        /**
748         * <p>
749         * determines a list of similar GUI elements to which the provided GUI element belongs.
750         * If it finds one, it adds the GUI element to that list. If not, it creates a new one.
751         * </p>
752         *
753         * @param child         the child to to be added to the list of similar GUI elements
754         * @param clusterParent the main parent of the cluster to which the GUI element belongs
755         *                      (usually the document)
756         */
757        private void addSimilarChild(IGUIElement child, IGUIElement clusterParent) {
758            SimilarGUIElements similarGUIElements = null;
759           
760            for (SimilarGUIElements candidate : similarChildrenGUIElements) {
761                if (candidate.elementsMatch(child)) {
762                    similarGUIElements = candidate;
763                }
764            }
765           
766            if (similarGUIElements == null) {
767                similarGUIElements = new SimilarGUIElements();
768                similarChildrenGUIElements.add(similarGUIElements);
769            }
770           
771            similarGUIElements.add(new SimilarGUIElement(child, clusterParent));
772        }
773
774        /**
775         * <p>
776         * checks, if this cluster is a cluster representing the provided main cluster parents
777         * </p>
778         */
779        private boolean isClusterOf(SimilarGUIElements checkedClusterParents) {
780            return (clusteredGUIElements.size() == checkedClusterParents.size()) &&
781                   (clusteredGUIElements.containsAll(checkedClusterParents));
782        }
783
784        /**
785         * <p>
786         * returns a name for this cluster
787         * </p>
788         */
789        private String getName() {
790            StringBuffer ret = new StringBuffer("cluster(");
791           
792            if (clusteredGUIElements.size() > 0) {
793                ret.append(clusteredGUIElements.get(0).similarGUIElement);
794                ret.append(" [");
795               
796                int length = ret.length();
797                for (SimilarGUIElement similarGUIElement : clusteredGUIElements) {
798                    if (ret.length() > length) {
799                        ret.append(", ");
800                    }
801
802                    ret.append(similarGUIElement.mainClusterParent);
803                }
804               
805                ret.append(']');
806            }
807            else {
808                ret.append("default");
809            }
810           
811            ret.append(")");
812           
813            return ret.toString();
814        }
815       
816        /**
817         * <p>
818         * dumps infos of this cluster to the provided stream
819         * </p>
820         */
821//        private void dump(PrintStream out, String indent) {
822//            out.print(indent);
823//            out.print(getName());
824//            out.println(" { ");
825//           
826//            if (clusteredGUIElements.size() > 0) {
827//                out.print(indent);
828//                out.println("  clustered GUIElements {");
829//
830//                for (SimilarGUIElement clusteredGUIElement : clusteredGUIElements) {
831//                    clusteredGUIElement.dump(out, indent + "    ");
832//                }
833//
834//                out.print(indent);
835//                out.println("  }");
836//            }
837//
838//            if (similarChildrenGUIElements.size() > 0) {
839//                out.print(indent);
840//                out.println("  similar children {");
841//
842//                for (SimilarGUIElements similarGuiElements : similarChildrenGUIElements) {
843//                    similarGuiElements.dump(out, indent + "    ");
844//                }
845//
846//                out.print(indent);
847//                out.println("  }");
848//            }
849//
850//            if (childClusters.size() > 0) {
851//                out.print(indent);
852//                out.println("  child clusters {");
853//
854//                for (GUIElementsCluster childCluster : childClusters) {
855//                    childCluster.dump(out, indent + "    ");
856//                }
857//
858//                out.print(indent);
859//                out.println("  }");
860//            }
861//           
862//            out.print(indent);
863//            out.println("}");
864//        }
865
866    }
867
868    /**
869     * <p>
870     * represents a list of similar GUI elements
871     * </p>
872     */
873    private static class SimilarGUIElements extends LinkedList<SimilarGUIElement> {
874
875        /**
876         * <p>
877         * default serial version UID
878         * </p>
879         */
880        private static final long serialVersionUID = 1L;
881
882        /**
883         * <p>
884         * checks if the provided GUI element is similar to at least on of the GUI elements
885         * represented by this list.
886         * </p>
887         */
888        private boolean elementsMatch(IGUIElement otherGUIElement) {
889            // it is sufficient, if one of the elements (if any) matches, as the similarity
890            // check must be transitive
891            if (size() > 0) {
892                return get(0).similarGUIElement.getSpecification().getSimilarity
893                           (otherGUIElement.getSpecification());
894            }
895            else {
896                return true;
897            }
898        }
899   
900        /**
901         * <p>
902         * transforms this list to a list of GUI elements
903         * </p>
904         */
905        private List<IGUIElement> toGUIElementList() {
906            List<IGUIElement> guiElementList = new LinkedList<IGUIElement>();
907           
908            for (SimilarGUIElement similarGUIElement : this) {
909                guiElementList.add(similarGUIElement.similarGUIElement);
910            }
911
912            return guiElementList;
913        }
914
915        /**
916         * <p>
917         * dumps infos of this list to the provided stream
918         * </p>
919         */
920//        private void dump(PrintStream out, String indent) {
921//            out.print(indent);
922//            out.print("{ ");
923//           
924//            for (int i = 0; i < super.size(); i++) {
925//                if (i > 0) {
926//                    out.print(", ");
927//                }
928//                out.print(super.get(i));
929//            }
930//           
931//            out.println(" }");
932//        }
933    }
934   
935    /**
936     * <p>
937     * represents a single similar GUI element and the main cluster parent, i.e., the document to
938     * which it belongs.
939     * </p>
940     */
941    private static class SimilarGUIElement {
942       
943        /**
944         * <p>
945         * the represented GUI element
946         * </p>
947         */
948        private IGUIElement similarGUIElement;
949       
950        /**
951         * <p>
952         * the main cluster parent, i.e., usually the document of the represented GUI element
953         * </p>
954         */
955        private IGUIElement mainClusterParent;
956       
957        /**
958         * <p>
959         * simple constructor to initialize the objects
960         * </p>
961         */
962        private SimilarGUIElement(IGUIElement similarGUIElement, IGUIElement clusterParent) {
963            this.similarGUIElement = similarGUIElement;
964            this.mainClusterParent = clusterParent;
965        }
966
967        /**
968         * <p>
969         * dumps infos about this object to the provided stream
970         * </p>
971         */
972//        private void dump(PrintStream out, String indent) {
973//            out.print(indent);
974//            out.println(this);
975//        }
976
977        /* (non-Javadoc)
978         * @see java.lang.Object#equals(java.lang.Object)
979         */
980        @Override
981        public boolean equals(Object obj) {
982            if (obj == this) {
983                return true;
984            }
985            else if (obj instanceof SimilarGUIElement) {
986                return (similarGUIElement.equals(((SimilarGUIElement) obj).similarGUIElement));
987            }
988            else {
989                return false;
990            }
991        }
992
993        /* (non-Javadoc)
994         * @see java.lang.Object#hashCode()
995         */
996        @Override
997        public int hashCode() {
998            return similarGUIElement.hashCode();
999        }
1000
1001        /* (non-Javadoc)
1002         * @see java.lang.Object#toString()
1003         */
1004        @Override
1005        public String toString() {
1006            return similarGUIElement + " (" + mainClusterParent + ")";
1007        }
1008
1009    }
1010       
1011}
Note: See TracBrowser for help on using the repository browser.