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

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