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

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