source: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/TextInputDetector.java @ 927

Last change on this file since 927 was 927, checked in by sherbold, 12 years ago
  • added copyright under the Apache License, Version 2.0
File size: 12.7 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.eventcore.gui;
16
17import java.util.ArrayList;
18import java.util.LinkedList;
19import java.util.List;
20import java.util.Locale;
21
22import de.ugoe.cs.autoquest.eventcore.Event;
23import de.ugoe.cs.autoquest.eventcore.IEventTarget;
24import de.ugoe.cs.autoquest.eventcore.gui.KeyInteraction;
25import de.ugoe.cs.autoquest.eventcore.gui.KeyPressed;
26import de.ugoe.cs.autoquest.eventcore.gui.KeyReleased;
27import de.ugoe.cs.autoquest.eventcore.gui.TextInput.TextEquality;
28import de.ugoe.cs.autoquest.eventcore.guimodel.ITextArea;
29import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField;
30import de.ugoe.cs.autoquest.keyboardmaps.KeyboardMap;
31import de.ugoe.cs.autoquest.keyboardmaps.KeyboardMapFactory;
32import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
33
34/**
35 * <p>
36 * The text input detector iterates a list of events and searches for subsequent key events. Those
37 * are replaced by a single text input event representing the text entered through the key events.
38 * The replacement is only done, if the key events have a text field or text area as target
39 * </p>
40 *
41 * @version $Revision: $ $Date: 18.03.2012$
42 * @author 2012, last modified by $Author: patrick$
43 */
44public class TextInputDetector {
45
46    /** the keyboard map to use for character recognition */
47    private KeyboardMap keyboardMap = KeyboardMapFactory.createKeyboardMap(Locale.GERMAN);
48
49    /** the keys pressed in parallel */
50    List<VirtualKey> pressedKeys = new ArrayList<VirtualKey>();
51
52    private final TextEquality textEqualityType;
53
54    /**
55     * <p>
56     * Constructor. Creates a new TextInputDectector that generates {@link TextInput} with
57     * {@link TextEquality#LEXICAL} equality.
58     * </p>
59     *
60     */
61    public TextInputDetector() {
62        this(TextEquality.LEXICAL);
63    }
64
65    /**
66     * <p>
67     * Constructor. Creates a new TextInputDectector that generates {@link TextInput} with a given
68     * {@link TextEquality} type.
69     * </p>
70     *
71     * @param textEqualityType
72     *            equality type of the generated events
73     */
74    public TextInputDetector(TextEquality textEqualityType) {
75        this.textEqualityType = textEqualityType;
76    }
77
78    /**
79     * <p>
80     * in the provided list of events, this method detects any event sequences that consists of key
81     * interactions and replaces them with a single text input interaction. This contains the
82     * entered text as well as the replaced key interaction events
83     * </p>
84     *
85     * @param sequence
86     *            the event sequence to search for text input events
87     *
88     * @return the resulting sequence, in which key interactions on text fields and areas are
89     *         reduced to text input interactions
90     */
91    public List<Event> detectTextInputs(List<Event> sequence) {
92        List<Event> resultingSequence = new LinkedList<Event>();
93
94        int textEntryStartIndex = -1;
95        IEventTarget lastEventTarget = null;
96
97        int index = 0;
98        Event currentEvent = null;
99        Event textInputEvent = null;
100        while (index < sequence.size()) {
101            currentEvent = sequence.get(index);
102            textInputEvent = null;
103
104            if (isKeyInteraction(currentEvent) && isDataInputEventTarget(currentEvent.getTarget()))
105            {
106                if (textEntryStartIndex < 0) {
107                    textEntryStartIndex = index;
108                    lastEventTarget = currentEvent.getTarget();
109                }
110                else if (!lastEventTarget.equals(currentEvent.getTarget())) {
111                    textInputEvent =
112                        handleTextEntrySequence(sequence, textEntryStartIndex, index - 1,
113                                                lastEventTarget);
114
115                    textEntryStartIndex = index;
116                    lastEventTarget = currentEvent.getTarget();
117                }
118                currentEvent = null;
119            }
120            else {
121                if (textEntryStartIndex >= 0) {
122                    textInputEvent =
123                        handleTextEntrySequence(sequence, textEntryStartIndex, index - 1,
124                                                lastEventTarget);
125
126                    textEntryStartIndex = -1;
127                    lastEventTarget = null;
128                }
129
130            }
131
132            if (textInputEvent != null) {
133                resultingSequence.add(textInputEvent);
134            }
135
136            if (currentEvent != null) {
137                resultingSequence.add(currentEvent);
138            }
139
140            index++;
141        }
142
143        if (textEntryStartIndex >= 0) {
144            textInputEvent =
145                handleTextEntrySequence(sequence, textEntryStartIndex, sequence.size() - 1,
146                                        lastEventTarget);
147
148            if (textInputEvent != null) {
149                resultingSequence.add(textInputEvent);
150            }
151        }
152
153        return resultingSequence;
154    }
155
156    /**
157     * <p>
158     * returns true if the provide event is a key interaction; false else
159     * </p>
160     *
161     * @param event
162     *            the even to check
163     *
164     * @return as described
165     */
166    private boolean isKeyInteraction(Event event) {
167        return (event.getType() instanceof KeyInteraction);
168    }
169
170    /**
171     * <p>
172     * creates a single text input event as replacement for key interactions being part of the
173     * subsequence of events denoted by the start and end index in the provide event sequence. If no
174     * text was entered, because the subsequence only contained key released events, then no text
175     * input event is generated (the method returns null).
176     * </p>
177     *
178     * @param sequence
179     *            the event sequence of which the subsequence is analyzed
180     * @param startIndex
181     *            the start index in the event sequence from which the analysis should start
182     *            (inclusive)
183     * @param endIndex
184     *            the end index in the event sequence where the analysis should end (inclusive)
185     * @param eventTarget
186     *            the event target to be used for the new event
187     *
188     * @return a text input event representing the text input resulting from the events of the
189     *         provided subsequence
190     *
191     * @throws IllegalArgumentException
192     *             if the denoted subsequence contains other events than key interactions
193     */
194    private Event handleTextEntrySequence(List<Event> sequence,
195                                          int startIndex,
196                                          int endIndex,
197                                          IEventTarget eventTarget)
198    {
199        List<Event> textInputEvents = new ArrayList<Event>();
200
201        String enteredText = determineEnteredText(sequence, startIndex, endIndex, textInputEvents);
202
203        if ((enteredText != null) && (!"".equals(enteredText))) {
204            TextInput textInput = new TextInput(enteredText, textInputEvents, textEqualityType);
205            return new Event(textInput, eventTarget);
206        }
207        else {
208            return null;
209        }
210    }
211
212    /**
213     * <p>
214     * check if an event target is a data input field, i.e. a text field or a text area
215     * </p>
216     *
217     * @param eventTarget
218     *            the event target to check
219     *
220     * @return true, if it is a text field or a text area ; false else
221     */
222    private boolean isDataInputEventTarget(IEventTarget eventTarget) {
223        return ((eventTarget instanceof ITextField) || (eventTarget instanceof ITextArea));
224    }
225
226    /**
227     * <p>
228     * determines the text entered in the event subsequence denoted by the start and end index of
229     * the provided event sequence. The method records any pressed and released key interaction as
230     * well as combinations of pressed key interactions (such as shift + letter) and determines the
231     * respective character. All identified characters are then combined to the entered text. The
232     * method also identifies the usage of the back space. The enter key is ignored for text fields
233     * in which pressing the enter key results in finishing the text entry. All analyzed key
234     * interaction events are stored in the provided text input events result list. If the
235     * subsequence only contains key released events, no text was entered and the returned text is
236     * null.
237     * </p>
238     *
239     * @param sequence
240     *            the event sequence of which the subsequence is analyzed
241     * @param startIndex
242     *            the start index in the event sequence from which the analysis should start
243     *            (inclusive)
244     * @param endIndex
245     *            the end index in the event sequence where the analysis should end (inclusive)
246     * @param textInputEvents
247     *            a buffer to contain any key interaction event analyzed (in out)
248     *
249     * @return the text entered through the interaction events of the denoted subsequence
250     *
251     * @throws IllegalArgumentException
252     *             if the denoted sequence contains other events than key interactions
253     */
254    private String determineEnteredText(List<Event> sequence,
255                                        int startIndex,
256                                        int endIndex,
257                                        List<Event> textInputEvents)
258        throws IllegalArgumentException
259    {
260        Event event;
261        StringBuffer enteredText = new StringBuffer();
262
263        for (int i = startIndex; i <= endIndex; i++) {
264            event = sequence.get(i);
265
266            if (event.getType() instanceof KeyPressed || event.getType() instanceof KeyTyped) {
267                VirtualKey key = ((KeyInteraction) event.getType()).getKey();
268
269                pressedKeys.add(key);
270
271                if (key == VirtualKey.BACK_SPACE) {
272                    if (enteredText.length() > 0) {
273                        enteredText.deleteCharAt(enteredText.length() - 1);
274                    }
275                }
276                else if (key == VirtualKey.ENTER) {
277                    // text fields only contain one line of code. Therefore the return is ignored.
278                    if (!(event.getTarget() instanceof ITextField)) {
279                        enteredText.append(getCharacter(key, pressedKeys));
280                    }
281                }
282                else {
283                    char theChar = getCharacter(key, pressedKeys);
284                    if (theChar != Character.UNASSIGNED) {
285                        enteredText.append(theChar);
286                    }
287                }
288            }
289            else if (event.getType() instanceof KeyReleased || event.getType() instanceof KeyTyped)
290            {
291                pressedKeys.remove(((KeyInteraction) event.getType()).getKey());
292            }
293            else {
294                throw new IllegalArgumentException(
295                                                   "the subsequence denoted by the indexes contains other interactions than "
296                                                       + "just key strokes");
297            }
298
299            textInputEvents.add(event);
300        }
301
302        if (enteredText.length() > 0) {
303            return enteredText.toString();
304        }
305        else {
306            return null;
307        }
308    }
309
310    /**
311     * <p>
312     * determines the character matching the pressed key depending on other keys pressed in parallel
313     * such as the shift key.
314     * </p>
315     *
316     * @param key
317     *            the key for which the character shall be determined
318     * @param pressedKeys
319     *            the list of other keys pressed in parallel
320     *
321     * @return the character resulting from the combination of pressed keys
322     */
323    private char getCharacter(VirtualKey key, List<VirtualKey> pressedKeys) {
324        boolean numlock = false;
325        boolean shift = false;
326        boolean altgr = false;
327
328        for (VirtualKey pressedKey : pressedKeys) {
329            if (pressedKey.isShiftKey()) {
330                shift = !shift;
331            }
332            else if (pressedKey == VirtualKey.ALT_GRAPH) {
333                altgr = !altgr;
334            }
335            else if (pressedKey == VirtualKey.NUM_LOCK) {
336                numlock = !numlock;
337            }
338        }
339
340        return keyboardMap.getCharacterFor(key, numlock, shift, altgr, false);
341    }
342
343}
Note: See TracBrowser for help on using the repository browser.