source: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultGuiElementSequenceDetectionRuleBad.java @ 987

Last change on this file since 987 was 987, checked in by pharms, 12 years ago
  • improvement and documentation of the creation of sub sequences based on the GUI hierarchy
File size: 25.9 KB
Line 
1package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
2
3import java.util.ArrayList;
4import java.util.List;
5
6import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
7import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
8import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
9import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
10import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
11import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
12
13/**
14 * TODO: comment or delete
15 *
16 * @version $Revision: $ $Date: 18.03.2012$
17 * @author 2012, last modified by $Author: patrick$
18 */
19public class DefaultGuiElementSequenceDetectionRuleBad implements TemporalRelationshipRule {
20
21    /**
22     *
23     */
24    private List<Class<? extends IGUIElement>> guiElementFilter;
25
26    /**
27     *
28     */
29    DefaultGuiElementSequenceDetectionRuleBad() {
30        this.guiElementFilter = null;
31    }
32
33    /**
34     *
35     */
36    DefaultGuiElementSequenceDetectionRuleBad(List<Class<? extends IGUIElement>> guiElementFilter) {
37        this.guiElementFilter = guiElementFilter;
38    }
39
40    /*
41     * (non-Javadoc)
42     *
43     * @see de.ugoe.cs.tasktree.temporalrelation.TemporalRelationshipRule#apply(TaskTreeNode,
44     * TaskTreeBuilder, TaskTreeNodeFactory)
45     */
46    @Override
47    public RuleApplicationResult apply(ITaskTreeNode        parent,
48                                       ITaskTreeBuilder     builder,
49                                       ITaskTreeNodeFactory nodeFactory,
50                                       boolean              finalize)
51    {
52        if (!(parent instanceof ISequence)) {
53            return null;
54        }
55
56        List<ITaskTreeNode> children = parent.getChildren();
57        RuleApplicationResult result = new RuleApplicationResult();
58       
59        IGUIElement lastGuiElement = null;
60        int index = 0;
61        while (index <= children.size()) {
62            ITaskTreeNode child = index < children.size() ? children.get(index) : null;
63            IGUIElement currentGuiElement = getGuiElement(child);
64            if ((index > 0) && (!guiElementsEqual(lastGuiElement, currentGuiElement))) {
65                ReducableCommonDenominator commonDenominator =
66                    getNextReducableCommonDenominator(parent, index - 1);
67               
68                RuleApplicationStatus status = handleCommonDenominator
69                    (commonDenominator, parent, index, lastGuiElement, currentGuiElement, builder,
70                     nodeFactory, finalize, result);
71               
72                if (status != null) {
73                    result.setRuleApplicationStatus(status);
74                    return result;
75                }
76                // else go on
77            }
78
79            lastGuiElement = currentGuiElement;
80            index++;
81        }
82
83        return result;
84    }
85
86    /**
87     *
88     */
89    private ReducableCommonDenominator getNextReducableCommonDenominator(ITaskTreeNode parent,
90                                                                         int           childIndex)
91    {
92        ReducableCommonDenominator commonDenominator = null;
93       
94        // a common denominator can only exist for at least two task tree nodes
95        if (childIndex > 0) {
96            // start with the last one
97            int pos = childIndex;
98
99            commonDenominator = new ReducableCommonDenominator();
100           
101            // check for further predecessors, if they match the same common denominator
102            IGUIElement currentCommonDenominator = null;
103            do {
104                if (--pos < 0) {
105                    currentCommonDenominator = null;
106                }
107                else {
108                    currentCommonDenominator = getCommonDenominator
109                        (getGuiElement(parent.getChildren().get(pos)),
110                         getGuiElement(parent.getChildren().get(pos + 1)));
111                }
112               
113                if (commonDenominator.commonGuiElement == null) {
114                    commonDenominator.commonGuiElement = currentCommonDenominator;
115                }
116            }
117            while ((commonDenominator.commonGuiElement != null) &&
118                   (commonDenominator.commonGuiElement.equals(currentCommonDenominator)));
119           
120            if (commonDenominator.commonGuiElement != null) {
121                // pos points to the last element, that has not the same common denominator.
122                // This one must be subtracted from the task number as well
123                commonDenominator.noOfTasks = childIndex - pos;
124            }
125            else {
126                commonDenominator = null;
127            }
128        }
129       
130        return commonDenominator;
131    }
132
133    /**
134     *
135     */
136    private RuleApplicationStatus handleCommonDenominator(ReducableCommonDenominator commonDenominator,
137                                                          ITaskTreeNode              parent,
138                                                          int                        currentIndex,
139                                                          IGUIElement                lastGuiElement,
140                                                          IGUIElement                currentGuiElement,
141                                                          ITaskTreeBuilder           builder,
142                                                          ITaskTreeNodeFactory       nodeFactory,
143                                                          boolean                    finalize,
144                                                          RuleApplicationResult      result)
145    {
146        List<ITaskTreeNode> children = parent.getChildren();
147       
148        boolean sequenceHasOnlyOneChild = children.size() == 1;
149        boolean reachedEndOfSequence = currentIndex == children.size();
150        boolean haveCommonDenominator = commonDenominator != null;
151        boolean allChildrenShareDenominator =
152            haveCommonDenominator && commonDenominator.noOfTasks == children.size();
153        boolean nextChildSharesDenominator =
154            haveCommonDenominator && !reachedEndOfSequence &&
155            isOnGuiElementPath(commonDenominator.commonGuiElement, currentGuiElement);
156       
157        IGUIElement denominatorOfPreviousAndCurrentChild =
158            !reachedEndOfSequence ? getCommonDenominator(lastGuiElement, currentGuiElement) : null;
159       
160        boolean previousAndCurrentChildHaveDenominator =
161            denominatorOfPreviousAndCurrentChild != null;
162
163        if (haveCommonDenominator) {
164            if (!reachedEndOfSequence) {
165                if (nextChildSharesDenominator) {
166                    // the last child, although matching the identified common denominator, may
167                    // stand on its own because it is even deeper in the hierarchy, than the
168                    // common denominator as well as the common denominator with the next child.
169                    // So there need to appropriate subsequences to distinguish the child from
170                    // the hierarchy of the next one.
171                    if (isOnGuiElementPath(denominatorOfPreviousAndCurrentChild, lastGuiElement)) {
172                        return condenseChildToSequencesRepresentingHierarchy
173                            (parent, lastGuiElement, denominatorOfPreviousAndCurrentChild,
174                             currentIndex - 1, builder, nodeFactory, result);
175                    }
176                    else {
177                        // go on
178                        return null;
179                    }
180                }
181                else {
182                    condenseTasksToSequencesRepresentingHierarchy
183                        (commonDenominator.commonGuiElement, currentGuiElement, parent,
184                         currentIndex, commonDenominator.noOfTasks, builder, nodeFactory, result);
185
186                    return RuleApplicationStatus.RULE_APPLICATION_FINISHED;
187                }
188            }
189            else {
190                // end of sequence is reached and denominator is found
191                if (!allChildrenShareDenominator) {
192                    condenseTasksToSequencesRepresentingHierarchy
193                        (commonDenominator.commonGuiElement, currentGuiElement, parent,
194                         currentIndex, commonDenominator.noOfTasks, builder, nodeFactory, result);
195
196                    return RuleApplicationStatus.RULE_APPLICATION_FINISHED;
197                }
198                else {
199                    // all children share denominator
200                    if (finalize) {
201                        if (ensureSequencesRepresentingHierarchy
202                                (parent, commonDenominator.commonGuiElement,
203                                 builder, nodeFactory, result))
204                        {
205                            return RuleApplicationStatus.RULE_APPLICATION_FINISHED;
206                        }
207                        else {
208                            return condenseChildToSequencesRepresentingHierarchy
209                                (parent, lastGuiElement, getGuiElement(parent), currentIndex - 1,
210                                 builder, nodeFactory, result);
211                        }
212                    }
213                    else {
214                        return RuleApplicationStatus.RULE_APPLICATION_FEASIBLE;
215                    }
216                }
217            }
218        }
219        else {
220            // no common denominator found
221            if (!reachedEndOfSequence) {
222                if (previousAndCurrentChildHaveDenominator) {
223                    // go on
224                    return null;
225                }
226                else {
227                    return condenseChildToSequencesRepresentingHierarchy
228                        (parent, lastGuiElement, getGuiElement(parent), currentIndex - 1,
229                         builder, nodeFactory, result);
230                }
231            }
232            else {
233                // last child has its own GUI hierarchy
234                if (sequenceHasOnlyOneChild) {
235                    if (finalize) {
236                        if (ensureSequencesRepresentingHierarchy
237                                (parent, lastGuiElement, builder, nodeFactory, result))
238                        {
239                            return RuleApplicationStatus.RULE_APPLICATION_FINISHED;
240                        }
241                        else {
242                            return RuleApplicationStatus.RULE_NOT_APPLIED;
243                        }
244                    }
245                    else {
246                        return RuleApplicationStatus.RULE_APPLICATION_FEASIBLE;
247                    }
248                }
249                else {
250                    if (finalize) {
251                        return condenseChildToSequencesRepresentingHierarchy
252                            (parent, lastGuiElement, getGuiElement(parent), currentIndex - 1,
253                             builder, nodeFactory, result);
254                    }
255                    else {
256                        return RuleApplicationStatus.RULE_APPLICATION_FEASIBLE;
257                    }
258                }
259            }
260        }
261    }
262
263    /**
264     *
265     */
266    private IGUIElement getCommonDenominator(List<IGUIElement> guiElements) {
267        IGUIElement commonDenominator = null;
268       
269        if (guiElements.size() > 0) {
270            List<IGUIElement> commonDenominatorPath = new ArrayList<IGUIElement>();
271           
272            // create a reference list using the first GUI element
273            IGUIElement guiElement = guiElements.get(0);
274            while (guiElement != null) {
275                if (guiElementMatchesConsideredTypes(guiElement)) {
276                    commonDenominatorPath.add(0, guiElement);
277                }
278                guiElement = guiElement.getParent();
279            }
280           
281            if (commonDenominatorPath.size() == 0) {
282                return null;
283            }
284           
285            // for each other GUI element, check the reference list for the first element in the
286            // path, that is not common to the current one, and delete it as well as it subsequent
287            // siblings
288            List<IGUIElement> currentPath = new ArrayList<IGUIElement>();
289            for (int i = 1; i < guiElements.size(); i++) {
290                currentPath.clear();
291                guiElement = guiElements.get(i);
292                while (guiElement != null) {
293                    if (guiElementMatchesConsideredTypes(guiElement)) {
294                        currentPath.add(0, guiElement);
295                    }
296                    guiElement = guiElement.getParent();
297                }
298               
299                // determine the index of the first unequal path element
300                int index = 0;
301                while ((index < commonDenominatorPath.size()) && (index < currentPath.size()) &&
302                        commonDenominatorPath.get(index).equals(currentPath.get(index)))
303                {
304                    index++;
305                }
306               
307                // remove all elements from the common denonimator path, that do not match
308                while (index < commonDenominatorPath.size()) {
309                    commonDenominatorPath.remove(index);
310                }
311            }
312           
313            if (commonDenominatorPath.size() > 0) {
314                commonDenominator = commonDenominatorPath.get(commonDenominatorPath.size() - 1);
315            }
316        }
317       
318        return commonDenominator;
319    }
320
321    /**
322     *
323     */
324    private IGUIElement getCommonDenominator(IGUIElement guiElement1, IGUIElement guiElement2) {
325        if ((guiElement1 == null) || (guiElement2 == null)) {
326            return null;
327        }
328   
329        List<IGUIElement> allGuiElements = new ArrayList<IGUIElement>();
330        allGuiElements.add(guiElement1);
331        allGuiElements.add(guiElement2);
332        return getCommonDenominator(allGuiElements);
333    }
334
335    /**
336     *
337     */
338    private void condenseTasksToSequencesRepresentingHierarchy(IGUIElement           lastGuiElement,
339                                                               IGUIElement           currentGuiElement,
340                                                               ITaskTreeNode         parent,
341                                                               int                   parentIndex,
342                                                               int                   noOfTasks,
343                                                               ITaskTreeBuilder      builder,
344                                                               ITaskTreeNodeFactory  nodeFactory,
345                                                               RuleApplicationResult result)
346    {
347        List<IGUIElement> lastHierarchy = getGuiElementHierarchy(lastGuiElement);
348        List<IGUIElement> currentHierarchy = getGuiElementHierarchy(currentGuiElement);
349        int index = parentIndex;
350       
351        ITaskTreeNode generatedSequence = parent;
352       
353        for (int i = 0; i < lastHierarchy.size(); i++) {
354            // add sequence for each element in the previous hierarchy, that does not occur in the
355            // current hierarchy
356            if ((currentHierarchy == null) || (i >= currentHierarchy.size()) ||
357                (!currentHierarchy.get(i).equals(lastHierarchy.get(i))))
358            {
359                condenseTasksToSequence(generatedSequence, lastHierarchy.get(i), index,
360                                        noOfTasks, builder, nodeFactory, result);
361               
362                // only in the first iteration, we condense tasks in the parent. Afterwards,
363                // we always condense all tasks in the created sequence to a new subsequence.
364                // Therefore, adapt all indexes appropriately
365                generatedSequence = generatedSequence.getChildren().get(index - noOfTasks);
366                index = noOfTasks;
367            }
368        }
369    }
370
371    /**
372     *
373     */
374    private boolean ensureSequencesRepresentingHierarchy(ITaskTreeNode         parent,
375                                                         IGUIElement           elementToRepresent,
376                                                         ITaskTreeBuilder      builder,
377                                                         ITaskTreeNodeFactory  nodeFactory,
378                                                         RuleApplicationResult result)
379    {
380        boolean applied = false;
381        IGUIElement currentlyConsideredElement = elementToRepresent;
382        IGUIElement elementRepresentedByParent = getGuiElement(parent);
383
384        while ((currentlyConsideredElement != null) &&
385               (!currentlyConsideredElement.equals(elementRepresentedByParent)))
386        {
387            condenseTasksToSequence(parent, currentlyConsideredElement, parent.getChildren().size(),
388                                    parent.getChildren().size(), builder, nodeFactory, result);
389            applied = true;
390            currentlyConsideredElement = currentlyConsideredElement.getParent();
391        }
392       
393        if (currentlyConsideredElement != null) {
394            applied |= updateDescription(parent, currentlyConsideredElement, builder);
395        }
396       
397        return applied;
398    }
399
400    /**
401     *
402     */
403    private RuleApplicationStatus condenseChildToSequencesRepresentingHierarchy(ITaskTreeNode         parent,
404                                                                                IGUIElement           childGuiElement,
405                                                                                IGUIElement           parentGuiElement,
406                                                                                int                   childIndex,
407                                                                                ITaskTreeBuilder      builder,
408                                                                                ITaskTreeNodeFactory  nodeFactory,
409                                                                                RuleApplicationResult result)
410    {
411        ITaskTreeNode child = parent.getChildren().get(childIndex);
412        boolean childIsSequence = child instanceof ISequence;
413        boolean childHasGuiElement = childGuiElement != null;
414       
415        if (childIsSequence) {
416            if (childHasGuiElement) {
417                if (updateDescription(child, childGuiElement, builder)) {
418                    return RuleApplicationStatus.RULE_APPLICATION_FINISHED;
419                }
420            }
421        }
422        else {
423            boolean applied = false;
424            IGUIElement currentlyConsideredElement = childGuiElement;
425
426            while ((currentlyConsideredElement != null) &&
427                   (!currentlyConsideredElement.equals(parentGuiElement)))
428            {
429                condenseTasksToSequence(parent, currentlyConsideredElement, childIndex + 1, 1,
430                                        builder, nodeFactory, result);
431                applied = true;
432                currentlyConsideredElement = currentlyConsideredElement.getParent();
433            }
434           
435            if (currentlyConsideredElement != null) {
436                IGUIElement parentCommonDenominator = getGuiElement(parent);
437                if (currentlyConsideredElement.equals(parentCommonDenominator)) {
438                    applied |= updateDescription(parent, currentlyConsideredElement, builder);
439                }
440            }
441
442            if (applied) {
443                return RuleApplicationStatus.RULE_APPLICATION_FINISHED;
444            }
445        }
446       
447        return null;
448    }
449
450    /**
451     * <p>
452     * condensed a subsequence of the children of the provided parent to a sequence. The
453     * subsequence in the list of children defined by the index of the succeeding child
454     * (parentIndex) and the number of tasks to be condensed (noOfTasks).
455     * </p>
456     *
457     * @param parent      the parent node whose children are to be condensed
458     * @param target      the GUI element in which all condensed interactions take place
459     * @param parentIndex the index of the child in the parent, which follows the children
460     *                    to be condensed
461     * @param noOfTasks   the number of children to be condensed
462     * @param builder     the builder to create the new subsequence
463     * @param nodeFactory the node factory to instantiate the new subsequence
464     * @param result      the result of the rule application to store newly created parent nodes
465     */
466    private void condenseTasksToSequence(ITaskTreeNode         parent,
467                                         IGUIElement           target,         
468                                         int                   parentIndex,
469                                         int                   noOfTasks,
470                                         ITaskTreeBuilder      builder,
471                                         ITaskTreeNodeFactory  nodeFactory,
472                                         RuleApplicationResult result)
473    {
474        ISequence newSequence = nodeFactory.createNewSequence();
475        updateDescription(newSequence, target, builder);
476       
477        for (int i = 0; i < noOfTasks; i++) {
478            builder.addChild(newSequence, parent.getChildren().get(parentIndex - noOfTasks));
479            // remove exactly the same number of children from the parent.
480            builder.removeChild((ISequence) parent, parentIndex - noOfTasks);
481        }
482               
483        builder.addChild((ISequence) parent, parentIndex - noOfTasks, newSequence);
484        result.addNewlyCreatedParentNode(newSequence);
485    }
486
487    /**
488     *
489     */
490    private boolean updateDescription(ITaskTreeNode    node,
491                                      IGUIElement      commonGuiElement,
492                                      ITaskTreeBuilder builder)
493    {
494        String newDescription = "interactions on " + commonGuiElement.getStringIdentifier();
495
496        String currentDescription = node.getDescription();
497       
498        if ((currentDescription == null) || (currentDescription.indexOf(newDescription) == -1)) {
499            if ((currentDescription != null) && (!"".equals(currentDescription))) {
500                newDescription = currentDescription + "; " + newDescription;
501            }
502           
503            builder.setDescription(node, newDescription);
504            return true;
505        }
506        else {
507            return false;
508        }
509    }
510
511    /**
512     *
513     */
514    private IGUIElement getGuiElement(ITaskTreeNode node) {
515        if (node != null) {
516            List<IGUIElement> terminalGuiElements = new ArrayList<IGUIElement>();
517            getTerminalGuiElements(node, terminalGuiElements);
518            return getCommonDenominator(terminalGuiElements);
519        }
520        else {
521            return null;
522        }
523    }
524       
525    /**
526     *
527     */
528    private void getTerminalGuiElements(ITaskTreeNode node, List<IGUIElement> terminalGuiElements) {
529        if (node instanceof IEventTask) {
530            if (((IEventTask) node).getEventTarget() instanceof IGUIElement) {
531                IGUIElement terminalGuiElement = (IGUIElement) ((IEventTask) node).getEventTarget();
532                terminalGuiElement =
533                    searchHierarchyForGuiElementWithConsideredType(terminalGuiElement);
534               
535                if (terminalGuiElement != null) {
536                    terminalGuiElements.add(terminalGuiElement);
537                }
538            }
539        }
540        else {
541            for (ITaskTreeNode child : node.getChildren()) {
542                getTerminalGuiElements(child, terminalGuiElements);
543            }
544        }
545    }
546
547    /**
548     *
549     */
550    private List<IGUIElement> getGuiElementHierarchy(IGUIElement guiElement) {
551        IGUIElement element = guiElement;
552       
553        if (!guiElementMatchesConsideredTypes(element)) {
554            element = searchHierarchyForGuiElementWithConsideredType(element);
555        }
556       
557        List<IGUIElement> hierarchy = new ArrayList<IGUIElement>();
558       
559        while (element != null) {
560            hierarchy.add(0, element);
561            element = searchHierarchyForGuiElementWithConsideredType(element.getParent());
562        }
563       
564        if (hierarchy.size() > 0) {
565            return hierarchy;
566        }
567        else {
568            return null;
569        }
570    }
571
572    /**
573     *
574     */
575    private IGUIElement searchHierarchyForGuiElementWithConsideredType(IGUIElement guiElement) {
576        IGUIElement returnValue = guiElement;
577       
578        while ((returnValue != null) && !guiElementMatchesConsideredTypes(returnValue)) {
579            returnValue = returnValue.getParent();
580        }
581       
582        return returnValue;
583    }
584
585    /**
586     *
587     */
588    private boolean guiElementsEqual(IGUIElement guiElement1, IGUIElement guiElement2) {
589        if (guiElement1 == null) {
590            return guiElement2 == null;
591        }
592        else {
593            return guiElement1.equals(guiElement2);
594        }
595    }
596
597    /**
598     *
599     */
600    private boolean isOnGuiElementPath(IGUIElement potentialPathElement, IGUIElement child) {
601        IGUIElement guiElement = child;
602       
603        while (guiElement != null) {
604            if (guiElement.equals(potentialPathElement)) {
605                return true;
606            }
607            guiElement = guiElement.getParent();
608        }
609       
610        return false;
611    }
612
613    /**
614     *
615     */
616    private boolean guiElementMatchesConsideredTypes(IGUIElement guiElement) {
617        if (guiElementFilter == null) {
618            return true;
619        }
620        else {
621            for (Class<? extends IGUIElement> clazz : guiElementFilter) {
622                if (clazz.isInstance(guiElement)) {
623                    return true;
624                }
625            }
626           
627            return false;
628        }
629    }
630
631    /**
632     *
633     */
634    static class ReducableCommonDenominator {
635       
636        /** the GUI element being the common denominator */
637        IGUIElement commonGuiElement;
638       
639        /** the number of tasks that match the common denominator */
640        int noOfTasks;
641
642        /* (non-Javadoc)
643         * @see java.lang.Object#toString()
644         */
645        @Override
646        public String toString() {
647            return noOfTasks + " tasks on " + commonGuiElement;
648        }
649       
650    }
651}
Note: See TracBrowser for help on using the repository browser.