View Javadoc
1   /*
2    * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  package javax.swing;
26  
27  import java.io.IOException;
28  import java.io.ObjectInputStream;
29  import java.io.ObjectOutputStream;
30  import java.io.Serializable;
31  import java.util.HashMap;
32  import java.util.Set;
33  
34  /**
35   * <code>InputMap</code> provides a binding between an input event
36   * (currently only <code>KeyStroke</code>s are used)
37   * and an <code>Object</code>. <code>InputMap</code>s
38   * are usually used with an <code>ActionMap</code>,
39   * to determine an <code>Action</code> to perform
40   * when a key is pressed.
41   * An <code>InputMap</code> can have a parent
42   * that is searched for bindings not defined in the <code>InputMap</code>.
43   * <p>As with <code>ActionMap</code> if you create a cycle, eg:
44   * <pre>
45   *   InputMap am = new InputMap();
46   *   InputMap bm = new InputMap():
47   *   am.setParent(bm);
48   *   bm.setParent(am);
49   * </pre>
50   * some of the methods will cause a StackOverflowError to be thrown.
51   *
52   * @author Scott Violet
53   * @since 1.3
54   */
55  @SuppressWarnings("serial")
56  public class InputMap implements Serializable {
57      /** Handles the mapping between KeyStroke and Action name. */
58      private transient ArrayTable     arrayTable;
59      /** Parent that handles any bindings we don't contain. */
60      private InputMap                                parent;
61  
62  
63      /**
64       * Creates an <code>InputMap</code> with no parent and no mappings.
65       */
66      public InputMap() {
67      }
68  
69      /**
70       * Sets this <code>InputMap</code>'s parent.
71       *
72       * @param map  the <code>InputMap</code> that is the parent of this one
73       */
74      public void setParent(InputMap map) {
75          this.parent = map;
76      }
77  
78      /**
79       * Gets this <code>InputMap</code>'s parent.
80       *
81       * @return map  the <code>InputMap</code> that is the parent of this one,
82       *              or null if this <code>InputMap</code> has no parent
83       */
84      public InputMap getParent() {
85          return parent;
86      }
87  
88      /**
89       * Adds a binding for <code>keyStroke</code> to <code>actionMapKey</code>.
90       * If <code>actionMapKey</code> is null, this removes the current binding
91       * for <code>keyStroke</code>.
92       */
93      public void put(KeyStroke keyStroke, Object actionMapKey) {
94          if (keyStroke == null) {
95              return;
96          }
97          if (actionMapKey == null) {
98              remove(keyStroke);
99          }
100         else {
101             if (arrayTable == null) {
102                 arrayTable = new ArrayTable();
103             }
104             arrayTable.put(keyStroke, actionMapKey);
105         }
106     }
107 
108     /**
109      * Returns the binding for <code>keyStroke</code>, messaging the
110      * parent <code>InputMap</code> if the binding is not locally defined.
111      */
112     public Object get(KeyStroke keyStroke) {
113         if (arrayTable == null) {
114             InputMap    parent = getParent();
115 
116             if (parent != null) {
117                 return parent.get(keyStroke);
118             }
119             return null;
120         }
121         Object value = arrayTable.get(keyStroke);
122 
123         if (value == null) {
124             InputMap    parent = getParent();
125 
126             if (parent != null) {
127                 return parent.get(keyStroke);
128             }
129         }
130         return value;
131     }
132 
133     /**
134      * Removes the binding for <code>key</code> from this
135      * <code>InputMap</code>.
136      */
137     public void remove(KeyStroke key) {
138         if (arrayTable != null) {
139             arrayTable.remove(key);
140         }
141     }
142 
143     /**
144      * Removes all the mappings from this <code>InputMap</code>.
145      */
146     public void clear() {
147         if (arrayTable != null) {
148             arrayTable.clear();
149         }
150     }
151 
152     /**
153      * Returns the <code>KeyStroke</code>s that are bound in this <code>InputMap</code>.
154      */
155     public KeyStroke[] keys() {
156         if (arrayTable == null) {
157             return null;
158         }
159         KeyStroke[] keys = new KeyStroke[arrayTable.size()];
160         arrayTable.getKeys(keys);
161         return keys;
162     }
163 
164     /**
165      * Returns the number of <code>KeyStroke</code> bindings.
166      */
167     public int size() {
168         if (arrayTable == null) {
169             return 0;
170         }
171         return arrayTable.size();
172     }
173 
174     /**
175      * Returns an array of the <code>KeyStroke</code>s defined in this
176      * <code>InputMap</code> and its parent. This differs from <code>keys()</code> in that
177      * this method includes the keys defined in the parent.
178      */
179     public KeyStroke[] allKeys() {
180         int             count = size();
181         InputMap        parent = getParent();
182 
183         if (count == 0) {
184             if (parent != null) {
185                 return parent.allKeys();
186             }
187             return keys();
188         }
189         if (parent == null) {
190             return keys();
191         }
192         KeyStroke[]    keys = keys();
193         KeyStroke[]    pKeys =  parent.allKeys();
194 
195         if (pKeys == null) {
196             return keys;
197         }
198         if (keys == null) {
199             // Should only happen if size() != keys.length, which should only
200             // happen if mutated from multiple threads (or a bogus subclass).
201             return pKeys;
202         }
203 
204         HashMap<KeyStroke, KeyStroke> keyMap = new HashMap<KeyStroke, KeyStroke>();
205         int            counter;
206 
207         for (counter = keys.length - 1; counter >= 0; counter--) {
208             keyMap.put(keys[counter], keys[counter]);
209         }
210         for (counter = pKeys.length - 1; counter >= 0; counter--) {
211             keyMap.put(pKeys[counter], pKeys[counter]);
212         }
213 
214         KeyStroke[]    allKeys = new KeyStroke[keyMap.size()];
215 
216         return keyMap.keySet().toArray(allKeys);
217     }
218 
219     private void writeObject(ObjectOutputStream s) throws IOException {
220         s.defaultWriteObject();
221 
222         ArrayTable.writeArrayTable(s, arrayTable);
223     }
224 
225     private void readObject(ObjectInputStream s) throws ClassNotFoundException,
226                                                  IOException {
227         s.defaultReadObject();
228         for (int counter = s.readInt() - 1; counter >= 0; counter--) {
229             put((KeyStroke)s.readObject(), s.readObject());
230         }
231     }
232 }