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