1. /*
  2. * @(#)InputMap.java 1.12 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import java.io.IOException;
  9. import java.io.ObjectInputStream;
  10. import java.io.ObjectOutputStream;
  11. import java.io.Serializable;
  12. import java.util.HashMap;
  13. import java.util.Set;
  14. /**
  15. * <code>InputMap</code> provides a binding between an input event
  16. * (currently only <code>KeyStroke</code>s are used)
  17. * and an <code>Object</code>. <code>InputMap</code>s
  18. * are usually used with an <code>ActionMap</code>,
  19. * to determine an <code>Action</code> to perform
  20. * when a key is pressed.
  21. * An <code>InputMap</code> can have a parent
  22. * that is searched for bindings not defined in the <code>InputMap</code>.
  23. * <p>As with <code>ActionMap</code> if you create a cycle, eg:
  24. * <pre>
  25. * InputMap am = new InputMap();
  26. * InputMap bm = new InputMap():
  27. * am.setParent(bm);
  28. * bm.setParent(am);
  29. * </pre>
  30. * some of the methods will cause a StackOverflowError to be thrown.
  31. *
  32. * @version 1.12 01/23/03
  33. * @author Scott Violet
  34. * @since 1.3
  35. */
  36. public class InputMap implements Serializable {
  37. /** Handles the mapping between KeyStroke and Action name. */
  38. private transient AbstractAction.ArrayTable arrayTable;
  39. /** Parent that handles any bindings we don't contain. */
  40. private InputMap parent;
  41. /**
  42. * Creates an <code>InputMap</code> with no parent and no mappings.
  43. */
  44. public InputMap() {
  45. }
  46. /**
  47. * Sets this <code>InputMap</code>'s parent.
  48. *
  49. * @param map the <code>InputMap</code> that is the parent of this one
  50. */
  51. public void setParent(InputMap map) {
  52. this.parent = map;
  53. }
  54. /**
  55. * Gets this <code>InputMap</code>'s parent.
  56. *
  57. * @return map the <code>InputMap</code> that is the parent of this one,
  58. * or null if this <code>InputMap</code> has no parent
  59. */
  60. public InputMap getParent() {
  61. return parent;
  62. }
  63. /**
  64. * Adds a binding for <code>keyStroke</code> to <code>actionMapKey</code>.
  65. * If <code>actionMapKey</code> is null, this removes the current binding
  66. * for <code>keyStroke</code>.
  67. */
  68. public void put(KeyStroke keyStroke, Object actionMapKey) {
  69. if (keyStroke == null) {
  70. return;
  71. }
  72. if (actionMapKey == null) {
  73. remove(keyStroke);
  74. }
  75. else {
  76. if (arrayTable == null) {
  77. arrayTable = new AbstractAction.ArrayTable();
  78. }
  79. arrayTable.put(keyStroke, actionMapKey);
  80. }
  81. }
  82. /**
  83. * Returns the binding for <code>keyStroke</code>, messaging the
  84. * parent <code>InputMap</code> if the binding is not locally defined.
  85. */
  86. public Object get(KeyStroke keyStroke) {
  87. if (arrayTable == null) {
  88. InputMap parent = getParent();
  89. if (parent != null) {
  90. return parent.get(keyStroke);
  91. }
  92. return null;
  93. }
  94. Object value = arrayTable.get(keyStroke);
  95. if (value == null) {
  96. InputMap parent = getParent();
  97. if (parent != null) {
  98. return parent.get(keyStroke);
  99. }
  100. }
  101. return value;
  102. }
  103. /**
  104. * Removes the binding for <code>key</code> from this
  105. * <code>InputMap</code>.
  106. */
  107. public void remove(KeyStroke key) {
  108. if (arrayTable != null) {
  109. arrayTable.remove(key);
  110. }
  111. }
  112. /**
  113. * Removes all the mappings from this <code>InputMap</code>.
  114. */
  115. public void clear() {
  116. if (arrayTable != null) {
  117. arrayTable.clear();
  118. }
  119. }
  120. /**
  121. * Returns the <code>KeyStroke</code>s that are bound in this <code>InputMap</code>.
  122. */
  123. public KeyStroke[] keys() {
  124. if (arrayTable == null) {
  125. return null;
  126. }
  127. KeyStroke[] keys = new KeyStroke[arrayTable.size()];
  128. arrayTable.getKeys(keys);
  129. return keys;
  130. }
  131. /**
  132. * Returns the number of <code>KeyStroke</code> bindings.
  133. */
  134. public int size() {
  135. if (arrayTable == null) {
  136. return 0;
  137. }
  138. return arrayTable.size();
  139. }
  140. /**
  141. * Returns an array of the <code>KeyStroke</code>s defined in this
  142. * <code>InputMap</code> and its parent. This differs from <code>keys()</code> in that
  143. * this method includes the keys defined in the parent.
  144. */
  145. public KeyStroke[] allKeys() {
  146. int count = size();
  147. InputMap parent = getParent();
  148. if (count == 0) {
  149. if (parent != null) {
  150. return parent.allKeys();
  151. }
  152. return keys();
  153. }
  154. if (parent == null) {
  155. return keys();
  156. }
  157. KeyStroke[] keys = keys();
  158. KeyStroke[] pKeys = parent.allKeys();
  159. if (pKeys == null) {
  160. return keys;
  161. }
  162. if (keys == null) {
  163. // Should only happen if size() != keys.length, which should only
  164. // happen if mutated from multiple threads (or a bogus subclass).
  165. return pKeys;
  166. }
  167. HashMap keyMap = new HashMap();
  168. int counter;
  169. for (counter = keys.length - 1; counter >= 0; counter--) {
  170. keyMap.put(keys[counter], keys[counter]);
  171. }
  172. for (counter = pKeys.length - 1; counter >= 0; counter--) {
  173. keyMap.put(pKeys[counter], pKeys[counter]);
  174. }
  175. KeyStroke[] allKeys = new KeyStroke[keyMap.size()];
  176. return (KeyStroke[])keyMap.keySet().toArray(allKeys);
  177. }
  178. private void writeObject(ObjectOutputStream s) throws IOException {
  179. s.defaultWriteObject();
  180. AbstractAction.ArrayTable.writeArrayTable(s, arrayTable);
  181. }
  182. private void readObject(ObjectInputStream s) throws ClassNotFoundException,
  183. IOException {
  184. s.defaultReadObject();
  185. for (int counter = s.readInt() - 1; counter >= 0; counter--) {
  186. put((KeyStroke)s.readObject(), s.readObject());
  187. }
  188. }
  189. }