001    /*
002     * $URL: file:///mit/6.170/groups/se64/repository/trunk/gizmoball/SwingKeyboardInput.java $
003     * $Id: SwingKeyboardInput.java 574 2007-05-14 09:48:02Z billmag $
004     */
005    
006    package gizmoball;
007    
008    import gizmoball.staffui.MagicKeyListener;
009    
010    import java.awt.event.KeyEvent;
011    import java.awt.event.KeyListener;
012    import java.util.Collections;
013    import java.util.HashMap;
014    import java.util.HashSet;
015    import java.util.Map;
016    import java.util.Set;
017    
018    
019    /**
020     * A <code>KeyboardInput</code> implementation backed by keyboard events
021     * generated by AWT.
022     * <p>
023     * Note that this class by itself does not connect its
024     * <code>KeyListener</code> to any AWT components. Instead, clients of
025     * this class should take the <code>KeyListener</code> returned by
026     * {@link #getKeyListener} and pass it to {@link
027     * java.awt.Component#addKeyListener} for all the compenents whose
028     * keyboard events they want this class to capture.
029     *
030     * @specfield keyboardTriggerGenerators :
031     *     map from pairs of integers and keyboard event types to
032     *     <code>KeyboardTriggerGenerator</code>s -
033     *     inherited from KeyboardInput; a mapping that maps each keyboard
034     *     event, specified by an ASCII character code and whether that
035     *     event is a key up or key down event, to the
036     *     <code>KeyboardTriggerGenerator</code> that is responsible for
037     *     triggering on that event
038     */
039    public final class SwingKeyboardInput implements KeyboardInput {
040    
041        // Abstraction Function:
042        // keyboardTriggerGenerators =
043        //     map of (a, e) => k where
044        //         a     : an ASCII character code
045        //         e     : a key event type
046        //         k     : keyDownTriggerGenerators[vk(a)],
047        //                     if e is a key down event
048        //                 keyUpTriggerGenerators[a],
049        //                     if e is a key up event
050        //         vk(a) : virtualKeyForAscii[a]
051        //     except betware that the k are lazily created as needed
052    
053        // Representation Invariant:
054        // keyListener != null
055        // keyDownTriggerGenerators != null
056        // keyUpTriggerGenerators != null
057        // allKeyTriggerGenerators != null
058        // The union of the sets of KeyboardTriggerGenerators in
059        //     keyDownTriggerGenerators and keyUpTriggerGenerators is the
060        //     same as the set allKeyTriggerGenerators.
061    
062    
063        /*------------------------------------------------------------------
064         * Fields
065         *----------------------------------------------------------------*/
066    
067        // The KeyListener through which this SwingKeyboardInput receives
068        // its notifications about keyboard events
069        private final KeyListener keyListener;
070    
071        // A mapping from KeyEvent virtual key codes to
072        // KeyboardTriggerGenerators that trigger on the key down event of
073        // that virtual key code.
074        private final Map<Integer, KeyboardTriggerGenerator>
075            keyDownTriggerGenerators;
076    
077        // A mapping from KeyEvent virtual key codes to
078        // KeyboardTriggerGenerators that trigger on the key up event of
079        // that virtual key code.
080        private final Map<Integer, KeyboardTriggerGenerator>
081            keyUpTriggerGenerators;
082    
083        // All the KeyboardTriggerGenerators that this KeyboardInput is
084        // managing
085        private final Set<KeyboardTriggerGenerator> allKeyTriggerGenerators;
086    
087        // The mapping from ASCII character codes to KeyEvent.VK_* virtual
088        // key codes that they correspond to. To find the appropriate
089        // virtual key code for ASCII character code a, use
090        // virtualKeyForAscii[a]. Note that ASCII character codes that
091        // aren't mapped to any actual key are mapped to VK_UNDEFINED.
092        private static final int[] virtualKeyForAscii = {
093            KeyEvent.VK_UNDEFINED,          //   0: null character
094            KeyEvent.VK_UNDEFINED,          //   1: start of header
095            KeyEvent.VK_UNDEFINED,          //   2: start of text
096            KeyEvent.VK_UNDEFINED,          //   3: end of text
097            KeyEvent.VK_UNDEFINED,          //   4: end of transmission
098            KeyEvent.VK_UNDEFINED,          //   5: enquiry
099            KeyEvent.VK_UNDEFINED,          //   6: acknowledgment
100            KeyEvent.VK_UNDEFINED,          //   7: bell
101            KeyEvent.VK_BACK_SPACE,         //   8: backspace
102            KeyEvent.VK_TAB,                //   9: horizontal tab
103                // XXX: Tab doesn't work because it's used to change the
104                // control that has keyboard focus...
105            KeyEvent.VK_ENTER,              //  10: line feed
106            KeyEvent.VK_UNDEFINED,          //  11: vertical tab
107            KeyEvent.VK_UNDEFINED,          //  12: form feed
108            KeyEvent.VK_ENTER,              //  13: carriage return
109            KeyEvent.VK_UNDEFINED,          //  14: shift out
110            KeyEvent.VK_UNDEFINED,          //  15: shift in
111            KeyEvent.VK_UNDEFINED,          //  16: data link escape
112            KeyEvent.VK_UNDEFINED,          //  17: device control 1
113            KeyEvent.VK_UNDEFINED,          //  18: device control 2
114            KeyEvent.VK_UNDEFINED,          //  19: device control 3
115            KeyEvent.VK_UNDEFINED,          //  20: device control 4
116            KeyEvent.VK_UNDEFINED,          //  21: negative acknowledgment
117            KeyEvent.VK_UNDEFINED,          //  22: synchronous idle
118            KeyEvent.VK_UNDEFINED,          //  23: end of transmission block
119            KeyEvent.VK_UNDEFINED,          //  24: cancel
120            KeyEvent.VK_UNDEFINED,          //  25: end of medium
121            KeyEvent.VK_UNDEFINED,          //  26: substitute
122            KeyEvent.VK_ESCAPE,             //  27: escape
123            KeyEvent.VK_UNDEFINED,          //  28: file separator
124            KeyEvent.VK_UNDEFINED,          //  29: group separator
125            KeyEvent.VK_UNDEFINED,          //  30: record separator
126            KeyEvent.VK_UNDEFINED,          //  31: unit separator
127            KeyEvent.VK_SPACE,              //  32: space
128            KeyEvent.VK_1,                  //  33: !
129            KeyEvent.VK_QUOTE,              //  34: "
130            KeyEvent.VK_2,                  //  35: #
131            KeyEvent.VK_4,                  //  36: $
132            KeyEvent.VK_5,                  //  37: %
133            KeyEvent.VK_7,                  //  38: &
134            KeyEvent.VK_QUOTE,              //  39: '
135            KeyEvent.VK_9,                  //  40: (
136            KeyEvent.VK_0,                  //  41: )
137            KeyEvent.VK_8,                  //  42: *
138            KeyEvent.VK_PLUS,               //  43: +
139            KeyEvent.VK_COMMA,              //  44: ,
140            KeyEvent.VK_MINUS,              //  45: -
141            KeyEvent.VK_PERIOD,             //  46: .
142            KeyEvent.VK_SLASH,              //  47: /
143            KeyEvent.VK_0,                  //  48: 0
144            KeyEvent.VK_1,                  //  49: 1
145            KeyEvent.VK_2,                  //  50: 2
146            KeyEvent.VK_3,                  //  51: 3
147            KeyEvent.VK_4,                  //  52: 4
148            KeyEvent.VK_5,                  //  53: 5
149            KeyEvent.VK_6,                  //  54: 6
150            KeyEvent.VK_7,                  //  55: 7
151            KeyEvent.VK_8,                  //  56: 8
152            KeyEvent.VK_9,                  //  57: 9
153            KeyEvent.VK_SEMICOLON,          //  58: :
154            KeyEvent.VK_SEMICOLON,          //  59: ;
155            KeyEvent.VK_COMMA,              //  60: <
156            KeyEvent.VK_EQUALS,             //  61: =
157            KeyEvent.VK_PERIOD,             //  62: >
158            KeyEvent.VK_SLASH,              //  63: ?
159            KeyEvent.VK_2,                  //  64: @
160            KeyEvent.VK_A,                  //  65: A
161            KeyEvent.VK_B,                  //  66: B
162            KeyEvent.VK_C,                  //  67: C
163            KeyEvent.VK_D,                  //  68: D
164            KeyEvent.VK_E,                  //  69: E
165            KeyEvent.VK_F,                  //  70: F
166            KeyEvent.VK_G,                  //  71: G
167            KeyEvent.VK_H,                  //  72: H
168            KeyEvent.VK_I,                  //  73: I
169            KeyEvent.VK_J,                  //  74: J
170            KeyEvent.VK_K,                  //  75: K
171            KeyEvent.VK_L,                  //  76: L
172            KeyEvent.VK_M,                  //  77: M
173            KeyEvent.VK_N,                  //  78: N
174            KeyEvent.VK_O,                  //  79: O
175            KeyEvent.VK_P,                  //  80: P
176            KeyEvent.VK_Q,                  //  81: Q
177            KeyEvent.VK_R,                  //  82: R
178            KeyEvent.VK_S,                  //  83: S
179            KeyEvent.VK_T,                  //  84: T
180            KeyEvent.VK_U,                  //  85: U
181            KeyEvent.VK_V,                  //  86: V
182            KeyEvent.VK_W,                  //  87: W
183            KeyEvent.VK_X,                  //  88: X
184            KeyEvent.VK_Y,                  //  89: Y
185            KeyEvent.VK_Z,                  //  90: Z
186            KeyEvent.VK_OPEN_BRACKET,       //  91: [
187            KeyEvent.VK_BACK_SLASH,         //  92: \
188            KeyEvent.VK_CLOSE_BRACKET,      //  93: ]
189            KeyEvent.VK_6,                  //  94: ^
190            KeyEvent.VK_MINUS,              //  95: _
191            KeyEvent.VK_BACK_QUOTE,         //  96: `
192            KeyEvent.VK_A,                  //  97: a
193            KeyEvent.VK_B,                  //  98: b
194            KeyEvent.VK_C,                  //  99: c
195            KeyEvent.VK_D,                  // 100: d
196            KeyEvent.VK_E,                  // 101: e
197            KeyEvent.VK_F,                  // 102: f
198            KeyEvent.VK_G,                  // 103: g
199            KeyEvent.VK_H,                  // 104: h
200            KeyEvent.VK_I,                  // 105: i
201            KeyEvent.VK_J,                  // 106: j
202            KeyEvent.VK_K,                  // 107: k
203            KeyEvent.VK_L,                  // 108: l
204            KeyEvent.VK_M,                  // 109: m
205            KeyEvent.VK_N,                  // 110: n
206            KeyEvent.VK_O,                  // 111: o
207            KeyEvent.VK_P,                  // 112: p
208            KeyEvent.VK_Q,                  // 113: q
209            KeyEvent.VK_R,                  // 114: r
210            KeyEvent.VK_S,                  // 115: s
211            KeyEvent.VK_T,                  // 116: t
212            KeyEvent.VK_U,                  // 117: u
213            KeyEvent.VK_V,                  // 118: v
214            KeyEvent.VK_W,                  // 119: w
215            KeyEvent.VK_X,                  // 120: x
216            KeyEvent.VK_Y,                  // 121: y
217            KeyEvent.VK_Z,                  // 122: z
218            KeyEvent.VK_OPEN_BRACKET,       // 123: {
219            KeyEvent.VK_BACK_SLASH,         // 124: |
220            KeyEvent.VK_CLOSE_BRACKET,      // 125: }
221            KeyEvent.VK_BACK_QUOTE,         // 126: ~
222            KeyEvent.VK_DELETE,             // 127: delete
223        };
224    
225        // The mapping from KeyEvent.VK_* virtual key codes to the canonical
226        // ASCII character code that is associated with that key
227        private static final Map<Integer, Integer> asciiForVirtualKey;
228        static {
229            asciiForVirtualKey = new HashMap<Integer, Integer>();
230            asciiForVirtualKey.put(KeyEvent.VK_UNDEFINED, 0);
231            asciiForVirtualKey.put(KeyEvent.VK_BACK_SPACE, (int)'\b');
232            asciiForVirtualKey.put(KeyEvent.VK_TAB, (int)'\t');
233            asciiForVirtualKey.put(KeyEvent.VK_ENTER, (int)'\n');
234            asciiForVirtualKey.put(KeyEvent.VK_ESCAPE, 27);
235            asciiForVirtualKey.put(KeyEvent.VK_SPACE, (int)' ');
236            asciiForVirtualKey.put(KeyEvent.VK_QUOTE, (int)'\'');
237            asciiForVirtualKey.put(KeyEvent.VK_PLUS, (int)'+');
238            asciiForVirtualKey.put(KeyEvent.VK_COMMA, (int)',');
239            asciiForVirtualKey.put(KeyEvent.VK_MINUS, (int)'-');
240            asciiForVirtualKey.put(KeyEvent.VK_PERIOD, (int)'.');
241            asciiForVirtualKey.put(KeyEvent.VK_SLASH, (int)'/');
242            asciiForVirtualKey.put(KeyEvent.VK_0, (int)'0');
243            asciiForVirtualKey.put(KeyEvent.VK_1, (int)'1');
244            asciiForVirtualKey.put(KeyEvent.VK_2, (int)'2');
245            asciiForVirtualKey.put(KeyEvent.VK_3, (int)'3');
246            asciiForVirtualKey.put(KeyEvent.VK_4, (int)'4');
247            asciiForVirtualKey.put(KeyEvent.VK_5, (int)'5');
248            asciiForVirtualKey.put(KeyEvent.VK_6, (int)'6');
249            asciiForVirtualKey.put(KeyEvent.VK_7, (int)'7');
250            asciiForVirtualKey.put(KeyEvent.VK_8, (int)'8');
251            asciiForVirtualKey.put(KeyEvent.VK_9, (int)'9');
252            asciiForVirtualKey.put(KeyEvent.VK_SEMICOLON, (int)';');
253            asciiForVirtualKey.put(KeyEvent.VK_EQUALS, (int)'=');
254            asciiForVirtualKey.put(KeyEvent.VK_A, (int)'A');
255            asciiForVirtualKey.put(KeyEvent.VK_B, (int)'B');
256            asciiForVirtualKey.put(KeyEvent.VK_C, (int)'C');
257            asciiForVirtualKey.put(KeyEvent.VK_D, (int)'D');
258            asciiForVirtualKey.put(KeyEvent.VK_E, (int)'E');
259            asciiForVirtualKey.put(KeyEvent.VK_F, (int)'F');
260            asciiForVirtualKey.put(KeyEvent.VK_G, (int)'G');
261            asciiForVirtualKey.put(KeyEvent.VK_H, (int)'H');
262            asciiForVirtualKey.put(KeyEvent.VK_I, (int)'I');
263            asciiForVirtualKey.put(KeyEvent.VK_J, (int)'J');
264            asciiForVirtualKey.put(KeyEvent.VK_K, (int)'K');
265            asciiForVirtualKey.put(KeyEvent.VK_L, (int)'L');
266            asciiForVirtualKey.put(KeyEvent.VK_M, (int)'M');
267            asciiForVirtualKey.put(KeyEvent.VK_N, (int)'N');
268            asciiForVirtualKey.put(KeyEvent.VK_O, (int)'O');
269            asciiForVirtualKey.put(KeyEvent.VK_P, (int)'P');
270            asciiForVirtualKey.put(KeyEvent.VK_Q, (int)'Q');
271            asciiForVirtualKey.put(KeyEvent.VK_R, (int)'R');
272            asciiForVirtualKey.put(KeyEvent.VK_S, (int)'S');
273            asciiForVirtualKey.put(KeyEvent.VK_T, (int)'T');
274            asciiForVirtualKey.put(KeyEvent.VK_U, (int)'U');
275            asciiForVirtualKey.put(KeyEvent.VK_V, (int)'V');
276            asciiForVirtualKey.put(KeyEvent.VK_W, (int)'W');
277            asciiForVirtualKey.put(KeyEvent.VK_X, (int)'X');
278            asciiForVirtualKey.put(KeyEvent.VK_Y, (int)'Y');
279            asciiForVirtualKey.put(KeyEvent.VK_Z, (int)'Z');
280            asciiForVirtualKey.put(KeyEvent.VK_OPEN_BRACKET, (int)'[');
281            asciiForVirtualKey.put(KeyEvent.VK_BACK_SLASH, (int)'\\');
282            asciiForVirtualKey.put(KeyEvent.VK_CLOSE_BRACKET, (int)']');
283            asciiForVirtualKey.put(KeyEvent.VK_BACK_QUOTE, (int)'`');
284            asciiForVirtualKey.put(KeyEvent.VK_DELETE, 127);
285        }
286    
287        // Debugging output level. Follows the semantics defined by
288        // DebugUtils.printMessage.
289        private static final int debug = 0;
290    
291        // Whether to run expensive checkRep() tests.
292        private static final boolean runExpensiveCheckRepTests = false;
293    
294        /**
295         * Class revision identifier. <code>rcsid</code> contains an RCS Id
296         * keyword string that may be used to identify which revision of
297         * <code>SwingKeyboardInput.java</code> was used to generate an
298         * instance of <code>SwingKeyboardInput.class</code>. To identify
299         * the revision of a <code>SwingKeyboardInput.class</code> file, run
300         * "<code>ident SwingKeyboardInput.class</code>" (<code>ident</code>
301         * is part of the <a
302         * href="http://www.cs.purdue.edu/homes/trinkle/RCS/">RCS</a>
303         * software package).
304         */
305        public static final String rcsid = "$Id: SwingKeyboardInput.java 574 2007-05-14 09:48:02Z billmag $";
306    
307    
308        /*------------------------------------------------------------------
309         * Constructors
310         *----------------------------------------------------------------*/
311    
312        /**
313         * Constructs a new <code>SwingKeyboardInput</code> object.
314         */
315        public SwingKeyboardInput() {
316            keyListener = new MagicKeyListener(new MyKeyListener());
317            keyDownTriggerGenerators =
318                new HashMap<Integer, KeyboardTriggerGenerator>();
319            keyUpTriggerGenerators =
320                new HashMap<Integer, KeyboardTriggerGenerator>();
321            allKeyTriggerGenerators = new HashSet<KeyboardTriggerGenerator>();
322    
323            assert checkRep();
324        }
325    
326    
327        /*------------------------------------------------------------------
328         * checkRep()
329         *----------------------------------------------------------------*/
330    
331        // Checks that the representation invariant has not been violated.
332        // Returns true if the representation invariant holds. Throws a
333        // RuntimeException if the representation invariant does not hold.
334        private boolean checkRep() {
335            if (keyListener == null) {
336                throw new RuntimeException("keyListener was null");
337            } else if (keyDownTriggerGenerators == null) {
338                throw new RuntimeException("keyDownTriggerGenerators was null");
339            } else if (keyUpTriggerGenerators == null) {
340                throw new RuntimeException("keyUpTriggerGenerators was null");
341            } else if (allKeyTriggerGenerators == null) {
342                throw new RuntimeException("allKeyTriggerGenerators was null");
343            }
344    
345            if (runExpensiveCheckRepTests) {
346                if (!allKeyTriggerGenerators.containsAll(
347                        keyDownTriggerGenerators.values())) {
348                    throw new RuntimeException(
349                        "keyDownTriggerGenerators contains " +
350                        "KeyboardTriggerGenerators not in " +
351                        "allKeyTriggerGenerators");
352                } else if (!allKeyTriggerGenerators.containsAll(
353                        keyDownTriggerGenerators.values())) {
354                    throw new RuntimeException(
355                        "keyUpTriggerGenerators contains " +
356                        "KeyboardTriggerGenerators not in " +
357                        "allKeyTriggerGenerators");
358                }
359    
360                for (KeyboardTriggerGenerator k : allKeyTriggerGenerators) {
361                    if (!(keyDownTriggerGenerators.containsValue(k) ||
362                          keyUpTriggerGenerators.containsValue(k))) {
363                        throw new RuntimeException(k + " not found in either map");
364                    }
365                }
366            }
367    
368            return true;
369        }
370    
371    
372        /*------------------------------------------------------------------
373         * Methods inherited from KeyboardInput
374         *----------------------------------------------------------------*/
375    
376        /**
377         * {@inheritDoc}
378         */
379        public KeyboardTriggerGenerator getKeyboardTriggerGenerator(
380                int asciiCode, EventType eventType) {
381            assert checkRep();
382    
383            if (asciiCode < 0 || asciiCode > 127) {
384                throw new IllegalArgumentException(asciiCode +
385                    " passed as argument asciiCode is out of range (0-127)");
386            } else if (eventType == null) {
387                throw new NullPointerException(
388                    "null passed as argument eventType");
389            }
390    
391            Map<Integer, KeyboardTriggerGenerator> ktgMapOfInterest;
392            if (eventType == EventType.KEY_DOWN) {
393                ktgMapOfInterest = keyDownTriggerGenerators;
394            } else {
395                assert eventType == EventType.KEY_UP;
396                ktgMapOfInterest = keyUpTriggerGenerators;
397            }
398    
399            int virtualKeyCode = virtualKeyForAscii[asciiCode];
400    
401            KeyboardTriggerGenerator ktg = ktgMapOfInterest.get(virtualKeyCode);
402            if (ktg == null) {
403                // This KTG not used before; need to create a new one
404                ktg = new KeyboardTriggerGenerator(
405                        asciiForVirtualKey.get(virtualKeyCode), eventType);
406                ktgMapOfInterest.put(virtualKeyCode, ktg);
407                allKeyTriggerGenerators.add(ktg);
408            }
409    
410            assert checkRep();
411            return ktg;
412        }
413    
414        /**
415         * {@inheritDoc}
416         */
417        public Set<KeyboardTriggerGenerator> getAllKeyboardTriggerGenerators() {
418            assert checkRep();
419    
420            Set<KeyboardTriggerGenerator> returnSet =
421                Collections.unmodifiableSet(allKeyTriggerGenerators);
422    
423            assert checkRep();
424            return returnSet;
425        }
426    
427    
428        /*------------------------------------------------------------------
429         * Methods specific to SwingKeyboardInput
430         *----------------------------------------------------------------*/
431    
432        /**
433         * Returns the <code>KeyListener</code> through which this
434         * <code>SwingKeyboardInput</code> expects to receive keyboard
435         * events. Clients of this class should pass the returned
436         * <code>KeyListener</code> to the {@link
437         * java.awt.Component#addKeyListener} method of any AWT component
438         * whose key events this <code>SwingKeyboardInput</code> should
439         * receive.
440         *
441         * @return the <code>KeyListener</code> through which this
442         *         <code>SwingKeyboardInput</code> expects to receive
443         *         keyboard events.
444         */
445        public KeyListener getKeyListener() {
446            assert checkRep();
447            return keyListener;
448        }
449    
450    
451        /*------------------------------------------------------------------
452         * private class MyKeyListener
453         *----------------------------------------------------------------*/
454    
455        // A KeyListener that receives keyboard events and dispatches them
456        // to the propoer KeyboardTriggerGenerator.
457        private class MyKeyListener implements KeyListener {
458    
459            // Activates the KeyboardTriggerGenerator associated with the
460            // KeyEvent e, if one exists. Called on key down events.
461            public void keyPressed(KeyEvent e) {
462                if (debug >= 2) {
463                    DebugUtils.printMessage(this, 2, "Pressed event registered " +
464                        "with key code + " + e.getKeyCode());
465                }
466    
467                KeyboardTriggerGenerator ktg =
468                    keyDownTriggerGenerators.get(e.getKeyCode());
469    
470                if (ktg != null) {
471                    ktg.activate();
472                }
473            }
474    
475            // Activates the KeyboardTriggerGenerator associated with the
476            // KeyEvent e, if one exists. Called on key up events.
477            public void keyReleased(KeyEvent e) {
478                if (debug >= 2) {
479                    DebugUtils.printMessage(this, 2, "Released event registered " +
480                        "with key code + " + e.getKeyCode());
481                }
482    
483                KeyboardTriggerGenerator ktg =
484                    keyUpTriggerGenerators.get(e.getKeyCode());
485    
486                if (ktg != null) {
487                    ktg.activate();
488                }
489            }
490    
491            // Does nothing, since we're interested in key down and key up
492            // events only, not key typed events
493            public void keyTyped(KeyEvent e) {
494                // Do nothing
495            }
496        }
497    }
498    
499    
500    /*
501      Indentation spec for emacs:
502      Local Variables:
503      c-basic-offset:4
504      End:
505    */