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

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