source: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/taskequality/GUIEventTaskComparisonRule.java @ 1189

Last change on this file since 1189 was 1154, checked in by pharms, 11 years ago
  • improved java doc
File size: 22.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.tasktrees.taskequality;
16
17import de.ugoe.cs.autoquest.eventcore.IEventTarget;
18import de.ugoe.cs.autoquest.eventcore.gui.IInteraction;
19import de.ugoe.cs.autoquest.eventcore.gui.KeyInteraction;
20import de.ugoe.cs.autoquest.eventcore.gui.KeyPressed;
21import de.ugoe.cs.autoquest.eventcore.gui.KeyReleased;
22import de.ugoe.cs.autoquest.eventcore.gui.KeyTyped;
23import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonDown;
24import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction;
25import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonUp;
26import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
27import de.ugoe.cs.autoquest.eventcore.gui.MouseDoubleClick;
28import de.ugoe.cs.autoquest.eventcore.gui.MouseDragAndDrop;
29import de.ugoe.cs.autoquest.eventcore.gui.Scroll;
30import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
31import de.ugoe.cs.autoquest.eventcore.gui.ValueSelection;
32import de.ugoe.cs.autoquest.eventcore.guimodel.IButton;
33import de.ugoe.cs.autoquest.eventcore.guimodel.ICheckBox;
34import de.ugoe.cs.autoquest.eventcore.guimodel.IComboBox;
35import de.ugoe.cs.autoquest.eventcore.guimodel.IImage;
36import de.ugoe.cs.autoquest.eventcore.guimodel.IListBox;
37import de.ugoe.cs.autoquest.eventcore.guimodel.IMenu;
38import de.ugoe.cs.autoquest.eventcore.guimodel.IMenuButton;
39import de.ugoe.cs.autoquest.eventcore.guimodel.IRadioButton;
40import de.ugoe.cs.autoquest.eventcore.guimodel.IShape;
41import de.ugoe.cs.autoquest.eventcore.guimodel.IText;
42import de.ugoe.cs.autoquest.eventcore.guimodel.IToolTip;
43import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
44import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
45
46/**
47 * <p>
48 * This rule compares GUI event tasks (i.e. it is more concrete, than the
49 * {@link EventTaskComparisonRule}). Two GUI event tasks are only equal if their event type and
50 * target are equal. The returned equality is even more fine-grained for events whose type is
51 * {@link TextInput} and {@link ValueSelection}. For text inputs, lexical equality is returned if
52 * the same text is entered using the same key interactions. Syntactical equality is returned if
53 * the same text is entered using different key interactions. Semantical equality is returned if
54 * different text is entered, but into the same event target. Value selections are syntactically
55 * equal, if the same value is selected. Otherwise they are semantically equal.
56 * </p>
57 *
58 * @author Patrick Harms
59 */
60public class GUIEventTaskComparisonRule implements TaskComparisonRule {
61   
62    /* (non-Javadoc)
63     * @see NodeComparisonRule#isApplicable(ITask, ITask)
64     */
65    @Override
66    public boolean isApplicable(ITask task1, ITask task2) {
67        return
68            ((task1 instanceof IEventTask) && (task2 instanceof IEventTask) &&
69            (((IEventTask) task1).getEventType() instanceof IInteraction) &&
70            (((IEventTask) task2).getEventType() instanceof IInteraction));
71    }
72
73    /* (non-Javadoc)
74     * @see NodeComparisonRule#areLexicallyEqual(ITask, ITask)
75     */
76    @Override
77    public boolean areLexicallyEqual(ITask task1, ITask task2) {
78        TaskEquality equality = getEquality(task1, task2, TaskEquality.LEXICALLY_EQUAL);
79        return (equality != null) && (equality.isAtLeast(TaskEquality.LEXICALLY_EQUAL));
80    }
81
82    /* (non-Javadoc)
83     * @see NodeComparisonRule#areSyntacticallyEqual(ITask, ITask)
84     */
85    @Override
86    public boolean areSyntacticallyEqual(ITask task1, ITask task2) {
87        TaskEquality equality = getEquality(task1, task2, TaskEquality.SYNTACTICALLY_EQUAL);
88        return (equality != null) && (equality.isAtLeast(TaskEquality.SYNTACTICALLY_EQUAL));
89    }
90
91    /* (non-Javadoc)
92     * @see NodeComparisonRule#areSemanticallyEqual(ITask, ITask)
93     */
94    @Override
95    public boolean areSemanticallyEqual(ITask task1, ITask task2) {
96        TaskEquality equality = getEquality(task1, task2, TaskEquality.SEMANTICALLY_EQUAL);
97        return (equality != null) && (equality.isAtLeast(TaskEquality.SEMANTICALLY_EQUAL));
98    }
99
100    /* (non-Javadoc)
101     * @see NodeComparisonRule#compare(ITask, ITask)
102     */
103    @Override
104    public TaskEquality compare(ITask task1, ITask task2) {
105        return getEquality(task1, task2, null);
106    }
107
108    /**
109     *
110     */
111    private TaskEquality getEquality(ITask task1, ITask task2, TaskEquality requiredEqualityLevel) {
112        IEventTask eventTask1 = (IEventTask) task1;
113        IEventTask eventTask2 = (IEventTask) task2;
114       
115        if (!eventTask1.getEventTarget().equals(eventTask2.getEventTarget())) {
116            return TaskEquality.UNEQUAL;
117        }
118       
119        IInteraction interaction1 = (IInteraction) eventTask1.getEventType();
120        IInteraction interaction2 = (IInteraction) eventTask2.getEventType();
121       
122        return compareInteractions
123            (interaction1, interaction2, eventTask1.getEventTarget(), requiredEqualityLevel);
124    }
125
126    /**
127     * <p>
128     * compares two interactions. The method delegates to other, more specific compare method, e.g.,
129     * {@link #compareTextInputs(TextInput, TextInput)} and
130     * {@link #compareValueSelections(ValueSelection, ValueSelection)}, if any exist for the
131     * concrete interaction types. Otherwise it uses the equals method of the interactions for
132     * comparison. In this case, if the interactions equals method returns true, this method
133     * returns lexical equality.
134     * </p>
135     * <p>
136     * The provided equality level can be used to restrict the quality check to the given level.
137     * This is done for optimization purposes. The returned equality level can be at most as
138     * concrete as the provided one. If the provided one is null, it is expected to be lexical
139     * equality.
140     * </p>
141     *
142     * @param interaction1  the first interaction to compare
143     * @param interaction2  the second interaction to compare
144     * @param eventTarget   the event target on which the interactions happened (used within
145     *                      special comparisons like mouse clicks on buttons, where the coordinates
146     *                      can be ignored)
147     * @param equalityLevel the equality level to be checked for
148     *
149     * @return as described
150     */
151    private TaskEquality compareInteractions(IInteraction interaction1,
152                                             IInteraction interaction2,
153                                             IEventTarget eventTarget,
154                                             TaskEquality equalityLevel)
155    {
156        TaskEquality level = equalityLevel;
157       
158        if (level == null) {
159            level = TaskEquality.LEXICALLY_EQUAL;
160        }
161       
162        if (interaction1 == interaction2) {
163            return TaskEquality.LEXICALLY_EQUAL;
164        }
165        else if ((interaction1 instanceof KeyInteraction) &&
166                 (interaction2 instanceof KeyInteraction))
167        {
168            return compareKeyInteractions
169                ((KeyInteraction) interaction1, (KeyInteraction) interaction2, level);
170        }
171        else if ((interaction1 instanceof MouseButtonInteraction) &&
172                 (interaction2 instanceof MouseButtonInteraction))
173        {
174            return compareMouseButtonInteractions
175                ((MouseButtonInteraction) interaction1, (MouseButtonInteraction) interaction2,
176                 eventTarget, level);
177        }
178        else if ((interaction1 instanceof Scroll) && (interaction2 instanceof Scroll)) {
179            return compareScrolls((Scroll) interaction1, (Scroll) interaction2, level);
180        }
181        else if ((interaction1 instanceof TextInput) && (interaction2 instanceof TextInput)) {
182            return compareTextInputs
183                ((TextInput) interaction1, (TextInput) interaction2, level);
184        }
185        else if ((interaction1 instanceof ValueSelection) &&
186                 (interaction2 instanceof ValueSelection))
187        {
188            return compareValueSelections
189                ((ValueSelection<?>) interaction1, (ValueSelection<?>) interaction2, level);
190        }
191        else if (interaction1.equals(interaction2)) {
192            return TaskEquality.LEXICALLY_EQUAL;
193        }
194        else {
195            return TaskEquality.UNEQUAL;
196        }
197    }
198
199    /**
200     * <p>
201     * compares two key interactions. If both are of the same type and if both have the
202     * same key, they are lexically equal. If both are only of the same type, they are
203     * semantically equal. Otherwise, they are unequal.
204     * </p>
205     * <p>
206     * The provided equality level can be used to restrict the quality check to the given level.
207     * This is done for optimization purposes. The returned equality level is as concrete as
208     * the provided one. It may be more concrete if there is no difference regarding the
209     * comparison on the levels.
210     * </p>
211     *
212     * @param interaction1  the first key interaction
213     * @param interaction2  the second key interaction
214     * @param equalityLevel the equality level to be checked for
215     *
216     * @return as described
217     */
218    private TaskEquality compareKeyInteractions(KeyInteraction interaction1,
219                                                KeyInteraction interaction2,
220                                                TaskEquality   equalityLevel)
221    {
222        if (((interaction1 instanceof KeyPressed) && (interaction2 instanceof KeyPressed)) ||
223            ((interaction1 instanceof KeyReleased) && (interaction2 instanceof KeyReleased)) ||
224            ((interaction1 instanceof KeyTyped) && (interaction2 instanceof KeyTyped)))
225        {
226            if ((equalityLevel.isAtLeast(TaskEquality.SYNTACTICALLY_EQUAL)) &&
227                (interaction1.getKey() == interaction2.getKey()))
228            {
229                return TaskEquality.LEXICALLY_EQUAL;
230            }
231            else {
232                return TaskEquality.SEMANTICALLY_EQUAL;
233            }
234        }
235       
236        return TaskEquality.UNEQUAL;
237    }
238   
239    /**
240     * <p>
241     * compares two mouse drag and drops. If both drag and drops have the same start and end
242     * coordinates, they are lexically equal. Otherwise, they are semantically equal.
243     * </p>
244     * <p>
245     * The provided equality level can be used to restrict the quality check to the given level.
246     * This is done for optimization purposes. The returned equality level is as concrete as
247     * the provided one. It may be more concrete if there is no difference regarding the
248     * comparison on the levels.
249     * </p>
250     *
251     * @param interaction1  the first mouse drag and drop to compare
252     * @param interaction2  the second mouse drag and drop to compare
253     * @param equalityLevel the equality level to be checked for
254     *
255     * @return as described
256     */
257    private TaskEquality compareMouseDragAndDrops(MouseDragAndDrop interaction1,
258                                                  MouseDragAndDrop interaction2,
259                                                  TaskEquality     equalityLevel)
260    {
261        if (interaction1.getButton() != interaction2.getButton()) {
262            return TaskEquality.UNEQUAL;
263        }
264       
265        if (equalityLevel.isAtLeast(TaskEquality.SYNTACTICALLY_EQUAL)) {
266            int x1 = interaction1.getX();
267            int x1Start = interaction1.getXStart();
268            int x2 = interaction2.getX();
269            int x2Start = interaction2.getXStart();
270            int y1 = interaction1.getY();
271            int y1Start = interaction1.getYStart();
272            int y2 = interaction2.getY();
273            int y2Start = interaction2.getYStart();
274       
275            if ((x1Start == x2Start) && (x1 == x2) && (y1Start == y2Start) && (y1 == y2)) {
276                return TaskEquality.LEXICALLY_EQUAL;
277            }
278        }
279       
280        return TaskEquality.SEMANTICALLY_EQUAL;
281    }
282
283    /**
284     * <p>
285     * compares two mouse button interactions such as clicks, mouse button down, or double clicks.
286     * If both interactions have the same coordinates, they are lexically equal. Otherwise, they
287     * are semantically equal. Mouse clicks for which the coordinates make no lexical difference
288     * (see {@link #clickCoordinatesMakeLexicalDifference(IEventTarget)}) are treated as
289     * lexically equal.
290     * </p>
291     * <p>
292     * The provided equality level can be used to restrict the quality check to the given level.
293     * This is done for optimization purposes. The returned equality level is as concrete as
294     * the provided one. It may be more concrete if there is no difference regarding the
295     * comparison on the levels.
296     * </p>
297     *
298     * @param interaction1  the first mouse button interaction to compare
299     * @param interaction2  the second mouse button interaction to compare
300     * @param eventTarget   the event target on which the interactions happened (used within
301     *                      special comparisons like mouse clicks on buttons, where the coordinates
302     *                      can be ignored)
303     * @param equalityLevel the equality level to be checked for
304     *
305     * @return as described
306     */
307    private TaskEquality compareMouseButtonInteractions(MouseButtonInteraction interaction1,
308                                                        MouseButtonInteraction interaction2,
309                                                        IEventTarget           eventTarget,
310                                                        TaskEquality           equalityLevel)
311    {
312        boolean coordinatesMatch = true;
313       
314        if ((interaction1 instanceof MouseDragAndDrop) &&
315            (interaction2 instanceof MouseDragAndDrop))
316        {
317            return compareMouseDragAndDrops
318                ((MouseDragAndDrop) interaction1, (MouseDragAndDrop) interaction2, equalityLevel);
319        }
320        else if (interaction1.getButton() != interaction2.getButton()) {
321            return TaskEquality.UNEQUAL;
322        }
323        else if (equalityLevel.isAtLeast(TaskEquality.SYNTACTICALLY_EQUAL) &&
324                 clickCoordinatesMakeLexicalDifference(eventTarget))
325        {
326            int x1 = interaction1.getX();
327            int x2 = interaction2.getX();
328            int y1 = interaction1.getY();
329            int y2 = interaction2.getY();
330
331            if ((x1 != x2) || (y1 != y2)) {
332                coordinatesMatch = false;
333            }
334        }
335       
336        // up to now, they can be equal. Now check the types. Do it as last action as these
337        // checks take the most time and should, therefore, only be done latest
338        if (((interaction1 instanceof MouseClick) && (interaction2 instanceof MouseClick)) ||
339            ((interaction1 instanceof MouseDoubleClick) &&
340             (interaction2 instanceof MouseDoubleClick)) ||
341            ((interaction1 instanceof MouseButtonDown) &&
342             (interaction2 instanceof MouseButtonDown)) ||
343            ((interaction1 instanceof MouseButtonUp) &&
344             (interaction2 instanceof MouseButtonUp)))
345        {
346            if (coordinatesMatch) {
347                return TaskEquality.LEXICALLY_EQUAL;
348            }
349            else {
350                return TaskEquality.SEMANTICALLY_EQUAL;
351            }
352        }
353       
354        return TaskEquality.UNEQUAL;
355    }
356
357    /**
358     * <p>
359     * compares two mouse button interactions such as clicks, mouse button down, or double clicks.
360     * If both interactions have the same coordinates, they are lexically equal. Otherwise, they
361     * are semantically equal. Mouse clicks for which the coordinates make no lexical difference
362     * (see {@link #clickCoordinatesMakeLexicalDifference(IEventTarget)}) are treated as
363     * lexically equal.
364     * </p>
365     * <p>
366     * The provided equality level can be used to restrict the quality check to the given level.
367     * This is done for optimization purposes. The returned equality level is as concrete as
368     * the provided one. It may be more concrete if there is no difference regarding the
369     * comparison on the levels.
370     * </p>
371     *
372     * @param interaction1  the first mouse button interaction to compare
373     * @param interaction2  the second mouse button interaction to compare
374     * @param eventTarget   the event target on which the interactions happened (used within
375     *                      special comparisons like mouse clicks on buttons, where the coordinates
376     *                      can be ignored)
377     * @param equalityLevel the equality level to be checked for
378     *
379     * @return as described
380     */
381    private TaskEquality compareScrolls(Scroll       interaction1,
382                                        Scroll       interaction2,
383                                        TaskEquality equalityLevel)
384    {
385        if (equalityLevel.isAtLeast(TaskEquality.SYNTACTICALLY_EQUAL)) {
386            int x1 = interaction1.getXPosition();
387            int x2 = interaction2.getXPosition();
388            int y1 = interaction1.getYPosition();
389            int y2 = interaction2.getYPosition();
390       
391            if ((x1 == x2) && (y1 == y2)) {
392                return TaskEquality.LEXICALLY_EQUAL;
393            }
394        }
395       
396        return TaskEquality.SEMANTICALLY_EQUAL;
397    }
398
399    /**
400     * <p>
401     * compares two text inputs. If both text inputs have the same entered text and text input
402     * events, they are lexically equal. If they only have the same entered text, they are
403     * syntactically equal. If they are only both text inputs, they are semantically equal.
404     * (the equality of the event targets is checked beforehand).
405     * </p>
406     * <p>
407     * The provided equality level can be used to restrict the quality check to the given level.
408     * This is done for optimization purposes. The returned equality level is as concrete as
409     * the provided one. It may be more concrete if there is no difference regarding the
410     * comparison on the levels.
411     * </p>
412     *
413     * @param interaction1  the first text input to compare
414     * @param interaction2  the second text input to compare
415     * @param equalityLevel the equality level to be checked for
416     *
417     * @return as described
418     */
419    private TaskEquality compareTextInputs(TextInput    interaction1,
420                                           TextInput    interaction2,
421                                           TaskEquality equalityLevel)
422    {
423        switch (equalityLevel) {
424            case LEXICALLY_EQUAL:
425                if (interaction1.getTextInputEvents().equals(interaction2.getTextInputEvents())) {
426                    return TaskEquality.LEXICALLY_EQUAL;
427                }
428                // fall through
429            case SYNTACTICALLY_EQUAL:
430                if (interaction1.getEnteredText().equals(interaction2.getEnteredText())) {
431                    return TaskEquality.SYNTACTICALLY_EQUAL;
432                }
433                // fall through
434            case SEMANTICALLY_EQUAL:
435                return TaskEquality.SEMANTICALLY_EQUAL;
436            default:
437                return TaskEquality.UNEQUAL;
438        }
439    }
440
441    /**
442     * <p>
443     * compares two value selections. If both value selections have the same selected value, they
444     * are syntactically equal, otherwise they are semantically equal.
445     * (the equality of the event targets is checked beforehand).
446     * </p>
447     * <p>
448     * The provided equality level can be used to restrict the quality check to the given level.
449     * This is done for optimization purposes. The returned equality level is as concrete as
450     * the provided one. It may be more concrete if there is no difference regarding the
451     * comparison on the levels.
452     * </p>
453     *
454     * @param interaction1  the first value selection to compare
455     * @param interaction2  the second value selection to compare
456     * @param equalityLevel the equality level to be checked for
457     *
458     * @return as described
459     */
460    private TaskEquality compareValueSelections(ValueSelection<?> interaction1,
461                                                ValueSelection<?> interaction2,
462                                                TaskEquality      equalityLevel)
463    {
464        if (equalityLevel.isAtLeast(TaskEquality.SYNTACTICALLY_EQUAL)) {
465            Object value1 = interaction1.getSelectedValue();
466            Object value2 = interaction2.getSelectedValue();
467       
468            if ((value1 == value2) || ((value1 != null) && (value1.equals(value2)))) {
469                return TaskEquality.LEXICALLY_EQUAL;
470            }
471        }
472       
473        return TaskEquality.SEMANTICALLY_EQUAL;
474    }
475
476    /**
477     * <p>
478     * Checks, if the coordinates of a click or double click on the provided event target makes
479     * a lexical difference. Mouse clicks and double clicks on buttons, check boxes,
480     * combo boxes, images, list boxes, menu buttons, radio buttons, shapes, uneditable text,
481     * and tool tips have no lexical difference as long as they happen on the same event target.
482     * The concrete coordinates are not relevant.
483     * </p>
484     *
485     * @param eventTarget the event target on which the interaction occurred
486     *
487     * @return if the coordinates are important to be considered for clicks and double clicks,
488     *         false else
489     */
490    private boolean clickCoordinatesMakeLexicalDifference(IEventTarget eventTarget) {
491        if ((eventTarget instanceof IButton) ||
492            (eventTarget instanceof ICheckBox) ||
493            (eventTarget instanceof IComboBox) ||
494            (eventTarget instanceof IImage) ||
495            (eventTarget instanceof IListBox) ||
496            (eventTarget instanceof IMenu) ||
497            (eventTarget instanceof IMenuButton) ||
498            (eventTarget instanceof IRadioButton) ||
499            (eventTarget instanceof IShape) ||
500            (eventTarget instanceof IText) ||
501            (eventTarget instanceof IToolTip))
502        {
503            return false;
504        }
505        else {
506            return true;
507        }
508    }
509
510}
Note: See TracBrowser for help on using the repository browser.