/*
* @(#)AbstractAction.java 1.51 03/12/19
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package javax.swing;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.Serializable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import javax.swing.event.SwingPropertyChangeSupport;
/**
* This class provides default implementations for the JFC Action
* interface. Standard behaviors like the get and set methods for
* Action
object properties (icon, text, and enabled) are defined
* here. The developer need only subclass this abstract class and
* define the actionPerformed
method.
*
* Warning:
* Serialized objects of this class will not be compatible with
* future Swing releases. The current serialization support is
* appropriate for short term storage or RMI between applications running
* the same version of Swing. As of 1.4, support for long term storage
* of all JavaBeansTM
* has been added to the java.beans
package.
* Please see {@link java.beans.XMLEncoder}.
*
* @version 1.51 12/19/03
* @author Georges Saab
* @see Action
*/
public abstract class AbstractAction implements Action, Cloneable, Serializable
{
/**
* Specifies whether action is enabled; the default is true.
*/
protected boolean enabled = true;
/**
* Contains the array of key bindings.
*/
private transient ArrayTable arrayTable;
/**
* Defines an Action
object with a default
* description string and default icon.
*/
public AbstractAction() {
}
/**
* Defines an Action
object with the specified
* description string and a default icon.
*/
public AbstractAction(String name) {
putValue(Action.NAME, name);
}
/**
* Defines an Action
object with the specified
* description string and a the specified icon.
*/
public AbstractAction(String name, Icon icon) {
this(name);
putValue(Action.SMALL_ICON, icon);
}
/**
* Gets the Object
associated with the specified key.
*
* @param key a string containing the specified key
* @return the binding Object
stored with this key; if there
* are no keys, it will return null
* @see Action#getValue
*/
public Object getValue(String key) {
if (arrayTable == null) {
return null;
}
return arrayTable.get(key);
}
/**
* Sets the Value
associated with the specified key.
*
* @param key the String
that identifies the stored object
* @param newValue the Object
to store using this key
* @see Action#putValue
*/
public void putValue(String key, Object newValue) {
Object oldValue = null;
if (arrayTable == null) {
arrayTable = new ArrayTable();
}
if (arrayTable.containsKey(key))
oldValue = arrayTable.get(key);
// Remove the entry for key if newValue is null
// else put in the newValue for key.
if (newValue == null) {
arrayTable.remove(key);
} else {
arrayTable.put(key,newValue);
}
firePropertyChange(key, oldValue, newValue);
}
/**
* Returns true if the action is enabled.
*
* @return true if the action is enabled, false otherwise
* @see Action#isEnabled
*/
public boolean isEnabled() {
return enabled;
}
/**
* Enables or disables the action.
*
* @param newValue true to enable the action, false to
* disable it
* @see Action#setEnabled
*/
public void setEnabled(boolean newValue) {
boolean oldValue = this.enabled;
if (oldValue != newValue) {
this.enabled = newValue;
firePropertyChange("enabled",
Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
}
}
/**
* Returns an array of Object
s which are keys for
* which values have been set for this AbstractAction
,
* or null
if no keys have values set.
* @return an array of key objects, or null
if no
* keys have values set
* @since 1.3
*/
public Object[] getKeys() {
if (arrayTable == null) {
return null;
}
Object[] keys = new Object[arrayTable.size()];
arrayTable.getKeys(keys);
return keys;
}
/**
* If any PropertyChangeListeners
have been registered, the
* changeSupport
field describes them.
*/
protected SwingPropertyChangeSupport changeSupport;
/**
* Supports reporting bound property changes. This method can be called
* when a bound property has changed and it will send the appropriate
* PropertyChangeEvent
to any registered
* PropertyChangeListeners
.
*/
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
if (changeSupport == null ||
(oldValue != null && newValue != null && oldValue.equals(newValue))) {
return;
}
changeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
/**
* Adds a PropertyChangeListener
to the listener list.
* The listener is registered for all properties.
*
* A PropertyChangeEvent
will get fired in response to setting
* a bound property, e.g. setFont
, setBackground
,
* or setForeground
.
* Note that if the current component is inheriting its foreground,
* background, or font from its container, then no event will be
* fired in response to a change in the inherited property.
*
* @param listener The PropertyChangeListener
to be added
*
* @see Action#addPropertyChangeListener
*/
public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
if (changeSupport == null) {
changeSupport = new SwingPropertyChangeSupport(this);
}
changeSupport.addPropertyChangeListener(listener);
}
/**
* Removes a PropertyChangeListener
from the listener list.
* This removes a PropertyChangeListener
that was registered
* for all properties.
*
* @param listener the PropertyChangeListener
to be removed
*
* @see Action#removePropertyChangeListener
*/
public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
if (changeSupport == null) {
return;
}
changeSupport.removePropertyChangeListener(listener);
}
/**
* Returns an array of all the PropertyChangeListener
s added
* to this AbstractAction with addPropertyChangeListener().
*
* @return all of the PropertyChangeListener
s added or an empty
* array if no listeners have been added
* @since 1.4
*/
public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
if (changeSupport == null) {
return new PropertyChangeListener[0];
}
return changeSupport.getPropertyChangeListeners();
}
/**
* Clones the abstract action. This gives the clone
* its own copy of the key/value list,
* which is not handled for you by Object.clone()
.
**/
protected Object clone() throws CloneNotSupportedException {
AbstractAction newAction = (AbstractAction)super.clone();
synchronized(this) {
if (arrayTable != null) {
newAction.arrayTable = (ArrayTable)arrayTable.clone();
}
}
return newAction;
}
private void writeObject(ObjectOutputStream s) throws IOException {
// Store the default fields
s.defaultWriteObject();
// And the keys
ArrayTable.writeArrayTable(s, arrayTable);
}
private void readObject(ObjectInputStream s) throws ClassNotFoundException,
IOException {
s.defaultReadObject();
for (int counter = s.readInt() - 1; counter >= 0; counter--) {
putValue((String)s.readObject(), s.readObject());
}
}
}