1. /*
  2. * @(#)AbstractAction.java 1.51 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.awt.*;
  9. import java.awt.event.*;
  10. import java.beans.*;
  11. import java.util.Hashtable;
  12. import java.util.Enumeration;
  13. import java.io.Serializable;
  14. import java.io.IOException;
  15. import java.io.ObjectInputStream;
  16. import java.io.ObjectOutputStream;
  17. import javax.swing.event.SwingPropertyChangeSupport;
  18. /**
  19. * This class provides default implementations for the JFC <code>Action</code>
  20. * interface. Standard behaviors like the get and set methods for
  21. * <code>Action</code> object properties (icon, text, and enabled) are defined
  22. * here. The developer need only subclass this abstract class and
  23. * define the <code>actionPerformed</code> method.
  24. * <p>
  25. * <strong>Warning:</strong>
  26. * Serialized objects of this class will not be compatible with
  27. * future Swing releases. The current serialization support is
  28. * appropriate for short term storage or RMI between applications running
  29. * the same version of Swing. As of 1.4, support for long term storage
  30. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  31. * has been added to the <code>java.beans</code> package.
  32. * Please see {@link java.beans.XMLEncoder}.
  33. *
  34. * @version 1.51 12/19/03
  35. * @author Georges Saab
  36. * @see Action
  37. */
  38. public abstract class AbstractAction implements Action, Cloneable, Serializable
  39. {
  40. /**
  41. * Specifies whether action is enabled; the default is true.
  42. */
  43. protected boolean enabled = true;
  44. /**
  45. * Contains the array of key bindings.
  46. */
  47. private transient ArrayTable arrayTable;
  48. /**
  49. * Defines an <code>Action</code> object with a default
  50. * description string and default icon.
  51. */
  52. public AbstractAction() {
  53. }
  54. /**
  55. * Defines an <code>Action</code> object with the specified
  56. * description string and a default icon.
  57. */
  58. public AbstractAction(String name) {
  59. putValue(Action.NAME, name);
  60. }
  61. /**
  62. * Defines an <code>Action</code> object with the specified
  63. * description string and a the specified icon.
  64. */
  65. public AbstractAction(String name, Icon icon) {
  66. this(name);
  67. putValue(Action.SMALL_ICON, icon);
  68. }
  69. /**
  70. * Gets the <code>Object</code> associated with the specified key.
  71. *
  72. * @param key a string containing the specified <code>key</code>
  73. * @return the binding <code>Object</code> stored with this key; if there
  74. * are no keys, it will return <code>null</code>
  75. * @see Action#getValue
  76. */
  77. public Object getValue(String key) {
  78. if (arrayTable == null) {
  79. return null;
  80. }
  81. return arrayTable.get(key);
  82. }
  83. /**
  84. * Sets the <code>Value</code> associated with the specified key.
  85. *
  86. * @param key the <code>String</code> that identifies the stored object
  87. * @param newValue the <code>Object</code> to store using this key
  88. * @see Action#putValue
  89. */
  90. public void putValue(String key, Object newValue) {
  91. Object oldValue = null;
  92. if (arrayTable == null) {
  93. arrayTable = new ArrayTable();
  94. }
  95. if (arrayTable.containsKey(key))
  96. oldValue = arrayTable.get(key);
  97. // Remove the entry for key if newValue is null
  98. // else put in the newValue for key.
  99. if (newValue == null) {
  100. arrayTable.remove(key);
  101. } else {
  102. arrayTable.put(key,newValue);
  103. }
  104. firePropertyChange(key, oldValue, newValue);
  105. }
  106. /**
  107. * Returns true if the action is enabled.
  108. *
  109. * @return true if the action is enabled, false otherwise
  110. * @see Action#isEnabled
  111. */
  112. public boolean isEnabled() {
  113. return enabled;
  114. }
  115. /**
  116. * Enables or disables the action.
  117. *
  118. * @param newValue true to enable the action, false to
  119. * disable it
  120. * @see Action#setEnabled
  121. */
  122. public void setEnabled(boolean newValue) {
  123. boolean oldValue = this.enabled;
  124. if (oldValue != newValue) {
  125. this.enabled = newValue;
  126. firePropertyChange("enabled",
  127. Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
  128. }
  129. }
  130. /**
  131. * Returns an array of <code>Object</code>s which are keys for
  132. * which values have been set for this <code>AbstractAction</code>,
  133. * or <code>null</code> if no keys have values set.
  134. * @return an array of key objects, or <code>null</code> if no
  135. * keys have values set
  136. * @since 1.3
  137. */
  138. public Object[] getKeys() {
  139. if (arrayTable == null) {
  140. return null;
  141. }
  142. Object[] keys = new Object[arrayTable.size()];
  143. arrayTable.getKeys(keys);
  144. return keys;
  145. }
  146. /**
  147. * If any <code>PropertyChangeListeners</code> have been registered, the
  148. * <code>changeSupport</code> field describes them.
  149. */
  150. protected SwingPropertyChangeSupport changeSupport;
  151. /**
  152. * Supports reporting bound property changes. This method can be called
  153. * when a bound property has changed and it will send the appropriate
  154. * <code>PropertyChangeEvent</code> to any registered
  155. * <code>PropertyChangeListeners</code>.
  156. */
  157. protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
  158. if (changeSupport == null ||
  159. (oldValue != null && newValue != null && oldValue.equals(newValue))) {
  160. return;
  161. }
  162. changeSupport.firePropertyChange(propertyName, oldValue, newValue);
  163. }
  164. /**
  165. * Adds a <code>PropertyChangeListener</code> to the listener list.
  166. * The listener is registered for all properties.
  167. * <p>
  168. * A <code>PropertyChangeEvent</code> will get fired in response to setting
  169. * a bound property, e.g. <code>setFont</code>, <code>setBackground</code>,
  170. * or <code>setForeground</code>.
  171. * Note that if the current component is inheriting its foreground,
  172. * background, or font from its container, then no event will be
  173. * fired in response to a change in the inherited property.
  174. *
  175. * @param listener The <code>PropertyChangeListener</code> to be added
  176. *
  177. * @see Action#addPropertyChangeListener
  178. */
  179. public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
  180. if (changeSupport == null) {
  181. changeSupport = new SwingPropertyChangeSupport(this);
  182. }
  183. changeSupport.addPropertyChangeListener(listener);
  184. }
  185. /**
  186. * Removes a <code>PropertyChangeListener</code> from the listener list.
  187. * This removes a <code>PropertyChangeListener</code> that was registered
  188. * for all properties.
  189. *
  190. * @param listener the <code>PropertyChangeListener</code> to be removed
  191. *
  192. * @see Action#removePropertyChangeListener
  193. */
  194. public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
  195. if (changeSupport == null) {
  196. return;
  197. }
  198. changeSupport.removePropertyChangeListener(listener);
  199. }
  200. /**
  201. * Returns an array of all the <code>PropertyChangeListener</code>s added
  202. * to this AbstractAction with addPropertyChangeListener().
  203. *
  204. * @return all of the <code>PropertyChangeListener</code>s added or an empty
  205. * array if no listeners have been added
  206. * @since 1.4
  207. */
  208. public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
  209. if (changeSupport == null) {
  210. return new PropertyChangeListener[0];
  211. }
  212. return changeSupport.getPropertyChangeListeners();
  213. }
  214. /**
  215. * Clones the abstract action. This gives the clone
  216. * its own copy of the key/value list,
  217. * which is not handled for you by <code>Object.clone()</code>.
  218. **/
  219. protected Object clone() throws CloneNotSupportedException {
  220. AbstractAction newAction = (AbstractAction)super.clone();
  221. synchronized(this) {
  222. if (arrayTable != null) {
  223. newAction.arrayTable = (ArrayTable)arrayTable.clone();
  224. }
  225. }
  226. return newAction;
  227. }
  228. private void writeObject(ObjectOutputStream s) throws IOException {
  229. // Store the default fields
  230. s.defaultWriteObject();
  231. // And the keys
  232. ArrayTable.writeArrayTable(s, arrayTable);
  233. }
  234. private void readObject(ObjectInputStream s) throws ClassNotFoundException,
  235. IOException {
  236. s.defaultReadObject();
  237. for (int counter = s.readInt() - 1; counter >= 0; counter--) {
  238. putValue((String)s.readObject(), s.readObject());
  239. }
  240. }
  241. }