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

Last change on this file since 1146 was 1146, checked in by pharms, 11 years ago
  • complete refactoring of task tree model with a separation of task models and task instances
  • appropriate adaptation of task tree generation process
  • appropriate adaptation of commands and task tree visualization
File size: 19.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     *
136     * @param interaction1 the first interaction to compare
137     * @param interaction2 the second interaction to compare
138     * @param eventTarget  the event target on which the interactions happened (used within
139     *                     special comparisons like mouse clicks on buttons, where the coordinates
140     *                     can be ignored)
141     *
142     * @return as described
143     */
144    private TaskEquality compareInteractions(IInteraction interaction1,
145                                             IInteraction interaction2,
146                                             IEventTarget eventTarget,
147                                             TaskEquality equalityLevel)
148    {
149        TaskEquality level = equalityLevel;
150       
151        if (level == null) {
152            level = TaskEquality.LEXICALLY_EQUAL;
153        }
154       
155        if (interaction1 == interaction2) {
156            return TaskEquality.LEXICALLY_EQUAL;
157        }
158        else if ((interaction1 instanceof KeyInteraction) &&
159                 (interaction2 instanceof KeyInteraction))
160        {
161            return compareKeyInteractions
162                ((KeyInteraction) interaction1, (KeyInteraction) interaction2, level);
163        }
164        else if ((interaction1 instanceof MouseButtonInteraction) &&
165                 (interaction2 instanceof MouseButtonInteraction))
166        {
167            return compareMouseButtonInteractions
168                ((MouseButtonInteraction) interaction1, (MouseButtonInteraction) interaction2,
169                 eventTarget, level);
170        }
171        else if ((interaction1 instanceof Scroll) && (interaction2 instanceof Scroll)) {
172            return compareScrolls((Scroll) interaction1, (Scroll) interaction2, level);
173        }
174        else if ((interaction1 instanceof TextInput) && (interaction2 instanceof TextInput)) {
175            return compareTextInputs
176                ((TextInput) interaction1, (TextInput) interaction2, level);
177        }
178        else if ((interaction1 instanceof ValueSelection) &&
179                 (interaction2 instanceof ValueSelection))
180        {
181            return compareValueSelections
182                ((ValueSelection<?>) interaction1, (ValueSelection<?>) interaction2, level);
183        }
184        else if (interaction1.equals(interaction2)) {
185            return TaskEquality.LEXICALLY_EQUAL;
186        }
187        else {
188            return TaskEquality.UNEQUAL;
189        }
190    }
191
192    /**
193     * <p>
194     * TODO: comment
195     * </p>
196     *
197     * @param interaction1
198     * @param interaction2
199     * @param eventTarget
200     * @param level
201     * @return
202     */
203    private TaskEquality compareKeyInteractions(KeyInteraction interaction1,
204                                                KeyInteraction interaction2,
205                                                TaskEquality   equalityLevel)
206    {
207        if (((interaction1 instanceof KeyPressed) && (interaction2 instanceof KeyPressed)) ||
208            ((interaction1 instanceof KeyReleased) && (interaction2 instanceof KeyReleased)) ||
209            ((interaction1 instanceof KeyTyped) && (interaction2 instanceof KeyTyped)))
210        {
211            if ((equalityLevel.isAtLeast(TaskEquality.SYNTACTICALLY_EQUAL)) &&
212                (interaction1.getKey() == interaction2.getKey()))
213            {
214                return TaskEquality.LEXICALLY_EQUAL;
215            }
216            else {
217                return TaskEquality.SEMANTICALLY_EQUAL;
218            }
219        }
220       
221        return TaskEquality.UNEQUAL;
222    }
223   
224    /**
225     * <p>
226     * compares two mouse drag and drops. If both drag and drops have the same start and end
227     * coordinates, they are lexically equal. Otherwise, they are semantically equal.
228     * </p>
229     *
230     * @param interaction1 the first mouse drag and drop to compare
231     * @param interaction2 the second mouse drag and drop to compare
232     *
233     * @return as described
234     */
235    private TaskEquality compareMouseDragAndDrops(MouseDragAndDrop interaction1,
236                                                  MouseDragAndDrop interaction2,
237                                                  TaskEquality     equalityLevel)
238    {
239        if (interaction1.getButton() != interaction2.getButton()) {
240            return TaskEquality.UNEQUAL;
241        }
242       
243        if (equalityLevel.isAtLeast(TaskEquality.SYNTACTICALLY_EQUAL)) {
244            int x1 = interaction1.getX();
245            int x1Start = interaction1.getXStart();
246            int x2 = interaction2.getX();
247            int x2Start = interaction2.getXStart();
248            int y1 = interaction1.getY();
249            int y1Start = interaction1.getYStart();
250            int y2 = interaction2.getY();
251            int y2Start = interaction2.getYStart();
252       
253            if ((x1Start == x2Start) && (x1 == x2) && (y1Start == y2Start) && (y1 == y2)) {
254                return TaskEquality.LEXICALLY_EQUAL;
255            }
256        }
257       
258        return TaskEquality.SEMANTICALLY_EQUAL;
259    }
260
261    /**
262     * <p>
263     * compares two mouse button interactions such as clicks, mouse button down, or double clicks.
264     * If both interactions have the same coordinates, they are lexically equal. Otherwise, they
265     * are semantically equal. Mouse clicks for which the coordinates make no lexical difference
266     * (see {@link #clickCoordinatesMakeLexicalDifference(IEventTarget)}) are treated as
267     * lexically equal.
268     * </p>
269     *
270     * @param interaction1 the first mouse button interaction to compare
271     * @param interaction2 the second mouse button interaction to compare
272     * @param eventTarget  the event target on which the interactions happened (used within
273     *                     special comparisons like mouse clicks on buttons, where the coordinates
274     *                     can be ignored)
275     *
276     * @return as described
277     */
278    private TaskEquality compareMouseButtonInteractions(MouseButtonInteraction interaction1,
279                                                        MouseButtonInteraction interaction2,
280                                                        IEventTarget           eventTarget,
281                                                        TaskEquality           equalityLevel)
282    {
283        boolean coordinatesMatch = true;
284       
285        if ((interaction1 instanceof MouseDragAndDrop) &&
286            (interaction2 instanceof MouseDragAndDrop))
287        {
288            return compareMouseDragAndDrops
289                ((MouseDragAndDrop) interaction1, (MouseDragAndDrop) interaction2, equalityLevel);
290        }
291        else if (interaction1.getButton() != interaction2.getButton()) {
292            return TaskEquality.UNEQUAL;
293        }
294        else if (equalityLevel.isAtLeast(TaskEquality.SYNTACTICALLY_EQUAL) &&
295                 clickCoordinatesMakeLexicalDifference(eventTarget))
296        {
297            int x1 = interaction1.getX();
298            int x2 = interaction2.getX();
299            int y1 = interaction1.getY();
300            int y2 = interaction2.getY();
301
302            if ((x1 != x2) || (y1 != y2)) {
303                coordinatesMatch = false;
304            }
305        }
306       
307        // up to now, they can be equal. Now check the types. Do it as last action as these
308        // checks take the most time and should, therefore, only be done latest
309        if (((interaction1 instanceof MouseClick) && (interaction2 instanceof MouseClick)) ||
310            ((interaction1 instanceof MouseDoubleClick) &&
311             (interaction2 instanceof MouseDoubleClick)) ||
312            ((interaction1 instanceof MouseButtonDown) &&
313             (interaction2 instanceof MouseButtonDown)) ||
314            ((interaction1 instanceof MouseButtonUp) &&
315             (interaction2 instanceof MouseButtonUp)))
316        {
317            if (coordinatesMatch) {
318                return TaskEquality.LEXICALLY_EQUAL;
319            }
320            else {
321                return TaskEquality.SEMANTICALLY_EQUAL;
322            }
323        }
324       
325        return TaskEquality.UNEQUAL;
326    }
327
328    /**
329     * <p>
330     * compares two mouse button interactions such as clicks, mouse button down, or double clicks.
331     * If both interactions have the same coordinates, they are lexically equal. Otherwise, they
332     * are semantically equal. Mouse clicks for which the coordinates make no lexical difference
333     * (see {@link #clickCoordinatesMakeLexicalDifference(IEventTarget)}) are treated as
334     * lexically equal.
335     * </p>
336     *
337     * @param interaction1 the first mouse button interaction to compare
338     * @param interaction2 the second mouse button interaction to compare
339     * @param eventTarget  the event target on which the interactions happened (used within
340     *                     special comparisons like mouse clicks on buttons, where the coordinates
341     *                     can be ignored)
342     *
343     * @return as described
344     */
345    private TaskEquality compareScrolls(Scroll       interaction1,
346                                        Scroll       interaction2,
347                                        TaskEquality equalityLevel)
348    {
349        if (equalityLevel.isAtLeast(TaskEquality.SYNTACTICALLY_EQUAL)) {
350            int x1 = interaction1.getXPosition();
351            int x2 = interaction2.getXPosition();
352            int y1 = interaction1.getYPosition();
353            int y2 = interaction2.getYPosition();
354       
355            if ((x1 == x2) && (y1 == y2)) {
356                return TaskEquality.LEXICALLY_EQUAL;
357            }
358        }
359       
360        return TaskEquality.SEMANTICALLY_EQUAL;
361    }
362
363    /**
364     * <p>
365     * compares two text inputs. If both text inputs have the same entered text and text input
366     * events, they are lexically equal. If they only have the same entered text, they are
367     * syntactically equal. If they are only both text inputs, they are semantically equal.
368     * (the equality of the event targets is checked beforehand).
369     * </p>
370     *
371     * @param interaction1 the first text input to compare
372     * @param interaction2 the second text input to compare
373     *
374     * @return as described
375     */
376    private TaskEquality compareTextInputs(TextInput    interaction1,
377                                           TextInput    interaction2,
378                                           TaskEquality equalityLevel)
379    {
380        switch (equalityLevel) {
381            case LEXICALLY_EQUAL:
382                if (interaction1.getTextInputEvents().equals(interaction2.getTextInputEvents())) {
383                    return TaskEquality.LEXICALLY_EQUAL;
384                }
385                // fall through
386            case SYNTACTICALLY_EQUAL:
387                if (interaction1.getEnteredText().equals(interaction2.getEnteredText())) {
388                    return TaskEquality.SYNTACTICALLY_EQUAL;
389                }
390                // fall through
391            case SEMANTICALLY_EQUAL:
392                return TaskEquality.SEMANTICALLY_EQUAL;
393            default:
394                return TaskEquality.UNEQUAL;
395        }
396    }
397
398    /**
399     * <p>
400     * compares two value selections. If both value selections have the same selected value, they
401     * are syntactically equal, otherwise they are semantically equal.
402     * (the equality of the event targets is checked beforehand).
403     * </p>
404     *
405     * @param interaction1 the first value selection to compare
406     * @param interaction2 the second value selection to compare
407     *
408     * @return as described
409     */
410    private TaskEquality compareValueSelections(ValueSelection<?> interaction1,
411                                                ValueSelection<?> interaction2,
412                                                TaskEquality      equalityLevel)
413    {
414        if (equalityLevel.isAtLeast(TaskEquality.SYNTACTICALLY_EQUAL)) {
415            Object value1 = interaction1.getSelectedValue();
416            Object value2 = interaction2.getSelectedValue();
417       
418            if ((value1 == value2) || ((value1 != null) && (value1.equals(value2)))) {
419                return TaskEquality.LEXICALLY_EQUAL;
420            }
421        }
422       
423        return TaskEquality.SEMANTICALLY_EQUAL;
424    }
425
426    /**
427     * <p>
428     * Checks, if the coordinates of a click or double click on the provided event target makes
429     * a lexical difference. Mouse clicks and double clicks on buttons, check boxes,
430     * combo boxes, images, list boxes, menu buttons, radio buttons, shapes, uneditable text,
431     * and tool tips have no lexical difference as long as they happen on the same event target.
432     * The concrete coordinates are not relevant.
433     * </p>
434     *
435     * @param eventTarget the event target on which the interaction occurred
436     *
437     * @return if the coordinates are important to be considered for clicks and double clicks,
438     *         false else
439     */
440    private boolean clickCoordinatesMakeLexicalDifference(IEventTarget eventTarget) {
441        if ((eventTarget instanceof IButton) ||
442            (eventTarget instanceof ICheckBox) ||
443            (eventTarget instanceof IComboBox) ||
444            (eventTarget instanceof IImage) ||
445            (eventTarget instanceof IListBox) ||
446            (eventTarget instanceof IMenu) ||
447            (eventTarget instanceof IMenuButton) ||
448            (eventTarget instanceof IRadioButton) ||
449            (eventTarget instanceof IShape) ||
450            (eventTarget instanceof IText) ||
451            (eventTarget instanceof IToolTip))
452        {
453            return false;
454        }
455        else {
456            return true;
457        }
458    }
459
460}
Note: See TracBrowser for help on using the repository browser.