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

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