/*
* PluginAction.java 26 oct. 2008
*
* Sweet Home 3D, Copyright (c) 2008 Emmanuel PUYBARET / eTeks <info@eteks.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.eteks.sweethome3d.plugin;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import com.eteks.sweethome3d.tools.ResourceURLContent;
/**
* An action made available to application users through a plugin.
* @author Emmanuel Puybaret
*/
public abstract class PluginAction {
/**
* Enumeration of the various properties this action may define.
*/
public enum Property {
/**
* The key of the property of <code>String</code> type that specifies
* the name of an action, used for a menu or button.
*/
NAME,
/**
* The key of the property of <code>String</code> type that specifies
* a short description of an action, used for tool tip text.
*/
SHORT_DESCRIPTION,
/**
* The key of the property of <code>Content</code> type that specifies
* an image content of an action, used for tool bar buttons.
*/
SMALL_ICON,
/**
* The key of the property of <code>Character</code> type that specifies
* the ASCII character used as the mnemonic of an action.
*/
MNEMONIC,
/**
* The key of the property of <code>Boolean</code> type that specifies
* if an action will appear in the main tool bar.
*/
TOOL_BAR,
/**
* The key of the property of <code>String</code> type that specifies
* in which menu of the main menu bar an action should appear.
*/
MENU,
/**
* The key of the property of <code>Boolean</code> type that specifies
* if an action is enabled or not.
*/
ENABLED,
}
private final Map<Property, Object> propertyValues = new HashMap<Property, Object>();
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
/**
* Creates a disabled plug-in action.
*/
public PluginAction() {
}
/**
* Creates a disabled plug-in action with properties retrieved from a resource bundle
* in which key starts with <code>actionPrefix</code>.
* <br>For example, a plug-in action created by the call
* <code>new PluginAction("com.mycompany.mypackage.MyResources", "EXPORT", plugin.getPluginClassLoader())</code>
* will retrieve its property values from the <code>/com/mycompany/mypackage/MyResources.properties</code> file
* bundled with the plug-in class, and this file may describe the action <code>EXPORT</code>
* with the following keys:
* <pre> EXPORT.NAME=Export
* EXPORT.SHORT_DESCRIPTION=Export home data
* EXPORT.SMALL_ICON=/com/mycompany/mypackage/resources/export.png
* EXPORT.MNEMONIC=X
* EXPORT.IN_TOOL_BAR=true
* EXPORT.MENU=File</pre>
* @param resourceBaseName the base name of a resource bundle
* @param actionPrefix prefix used in resource bundle to search action properties
* @param pluginClassLoader the class loader that will be used to search the resource bundle
* @throws MissingResourceException if no resource bundle could be found from
* <code>resourceBaseName</code>.
*/
public PluginAction(String resourceBaseName,
String actionPrefix,
ClassLoader pluginClassLoader) {
this(resourceBaseName, actionPrefix, pluginClassLoader, false);
}
/**
* Creates an action with properties retrieved from a resource bundle
* in which key starts with <code>actionPrefix</code>.
* @param resourceBaseName the base name of a resource bundle
* @param actionPrefix prefix used in resource bundle to search action properties
* @param pluginClassLoader the class loader that will be used to search the resource bundle
* @param enabled <code>true</code> if the action should be enabled at creation.
* @throws MissingResourceException if no resource bundle could be found from
* <code>resourceBaseName</code>.
*/
public PluginAction(String resourceBaseName,
String actionPrefix,
ClassLoader pluginClassLoader,
boolean enabled) {
readActionPropertyValues(resourceBaseName, actionPrefix, pluginClassLoader);
setEnabled(enabled);
}
/**
* Reads the properties of this action from a resource bundle of given base name.
* @throws MissingResourceException if no resource bundle could be found from
* <code>resourceBaseName</code>.
*/
private void readActionPropertyValues(String resourceBaseName,
String actionPrefix,
ClassLoader pluginClassLoader) {
ResourceBundle resource;
if (pluginClassLoader != null) {
resource = ResourceBundle.getBundle(resourceBaseName, Locale.getDefault(),
pluginClassLoader);
} else {
resource = ResourceBundle.getBundle(resourceBaseName, Locale.getDefault());
}
String propertyPrefix = actionPrefix + ".";
putPropertyValue(Property.NAME,
getOptionalString(resource, propertyPrefix + Property.NAME));
putPropertyValue(Property.SHORT_DESCRIPTION,
getOptionalString(resource, propertyPrefix + Property.SHORT_DESCRIPTION));
String smallIcon = getOptionalString(resource, propertyPrefix + Property.SMALL_ICON);
if (smallIcon != null) {
if (smallIcon.startsWith("/")) {
smallIcon = smallIcon.substring(1);
}
putPropertyValue(Property.SMALL_ICON,
new ResourceURLContent(pluginClassLoader, smallIcon));
}
String mnemonicKey = getOptionalString(resource, propertyPrefix + Property.MNEMONIC);
if (mnemonicKey != null) {
putPropertyValue(Property.MNEMONIC, Character.valueOf(mnemonicKey.charAt(0)));
}
String toolBar = getOptionalString(resource, propertyPrefix + Property.TOOL_BAR);
if (toolBar != null) {
putPropertyValue(Property.TOOL_BAR, Boolean.valueOf(toolBar));
}
putPropertyValue(Property.MENU,
getOptionalString(resource, propertyPrefix + Property.MENU));
}
/**
* Returns the value of <code>propertyKey</code> in <code>resource</code>,
* or <code>null</code> if the property doesn't exist.
*/
private String getOptionalString(ResourceBundle resource, String propertyKey) {
try {
return resource.getString(propertyKey);
} catch (MissingResourceException ex) {
return null;
}
}
/**
* Adds the property change <code>listener</code> in parameter to this plugin action.
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
this.propertyChangeSupport.addPropertyChangeListener(listener);
}
/**
* Removes the property change <code>listener</code> in parameter from this plugin action.
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
this.propertyChangeSupport.removePropertyChangeListener(listener);
}
/**
* Returns a property value of this action.
*/
public Object getPropertyValue(Property property) {
return this.propertyValues.get(property);
}
/**
* Sets a property value of this action, and fires a <code>PropertyChangeEvent</code>
* if the value changed.
*/
public void putPropertyValue(Property property, Object value) {
Object oldValue = this.propertyValues.get(property);
if (value != oldValue
|| (value != null && !value.equals(oldValue))) {
this.propertyValues.put(property, value);
this.propertyChangeSupport.firePropertyChange(property.name(), oldValue, value);
}
}
/**
* Sets the enabled state of this action. When enabled, any menu item or tool bar button
* associated with this object is enabled and able to call the <code>execute</code> method.
* If the value has changed, a <code>PropertyChangeEvent</code> is sent
* to listeners. By default, an action is disabled.
*/
public void setEnabled(boolean enabled) {
putPropertyValue(Property.ENABLED, enabled);
}
/**
* Returns the enabled state of this action.
* @return <code>true</code> if this action is enabled.
*/
public boolean isEnabled() {
Boolean enabled = (Boolean)getPropertyValue(Property.ENABLED);
return enabled != null && enabled.booleanValue();
}
/**
* Executes this action. This method will be called by application when the user
* wants to execute this action.
*/
public abstract void execute();
}