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