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

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