/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE 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 java.security.
AccessController;
import javax.swing.event.
SwingPropertyChangeSupport;
import sun.security.action.
GetPropertyAction;
/**
* This class provides default implementations for the JFC <code>Action</code>
* interface. Standard behaviors like the get and set methods for
* <code>Action</code> object properties (icon, text, and enabled) are defined
* here. The developer need only subclass this abstract class and
* define the <code>actionPerformed</code> method.
* <p>
* <strong>Warning:</strong>
* 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 JavaBeans™
* has been added to the <code>java.beans</code> package.
* Please see {@link java.beans.XMLEncoder}.
*
* @author Georges Saab
* @see Action
*/
public abstract class
AbstractAction implements
Action,
Cloneable,
Serializable
{
/**
* Whether or not actions should reconfigure all properties on null.
*/
private static
Boolean RECONFIGURE_ON_NULL;
/**
* Specifies whether action is enabled; the default is true.
*/
protected boolean
enabled = true;
/**
* Contains the array of key bindings.
*/
private transient
ArrayTable arrayTable;
/**
* Whether or not to reconfigure all action properties from the
* specified event.
*/
static boolean
shouldReconfigure(
PropertyChangeEvent e) {
if (
e.
getPropertyName() == null) {
synchronized(
AbstractAction.class) {
if (
RECONFIGURE_ON_NULL == null) {
RECONFIGURE_ON_NULL =
Boolean.
valueOf(
AccessController.
doPrivileged(new
GetPropertyAction(
"swing.actions.reconfigureOnNull", "false")));
}
return
RECONFIGURE_ON_NULL;
}
}
return false;
}
/**
* Sets the enabled state of a component from an Action.
*
* @param c the Component to set the enabled state on
* @param a the Action to set the enabled state from, may be null
*/
static void
setEnabledFromAction(
JComponent c,
Action a) {
c.
setEnabled((
a != null) ?
a.
isEnabled() : true);
}
/**
* Sets the tooltip text of a component from an Action.
*
* @param c the Component to set the tooltip text on
* @param a the Action to set the tooltip text from, may be null
*/
static void
setToolTipTextFromAction(
JComponent c,
Action a) {
c.
setToolTipText(
a != null ?
(
String)
a.
getValue(
Action.
SHORT_DESCRIPTION) : null);
}
static boolean
hasSelectedKey(
Action a) {
return (
a != null &&
a.
getValue(
Action.
SELECTED_KEY) != null);
}
static boolean
isSelected(
Action a) {
return
Boolean.
TRUE.
equals(
a.
getValue(
Action.
SELECTED_KEY));
}
/**
* Creates an {@code Action}.
*/
public
AbstractAction() {
}
/**
* Creates an {@code Action} with the specified name.
*
* @param name the name ({@code Action.NAME}) for the action; a
* value of {@code null} is ignored
*/
public
AbstractAction(
String name) {
putValue(
Action.
NAME,
name);
}
/**
* Creates an {@code Action} with the specified name and small icon.
*
* @param name the name ({@code Action.NAME}) for the action; a
* value of {@code null} is ignored
* @param icon the small icon ({@code Action.SMALL_ICON}) for the action; a
* value of {@code null} is ignored
*/
public
AbstractAction(
String name,
Icon icon) {
this(
name);
putValue(
Action.
SMALL_ICON,
icon);
}
/**
* Gets the <code>Object</code> associated with the specified key.
*
* @param key a string containing the specified <code>key</code>
* @return the binding <code>Object</code> stored with this key; if there
* are no keys, it will return <code>null</code>
* @see Action#getValue
*/
public
Object getValue(
String key) {
if (
key == "enabled") {
return
enabled;
}
if (
arrayTable == null) {
return null;
}
return
arrayTable.
get(
key);
}
/**
* Sets the <code>Value</code> associated with the specified key.
*
* @param key the <code>String</code> that identifies the stored object
* @param newValue the <code>Object</code> to store using this key
* @see Action#putValue
*/
public void
putValue(
String key,
Object newValue) {
Object oldValue = null;
if (
key == "enabled") {
// Treat putValue("enabled") the same way as a call to setEnabled.
// If we don't do this it means the two may get out of sync, and a
// bogus property change notification would be sent.
//
// To avoid dependencies between putValue & setEnabled this
// directly changes enabled. If we instead called setEnabled
// to change enabled, it would be possible for stack
// overflow in the case where a developer implemented setEnabled
// in terms of putValue.
if (
newValue == null || !(
newValue instanceof
Boolean)) {
newValue = false;
}
oldValue =
enabled;
enabled = (
Boolean)
newValue;
} else {
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;
}
/**
* Sets whether the {@code Action} is enabled. The default is {@code true}.
*
* @param newValue {@code true} to enable the action, {@code 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 <code>Object</code>s which are keys for
* which values have been set for this <code>AbstractAction</code>,
* or <code>null</code> if no keys have values set.
* @return an array of key objects, or <code>null</code> 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 <code>PropertyChangeListeners</code> have been registered, the
* <code>changeSupport</code> 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
* <code>PropertyChangeEvent</code> to any registered
* <code>PropertyChangeListeners</code>.
*/
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 <code>PropertyChangeListener</code> to the listener list.
* The listener is registered for all properties.
* <p>
* A <code>PropertyChangeEvent</code> will get fired in response to setting
* a bound property, e.g. <code>setFont</code>, <code>setBackground</code>,
* or <code>setForeground</code>.
* 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 <code>PropertyChangeListener</code> to be added
*
* @see Action#addPropertyChangeListener
*/
public synchronized void
addPropertyChangeListener(
PropertyChangeListener listener) {
if (
changeSupport == null) {
changeSupport = new
SwingPropertyChangeSupport(this);
}
changeSupport.
addPropertyChangeListener(
listener);
}
/**
* Removes a <code>PropertyChangeListener</code> from the listener list.
* This removes a <code>PropertyChangeListener</code> that was registered
* for all properties.
*
* @param listener the <code>PropertyChangeListener</code> 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 <code>PropertyChangeListener</code>s added
* to this AbstractAction with addPropertyChangeListener().
*
* @return all of the <code>PropertyChangeListener</code>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 <code>Object.clone()</code>.
**/
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());
}
}
}