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

Last change on this file since 1336 was 1336, checked in by pharms, 10 years ago
  • removed common group stuff again as it did not work with a larger data set and as it also does not help, as grouping may introduce several group layers which can still not be merged.
File size: 36.1 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        IGUIElement mergeResult = similarGUIElements.get(0).similarGUIElement;
439
440        while (similarGUIElements.size() > 1) {
441            //System.out.println(indent + "merging " + mergeResult + " and " +
442            //                   similarGUIElements.get(1).similarGUIElement);
443            mergeResult = model.mergeGUIElements
444                (mergeResult, similarGUIElements.remove(1).similarGUIElement, false);
445        }
446    }
447
448    /**
449     * <p>
450     * creates a group of GUI elements to represent a cluster. Uses the provided model for group
451     * creation.
452     * </p>
453     *
454     * @param cluster the cluster for which the group shall be created
455     * @param model   the model to be used for creating the groups
456    */
457    private void createClusterGroup(GUIElementsCluster cluster, GUIModel model, String indent) {
458        //System.out.println(indent + "creating group for " + cluster);
459
460        List<IGUIElement> guiElementsToGroup = new LinkedList<IGUIElement>();
461       
462        for (SimilarGUIElements similarGUIElements : cluster.similarChildrenGUIElements) {
463            mergeGUIElements(similarGUIElements, model, indent);
464            guiElementsToGroup.addAll(similarGUIElements.toGUIElementList());
465        }
466       
467        //System.out.println(indent + "  iterating child clusters of " + cluster);
468        for (GUIElementsCluster childCluster : cluster.childClusters) {
469            if (cluster.isDefault() || cluster.clusterParentsMatch(childCluster)) {
470                // for default cluster or children not creating subgroups, just traverse the
471                // cluster hierarchy
472                mergeGUIElementsAccordingToClusters(childCluster, model, indent + "  ");
473            }
474            else {
475                createClusterGroup(childCluster, model, indent + "  ");
476            }
477           
478            if (childCluster.getGroup() != null) {
479                if (cluster.isSubCluster(childCluster)) {
480                    guiElementsToGroup.add(childCluster.getGroup());
481                }
482                else {
483                    guiElementsToGroup.add(childCluster.getGroup().getParent());
484                }
485            }
486        }
487       
488        //System.out.println(indent + "grouping: " + guiElementsToGroup);
489        IGUIElement group = model.groupGUIElements(guiElementsToGroup, getGroupName(cluster));
490        //System.out.println(indent + "  created group for " + cluster + ": " + group);
491       
492        cluster.setGroup(group);
493    }
494
495    /**
496     * <p>
497     * determines a name for a group to be created for the provided cluster
498     * </p>
499     *
500     * @param cluster the cluster for which a group name shall be determined
501     *
502     * @return an appropriate name
503     */
504    private String getGroupName(GUIElementsCluster cluster) {
505        StringBuffer name = new StringBuffer();
506       
507        name.append("group_");
508       
509        if (cluster.clusteredGUIElements.size() > 0) {
510            for (SimilarGUIElement guiElement : cluster.clusteredGUIElements) {
511                if (guiElement.mainClusterParent instanceof HTMLDocument) {
512                    name.append(((HTMLDocument) guiElement.mainClusterParent).getPath());
513                }
514                else {
515                    name.append(guiElement.mainClusterParent.getStringIdentifier());
516                }
517            }
518        }
519        else {
520            name.append("common");
521        }
522       
523        return name.toString();
524    }
525
526    /*
527     * (non-Javadoc)
528     *
529     * @see de.ugoe.cs.util.console.Command#help()
530     */
531    @Override
532    public String help() {
533        return "condenseHTMLGUIModel <sequence>";
534    }
535
536    /**
537     * <p>
538     * represents a cluster of similar GUI elements. It consists of a list of similar GUI elements
539     * represented by the cluster somewhere in the GUI element hierarchy. It contains, furthermore,
540     * a list of GUI elements that belong to the root cluster of the cluster hierarchy, which are
541     * usually the similar documents. It also refers to child clusters, and if created, a GUI
542     * element group representing the cluster.
543     * </p>
544     */
545    private static class GUIElementsCluster {
546       
547        /**
548         * <p>
549         * the similar children on the same level in the GUI model represented through the cluster
550         * </p>
551         */
552        private List<SimilarGUIElements> similarChildrenGUIElements =
553            new LinkedList<SimilarGUIElements>();
554       
555        /**
556         * <p>
557         * the similar root GUI elements on the first level of the created hierarchy (usually
558         * the documents on a website) to which the similar GUI elements belong
559         * </p>
560         */
561        private SimilarGUIElements clusteredGUIElements = new SimilarGUIElements();
562       
563        /**
564         * <p>
565         * reference to the child clusters.
566         * </p>
567         */
568        private List<GUIElementsCluster> childClusters = new LinkedList<GUIElementsCluster>();
569       
570        /**
571         * <p>
572         * reference to the GUI element group if one is created for the cluster
573         * </p>
574         */
575        private IGUIElement group = null;
576
577        /* (non-Javadoc)
578         * @see java.lang.Object#toString()
579         */
580        @Override
581        public String toString() {
582            return getName();
583        }
584
585        /**
586         * <p>
587         * checks, if the main cluster parents, i.e., the documents of this and the provided cluster
588         * match
589         * </p>
590         *
591         * @param other the other cluster of which the main cluster parents shall be compared to
592         *              this
593         *             
594         * @return true if they match, false else
595         */
596        private boolean clusterParentsMatch(GUIElementsCluster other) {
597            // cluster parent may already be merged and therefore equals --> use system identity
598            // hash code for uniqueness
599            Set<Integer> mainClusterParents1 = new HashSet<Integer>();
600            for (SimilarGUIElement clusteredElem1 : clusteredGUIElements) {
601                mainClusterParents1.add(System.identityHashCode(clusteredElem1.mainClusterParent));
602            }
603           
604            Set<Integer> mainClusterParents2 = new HashSet<Integer>();
605            for (SimilarGUIElement clusteredElem2 : other.clusteredGUIElements) {
606                mainClusterParents2.add(System.identityHashCode(clusteredElem2.mainClusterParent));
607            }
608           
609            return mainClusterParents1.equals(mainClusterParents2);
610        }
611
612        /**
613         * <p>
614         * returns true, if this cluster is a default cluster
615         * </p>
616         *
617         * @return
618         */
619        public boolean isDefault() {
620            return clusteredGUIElements.size() <= 0;
621        }
622
623        /**
624         * <p>
625         * sets the GUI element group created for this cluster
626         * </p>
627         *
628         * @param group the GUI element group created for this cluster
629         */
630        private void setGroup(IGUIElement group) {
631            this.group = group;
632        }
633
634        /**
635         * <p>
636         * returns the GUI element group created for this cluster
637         * </p>
638         *
639         * @return the GUI element group created for this cluster
640         */
641        private IGUIElement getGroup() {
642            return group;
643        }
644
645        /**
646         * <p>
647         * checks if the given cluster should be a child of this cluster. It should be a child
648         * if its list of clustered documents is a subset of the list of clustered documents of
649         * this cluster.
650         * </p>
651         *
652         * @param potentialChild the cluster to check
653         *
654         * @return true, if the cluster should be a child, false else
655         */
656        private boolean isSubCluster(GUIElementsCluster potentialChild) {
657            return
658                (potentialChild.clusteredGUIElements.size() < clusteredGUIElements.size()) &&
659                (clusteredGUIElements.containsAll(potentialChild.clusteredGUIElements));
660        }
661
662        /**
663         * <p>
664         * sets the list of clustered GUI elements, i.e., documents of which this cluster contains
665         * similar GUI elements
666         * </p>
667         *
668         * @param clusteredGUIElements the new list of clustered GUI elements
669         */
670        private void setClusteredGUIElements(SimilarGUIElements clusteredGUIElements) {
671            this.clusteredGUIElements = clusteredGUIElements;
672        }
673
674        /**
675         * <p>
676         * adds a child cluster to this cluster
677         * </p>
678         *
679         * @param cluster the new child cluster
680         */
681        private void addChildCluster(GUIElementsCluster cluster) {
682            childClusters.add(cluster);
683        }
684
685        /**
686         * <p>
687         * adds similar GUI elements to the list of similar GUI elements
688         * </p>
689         */
690        private void addSimilarGUIElements(SimilarGUIElements similarGuiElements) {
691            similarChildrenGUIElements.add(similarGuiElements);
692        }
693
694        /**
695         * <p>
696         * determines a list of similar GUI elements to which the provided GUI element belongs.
697         * If it finds one, it adds the GUI element to that list. If not, it creates a new one.
698         * </p>
699         *
700         * @param child         the child to to be added to the list of similar GUI elements
701         * @param clusterParent the main parent of the cluster to which the GUI element belongs
702         *                      (usually the document)
703         */
704        private void addSimilarChild(IGUIElement child, IGUIElement clusterParent) {
705            SimilarGUIElements similarGUIElements = null;
706           
707            for (SimilarGUIElements candidate : similarChildrenGUIElements) {
708                if (candidate.elementsMatch(child)) {
709                    similarGUIElements = candidate;
710                }
711            }
712           
713            if (similarGUIElements == null) {
714                similarGUIElements = new SimilarGUIElements();
715                similarChildrenGUIElements.add(similarGUIElements);
716            }
717           
718            similarGUIElements.add(new SimilarGUIElement(child, clusterParent));
719        }
720
721        /**
722         * <p>
723         * checks, if this cluster is a cluster representing the provided main cluster parents
724         * </p>
725         */
726        private boolean isClusterOf(SimilarGUIElements checkedClusterParents) {
727            return (clusteredGUIElements.size() == checkedClusterParents.size()) &&
728                   (clusteredGUIElements.containsAll(checkedClusterParents));
729        }
730
731        /**
732         * <p>
733         * returns a name for this cluster
734         * </p>
735         */
736        private String getName() {
737            StringBuffer ret = new StringBuffer("cluster(");
738           
739            if (clusteredGUIElements.size() > 0) {
740                ret.append(clusteredGUIElements.get(0).similarGUIElement);
741                ret.append(" [");
742               
743                int length = ret.length();
744                for (SimilarGUIElement similarGUIElement : clusteredGUIElements) {
745                    if (ret.length() > length) {
746                        ret.append(", ");
747                    }
748
749                    ret.append(similarGUIElement.mainClusterParent);
750                }
751               
752                ret.append(']');
753            }
754            else {
755                ret.append("default");
756            }
757           
758            ret.append(")");
759           
760            return ret.toString();
761        }
762       
763        /**
764         * <p>
765         * dumps infos of this cluster to the provided stream
766         * </p>
767         */
768//        private void dump(PrintStream out, String indent) {
769//            out.print(indent);
770//            out.print(getName());
771//            out.println(" { ");
772//           
773//            if (clusteredGUIElements.size() > 0) {
774//                out.print(indent);
775//                out.println("  clustered GUIElements {");
776//
777//                for (SimilarGUIElement clusteredGUIElement : clusteredGUIElements) {
778//                    clusteredGUIElement.dump(out, indent + "    ");
779//                }
780//
781//                out.print(indent);
782//                out.println("  }");
783//            }
784//
785//            if (similarChildrenGUIElements.size() > 0) {
786//                out.print(indent);
787//                out.println("  similar children {");
788//
789//                for (SimilarGUIElements similarGuiElements : similarChildrenGUIElements) {
790//                    similarGuiElements.dump(out, indent + "    ");
791//                }
792//
793//                out.print(indent);
794//                out.println("  }");
795//            }
796//
797//            if (childClusters.size() > 0) {
798//                out.print(indent);
799//                out.println("  child clusters {");
800//
801//                for (GUIElementsCluster childCluster : childClusters) {
802//                    childCluster.dump(out, indent + "    ");
803//                }
804//
805//                out.print(indent);
806//                out.println("  }");
807//            }
808//           
809//            out.print(indent);
810//            out.println("}");
811//        }
812
813    }
814
815    /**
816     * <p>
817     * represents a list of similar GUI elements
818     * </p>
819     */
820    private static class SimilarGUIElements extends LinkedList<SimilarGUIElement> {
821
822        /**
823         * <p>
824         * default serial version UID
825         * </p>
826         */
827        private static final long serialVersionUID = 1L;
828
829        /**
830         * <p>
831         * checks if the provided GUI element is similar to at least on of the GUI elements
832         * represented by this list.
833         * </p>
834         */
835        private boolean elementsMatch(IGUIElement otherGUIElement) {
836            // it is sufficient, if one of the elements (if any) matches, as the similarity
837            // check must be transitive
838            if (size() > 0) {
839                return get(0).similarGUIElement.getSpecification().getSimilarity
840                           (otherGUIElement.getSpecification());
841            }
842            else {
843                return true;
844            }
845        }
846   
847        /**
848         * <p>
849         * transforms this list to a list of GUI elements
850         * </p>
851         */
852        private List<IGUIElement> toGUIElementList() {
853            List<IGUIElement> guiElementList = new LinkedList<IGUIElement>();
854           
855            for (SimilarGUIElement similarGUIElement : this) {
856                guiElementList.add(similarGUIElement.similarGUIElement);
857            }
858
859            return guiElementList;
860        }
861
862        /**
863         * <p>
864         * dumps infos of this list to the provided stream
865         * </p>
866         */
867//        private void dump(PrintStream out, String indent) {
868//            out.print(indent);
869//            out.print("{ ");
870//           
871//            for (int i = 0; i < super.size(); i++) {
872//                if (i > 0) {
873//                    out.print(", ");
874//                }
875//                out.print(super.get(i));
876//            }
877//           
878//            out.println(" }");
879//        }
880    }
881   
882    /**
883     * <p>
884     * represents a single similar GUI element and the main cluster parent, i.e., the document to
885     * which it belongs.
886     * </p>
887     */
888    private static class SimilarGUIElement {
889       
890        /**
891         * <p>
892         * the represented GUI element
893         * </p>
894         */
895        private IGUIElement similarGUIElement;
896       
897        /**
898         * <p>
899         * the main cluster parent, i.e., usually the document of the represented GUI element
900         * </p>
901         */
902        private IGUIElement mainClusterParent;
903       
904        /**
905         * <p>
906         * simple constructor to initialize the objects
907         * </p>
908         */
909        private SimilarGUIElement(IGUIElement similarGUIElement, IGUIElement clusterParent) {
910            this.similarGUIElement = similarGUIElement;
911            this.mainClusterParent = clusterParent;
912        }
913
914        /**
915         * <p>
916         * dumps infos about this object to the provided stream
917         * </p>
918         */
919//        private void dump(PrintStream out, String indent) {
920//            out.print(indent);
921//            out.println(this);
922//        }
923
924        /* (non-Javadoc)
925         * @see java.lang.Object#equals(java.lang.Object)
926         */
927        @Override
928        public boolean equals(Object obj) {
929            if (obj == this) {
930                return true;
931            }
932            else if (obj instanceof SimilarGUIElement) {
933                return (similarGUIElement.equals(((SimilarGUIElement) obj).similarGUIElement));
934            }
935            else {
936                return false;
937            }
938        }
939
940        /* (non-Javadoc)
941         * @see java.lang.Object#toString()
942         */
943        @Override
944        public String toString() {
945            return similarGUIElement + " (" + mainClusterParent + ")";
946        }
947    }
948       
949}
Note: See TracBrowser for help on using the repository browser.