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

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