All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openbp.jaspira.plugin.AbstractPlugin Maven / Gradle / Ivy

The newest version!
/*
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package org.openbp.jaspira.plugin;

import java.awt.event.ActionEvent;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.swing.SwingUtilities;

import org.openbp.common.ExceptionUtil;
import org.openbp.common.icon.MultiIcon;
import org.openbp.common.rc.ResourceCollection;
import org.openbp.common.rc.ResourceCollectionMgr;
import org.openbp.jaspira.action.ActionMgr;
import org.openbp.jaspira.action.JaspiraAction;
import org.openbp.jaspira.action.JaspiraActionEvent;
import org.openbp.jaspira.action.keys.KeyMgr;
import org.openbp.jaspira.action.keys.KeySequence;
import org.openbp.jaspira.event.JaspiraEvent;
import org.openbp.jaspira.event.JaspiraEventHandlerCode;
import org.openbp.jaspira.event.JaspiraEventMgr;
import org.openbp.jaspira.event.KeySequenceEvent;
import org.openbp.jaspira.event.StackActionEvent;
import org.openbp.jaspira.event.VetoableEvent;
import org.openbp.jaspira.gui.interaction.InteractionClient;
import org.openbp.jaspira.gui.plugin.ApplicationBase;
import org.openbp.jaspira.plugins.statusbar.StatusBarTextEvent;

/**
 * Abstract superclass for plugins.
 *
 * @author Stephan Moritz
 */
public abstract class AbstractPlugin
	implements Plugin
{
	/** Description resource key */
	public static final String PROPERTY_DESCRIPTION = "description";

	/** Plugin name resource key */
	public static final String PROPERTY_NAME = "name";

	/** Title resource key */
	public static final String PROPERTY_TITLE = "title";

	/** Icon resource key */
	public static final String PROPERTY_ICON = "icon";

	/** Version resource key */
	public static final String PROPERTY_VERSION = "version";

	/** Vendor resource key */
	public static final String PROPERTY_VENDOR = "vendor";

	/** Key sequence to show the plugin */
	public static final String PROPERTY_SEQUENCE = "sequence";

	/** Conditional expression resource key */
	public static final String PROPERTY_CONDITION = "condition";

	/** Plugin resource */
	private ResourceCollection resourceCollection;

	/** Plugin name (can be null) */
	private String name;

	/** Plugin title */
	private String title;

	/** Description */
	private String description;

	/** Plugin icon */
	private MultiIcon icon;

	/** Plugin vendor */
	private String vendor;

	/** Plugin version */
	private String version;

	/** Conditional expression that determines if the plugin should be active */
	private String condition;

	/** Unique id of this plugin instance */
	private String uniqueId;

	/** Option modules of this plugin (internal and external, contains {@link OptionModule} objects) */
	private List optionModules;

	/** Event modules defined as inner classes of this plugin */
	private List eventModules;

	/** Contains eventgroups that this plugin reacts to as String - Set of {@link EventModule} pair. */
	private Map eventgroups;

	/**
	 * Maps names of foreign events that this plugin reacts to (in the event tree) as
	 * String - Set of {@link EventModule} pair.
	 */
	private Map foreignEvents;

	/**
	 * Table mapping event names to {@link JaspiraAction} objects that have been
	 * generated based upon the event methods provided by the event modules of this plugin.
	 */
	private List eventActionNames;

	/**
	 * This map contains all keysequence activatable actions of the
	 * plugin as {@link KeySequence} - {@link JaspiraAction} pair.
	 */
	private Map actionsBySequence;

	/**
	 * Map of peerGroup identifiers (String - Objects). Two plugins are considered to
	 * be in the same peer group, if both plugin's peer group list have at least one common
	 * String - Object pair. An entry with null as value is considered member of all peer groups
	 * of that specific type.
	 */
	private Map peerGroups;

	/** Contains a list of all children plugins of this plugin */
	private List childPlugins;

	/** The parent plugin of this plugin */
	private Plugin parent;

	/** Level of this plugin in the plugin tree (see {@link Plugin}) */
	private int level;

	/** Stack perfomer for event stack */
	private StackPerformer stackPerformer;

	/** Instance counter used to generate unique ids */
	private static int instanceCounter;

	/////////////////////////////////////////////////////////////////////////
	// @@ Install/Uninstall
	/////////////////////////////////////////////////////////////////////////

	/**
	 * Standard constructor. Note that all plugins should provide an empty constructor and
	 * that they are usually instantiated via the plugin manager as opposed to this
	 * constructor.
	 */
	public AbstractPlugin()
	{
		// Provide a default for the plugin name
		name = getClass().getName();
		level = LEVEL_PLUGIN;
	}

	/**
	 * Initializes the plugin.
	 * Should only be called by the plugin manager.
	 */
	public void initializePlugin()
	{
		initializeResources();

		fireEvent(new StatusBarTextEvent(this, "Loading plugin '" + getTitle() + "'..."));

		installEventModules();
	}

	/**
	 * Installs the plugin and all its modules.
	 * Should only be called by the plugin manager.
	 */
	public void installPlugin()
	{
		KeyMgr.getInstance().suspendUpdate();
		try
		{
			installPluginContent();
		}
		finally
		{
			KeyMgr.getInstance().resumeUpdate();
		}

		pluginInstalled();
	}

	/**
	 * Installs the contents of the plugin.
	 */
	protected void installPluginContent()
	{
		installActions();
	}

	/**
	 * Uninstalls the plugin and all of its modules.
	 * Never call this method directly, use {@link PluginMgr#removeInstance(Plugin)} instead.
	 */
	public void uninstallPlugin()
	{
		KeyMgr.getInstance().suspendUpdate();
		try
		{
			pluginUninstalled();

			uninstallActions();
			uninstallEventModules();

			setParentPlugin(null);
		}
		finally
		{
			KeyMgr.getInstance().resumeUpdate();
		}
	}

	/**
	 * This template method is called before the first plugin instance of this kind is installed.
	 * Can be used to implement peer group behaviour.
* Called by the plugin manager before the {@link #initializePlugin()} method has been called.
* If you override this method, make sure to call super.installFirst(). */ public void installFirstPlugin() { installOptionModules(); } /** * This is called after uninstall for the last instance has been uninstalled. * Called by the plugin manager after the {@link #uninstallLastPlugin()} method has been called.
* If you override this method, make sure to call super.uninstallLast(). */ public void uninstallLastPlugin() { uninstallOptionModules(); } /** * Loads the plugin resources. */ protected void initializeResources() { // Get the default resource if none given yet ResourceCollection res = getPluginResourceCollection(); if (res == null) { res = ResourceCollectionMgr.getDefaultInstance().getResource(getResourceCollectionContainerName(), getClass()); if (res == null) throw new RuntimeException("Cannot find resource for class '" + getClass().getName() + "' in resource container '" + getResourceCollectionContainerName() + "'."); setResourceCollection(res); } // Name and description are optional, title and icon not name = getNonNullResourceString(PROPERTY_NAME, name); title = getNonNullResourceString(PROPERTY_TITLE, title); description = getNonNullResourceString(PROPERTY_DESCRIPTION, description); icon = (MultiIcon) res.getRequiredObject(PROPERTY_ICON); vendor = getNonNullResourceString(PROPERTY_VENDOR, vendor); version = getNonNullResourceString(PROPERTY_VERSION, version); condition = getNonNullResourceString(PROPERTY_CONDITION, condition); } private String getNonNullResourceString(String resItemName, String dflt) { String s = getPluginResourceCollection().getOptionalString(resItemName); if (s != null) return s; return dflt; } /** * Initializes some properties from the given plugin profile. * * @param profile Profile */ protected void initializeFromPluginProfile(PluginProfile profile) { if (profile.getName() != null) { name = profile.getName(); } if (profile.getTitle() != null) { title = profile.getTitle(); } if (profile.getDescription() != null) { description = profile.getDescription(); } if (profile.getVendor() != null) { vendor = profile.getVendor(); } if (profile.getVersion() != null) { version = profile.getVersion(); } if (profile.getCondition() != null) { condition = profile.getCondition(); } } /** * Adds a key sequence that triggers the given action. * * @param sequence Sequence to add * @param action Action to execute if the sequence is recognized */ protected void addActionKeySequence(KeySequence sequence, JaspiraAction action) { if (actionsBySequence == null) actionsBySequence = new HashMap(); actionsBySequence.put(sequence, action); } /** * Returns a string representation of this plugin * * @return tile (uniqueId) [child plugins, ...] */ public String toString() { String title = getTitle(); StringBuffer result = new StringBuffer(title != null ? title : "(no title)"); result.append(" ("); result.append(getUniqueId()); result.append(" )"); if (childPlugins != null) { result.append(" ["); int n = childPlugins.size(); for (int i = 0; i < n; ++i) { result.append(childPlugins.get(i)); } result.append("]"); } return result.toString(); } ////////////////////////////////////////////////// // @@ Template methods ////////////////////////////////////////////////// /** * This template method is called after the plugin has been installed. */ protected void pluginInstalled() { } /** * This template method is called before the plugin is uninstalled. */ protected void pluginUninstalled() { } ////////////////////////////////////////////////// // @@ Overridables for plugin customization ////////////////////////////////////////////////// /** * Returns a list of external option module classes of this plugin. * Override this method to customize the plugin. * * @return An array of classes or null (the default) if the plugin doesn't declare external option modules. * The returned classes need to subclass {@link OptionModule} . */ protected Class[] getExternalOptionModuleClasses() { return null; } /** * Returns the list of external event module classes. * Override this method to customize the plugin. * * @return An array of classes or null (the default) if the plugin doesn't declare external event modules. * The returned classes need to subclass {@link ExternalEventModule} */ protected Class[] getExternalEventModuleClasses() { return null; } /** * Returns a collection of the external actions of this plugin. * Override this method to customize the plugin. * * @return A collection of {@link JaspiraAction} objects or null */ protected Collection getExternalActions() { return null; } /** * Gets the Dnd sub clients of this plugin. * * @return A list containing this object if it is a {@link InteractionClient} or null otherwise */ public List getSubClients() { if (this instanceof InteractionClient) return Collections.singletonList(this); return null; } ///////////////////////////////////////////////////////////////////////// // @@ Event handling ///////////////////////////////////////////////////////////////////////// /** * Fires the given event using the event queue. * @param je Event to fire * @return * true If the event was consumed by a plugin
* false Otherwise */ public boolean fireEvent(JaspiraEvent je) { boolean ret = false; // Use EventManager to pre-handle the event if (JaspiraEventMgr.getInstance().preHandleEvent(je)) { // This is like consuming the event ret = true; } else { // pass the event on if (je.getType() == JaspiraEvent.TYPE_GLOBAL) { ret = JaspiraEventMgr.getInstance().postHandleEvent(je); } else { ret = receiveEvent(je); } } return ret; } /** * Creates a new client event using the given event name and fires it. * * @param eventName Name of the event * @return * true If the event was consumed by a plugin
* false Otherwise */ public boolean fireEvent(String eventName) { return fireEvent(new JaspiraEvent(this, eventName)); } /** * Creates a new client event using the given event name and event object and fires it. * * @param eventName Name of the event * @param data Event object * @return * true If the event was consumed by a plugin
* false Otherwise */ public boolean fireEvent(String eventName, Object data) { return fireEvent(new JaspiraEvent(this, eventName, data)); } /** * Passes an event down to the child plugins. * @param je Event to pass * @return * true If the event was consumed by a child plugin
* false Otherwise */ protected boolean passDown(JaspiraEvent je) { Plugin ignore = je.getBrand(); if (childPlugins != null) { for (int i = 0; i < childPlugins.size(); ++i) { Plugin child = (Plugin) childPlugins.get(i); if (ignore != child) { if (child.inheritEvent(je)) return true; } } } return false; } /** * Tries to handles the event for this plugin without passing it on. * Stackable events will be stacked. * * @param je The event to handle * @return * true If the event was consumed by the plugin
* false Otherwise */ public boolean handleEvent(JaspiraEvent je) { String target = je.getTargetPluginId(); if (target != null) { // Target id specified, check if it maches if (! target.equals(getUniqueId())) // No, ignore this event for this plugin return false; } target = je.getTargetClassName(); if (target != null) { // Target id specified, check if it maches if (! target.equals(getClass().getName())) // No, ignore this event for this plugin return false; } if (je.isStackable()) { stackEvent(je); return false; } return doHandleEvent(je); } /** * Tries to handles the event for this plugin without passing it on. * * @param je The event to handle * @return * true If the event was consumed by the plugin
* false Otherwise */ boolean doHandleEvent(JaspiraEvent je) { // Check event groups if (eventgroups != null) { Set modules = (Set) eventgroups.get(je.getEventGroup()); if (modules != null) { for (Iterator it = modules.iterator(); it.hasNext();) { if (((EventModule) it.next()).eventFired(je)) return true; } } } // Check foreign events if (foreignEvents != null) { Set modules = (Set) foreignEvents.get(je.getEventName()); if (modules != null) { for (Iterator it = modules.iterator(); it.hasNext();) { if (((EventModule) it.next()).eventFired(je)) return true; } } } return false; } /** * Handles an incoming event that is received from a child plugin. * Passes this event to all other children if necessary (flood events). * * @param je The event to handle * @return * true If the event was consumed by the plugin
* false Otherwise */ public boolean receiveEvent(JaspiraEvent je) { if (je.getType() == JaspiraEvent.TYPE_FLOOD) { if (passDown(je)) return true; } if (handleEvent(je)) return true; if (getLevel() > je.getLevel() && getParentPlugin() != null) { // Prevent re-entering this part of the plugin tree when the parent plugin passed the event down to its children. je.brand(this); return getParentPlugin().receiveEvent(je); } // Use the event manager to post-handle the event return JaspiraEventMgr.getInstance().postHandleEvent(je); } /** * Receives an event incoming from a parent. * This event is first passed down to the children, than handeled by the plugin itself. * It is not passed upwards. * * @param je The event to handle * @return * true If the event was consumed by the plugin
* false Otherwise */ public boolean inheritEvent(JaspiraEvent je) { if (je.getType() == JaspiraEvent.TYPE_FLOOD) { if (passDown(je)) return true; } if (handleEvent(je)) return true; return false; } /** * Adds a stackable event to the event stack. * If the event is already part of the stack, moves it to the end. * * @param je The event to stack */ public synchronized void stackEvent(JaspiraEvent je) { if (stackPerformer == null) { stackPerformer = new StackPerformer(); } stackPerformer.addEvent(je); } /** * Checks if the event stack contains a particular event. * * @param eventName Event to look for * @return * true The event stack contains an event of this type.
* false The event stack does not contain such an event. */ public synchronized boolean containsStackedEvent(String eventName) { if (stackPerformer != null) return stackPerformer.containsEvent(eventName); return false; } /** * Removes any events with the given name from the event stack. * * @param eventName Event to remove */ public synchronized void removeStackedEvent(String eventName) { if (stackPerformer != null) { stackPerformer.removeEvent(eventName); } } /** * Clears the stack performer. * This is done during stack execution in order to make {@link #containsStackedEvent} * return false (some methods, e. g. those invoked during container rebuild, check if the stack * contains a GEU or GER and will return if so). */ synchronized void clearStackPerformer() { stackPerformer = null; } /** * Restores the stack performer after calling {@link #clearStackPerformer}. * * @param oldStackPerformer Old stack performer */ synchronized void restoreStackPerformer(StackPerformer oldStackPerformer) { // Restore only if no events have been stacked during the call to clearStackPerformer and this method. // Otherwise, continue using the new stack performer if (stackPerformer == null) { stackPerformer = oldStackPerformer; } } /** * Runnable that checks the event stack for stacked events. If found, processes them. */ private class StackPerformer implements Runnable { /** Contains stacked events as name - {@link JaspiraEvent} pair */ private Map eventStack; /** * Constructor. */ StackPerformer() { } /** * Adds a stackable event to the event stack. * If the event is already part of the stack, moves it to the end. * * @param je Event to add */ public synchronized void addEvent(JaspiraEvent je) { if (eventStack == null) { eventStack = new LinkedHashMap(); } if (eventStack.isEmpty()) { // When putting the first event on the event stack, // register the stack performer for later execution SwingUtilities.invokeLater(this); } // Put the event on the event stack, replacing an existing one if present eventStack.put(je.getEventName(), je); } /** * Checks if the event stack contains a particular event. * * @param eventName Event to look for * @return * true The event stack contains an event of this type.
* false The event stack does not contain such an event. */ public boolean containsEvent(String eventName) { if (eventStack != null) { if (eventStack.get(eventName) != null) return true; } return false; } /** * Removes any events with the given name from the event stack. * * @param eventName Event to remove */ public synchronized void removeEvent(String eventName) { if (eventStack != null) { eventStack.remove(eventName); } } /** * Handles the events on the event stack and clears the stack. */ public synchronized void run() { clearStackPerformer(); for (Iterator it = eventStack.values().iterator(); it.hasNext();) { JaspiraEvent je = (JaspiraEvent) it.next(); // Process the event doHandleEvent(je); } // Clear the event stack after execution eventStack.clear(); restoreStackPerformer(this); } } ///////////////////////////////////////////////////////////////////////// // @@ Plugin tree ///////////////////////////////////////////////////////////////////////// /** * Returns a list of all children of this plugin. * @return The child plugin list or null if there are no child plugins */ public List getChildPlugins() { return childPlugins; } /** * Returns a list of all children and further descendants of this plugin. * @param result The children of this plugin will be added to this list, if given * @return The list argument or a new linked list if null */ public List getDescendantPlugins(List result) { if (result == null) { result = new LinkedList(); } if (childPlugins != null) { result.addAll(childPlugins); for (int i = 0; i < childPlugins.size(); ++i) { Plugin child = (Plugin) childPlugins.get(i); child.getDescendantPlugins(result); } } return result; } /** * Returns the parent plugin of this plugin. * Each plugin except {@link ApplicationBase} must have a parent. * * @return Parent of this plugin or null in case of ApplicationBase */ public Plugin getParentPlugin() { return parent; } /** * Sets the parent plugin of this plugin. * Each plugin except {@link ApplicationBase} must have a parent. * * @param newParent New parent of this plugin or null in case of ApplicationBase */ public void setParentPlugin(Plugin newParent) { if (parent != null) { parent.removePlugin(this); } parent = newParent; if (newParent != null) { newParent.addPlugin(this); level = newParent.getLevel() + 1; } else { // Top-level plugin level = 0; } } /** * Adds a plugin as child plugin of this plugin. * * @param child Child plugin to add */ public void addPlugin(Plugin child) { if (childPlugins == null) { childPlugins = new ArrayList(); } childPlugins.add(child); } /** * Removes a child plugin from this plugin. * * @param child Child plugin to remove */ public void removePlugin(Plugin child) { if (childPlugins != null) { childPlugins.remove(child); } } /** * Returns the level of this plugin in the plugin tree. * The level corresponds to the hierarchy level in the plugin tree. The level of * a child plugin is always 1 higher as the level of its parent. * * @return The level with {@link Plugin#LEVEL_APPLICATION} being the lowest. * Defaults to {@link Plugin#LEVEL_PLUGIN}. */ public int getLevel() { return level; } ///////////////////////////////////////////////////////////////////////// // @@ Key sequences ///////////////////////////////////////////////////////////////////////// /** * Invokes the action associated with the given key sequence if applicable. * * @param ks Key sequence to handle * @return True if this sequence has been handeled, false otherwise */ protected boolean handleKeySequence(KeySequence ks) { if (actionsBySequence != null) { JaspiraAction action = (JaspiraAction) actionsBySequence.get(ks); if (action != null && action.isEnabled()) { action.actionPerformed(new ActionEvent(this, 0, null)); return true; } } return false; } ////////////////////////////////////////////////// // @@ Property access ////////////////////////////////////////////////// /** * Gets the name of the resource component the default resource of the plugin belongs to. * @return The container name */ public abstract String getResourceCollectionContainerName(); /** * Gets the plugin resource. */ public ResourceCollection getPluginResourceCollection() { return resourceCollection; } /** * Sets the plugin resource. */ public void setResourceCollection(ResourceCollection resourceCollection) { this.resourceCollection = resourceCollection; } /** * Gets the title. */ public String getTitle() { return title; } /** * Sets the title. */ public void setTitle(String title) { this.title = title; } /** * Returns the sub title of this plugin. * This defaults to the title itself. */ public String getSubTitle() { return getTitle(); } /** * Gets the description. */ public String getDescription() { return description; } /** * Sets the description. */ public void setDescription(String description) { this.description = description; } /** * Gets the icon. */ public MultiIcon getIcon() { return icon; } /** * Sets the icon. */ public void setIcon(MultiIcon icon) { this.icon = icon; } /** * Gets the plugin vendor. */ public String getVendor() { return vendor; } /** * Gets the plugin version. */ public String getVersion() { return version; } /** * Gets the conditional expression that determines if the plugin should be active. */ public String getCondition() { return condition; } /** * Returns the state of this plugin. */ public PluginState getPluginState() { return new PluginState(this); } /** * Rebuilds the state of the plugin using the given state object. */ public void setPluginState(PluginState state) { } ///////////////////////////////////////////////////////////////////////// // @@ Member access ///////////////////////////////////////////////////////////////////////// /** * Returns the name of the plugin. * @return By default, this is the fully qualified class name (see {@link #getClassName}) * unless not otherwise set or specified in the resource file. */ public final String getName() { return name; } /** * Sets the plugin name (can be null). */ public void setName(String name) { this.name = name; } /** * Returns the class name of the plugin. * @return The name of the plugin class (the class that implements this interface) */ public String getClassName() { return getClass().getName(); } /** * Returns the unique id of this plugin. */ public final String getUniqueId() { if (uniqueId == null) { uniqueId = name + ID_DELIMETER + instanceCounter++; } return uniqueId; } /** * Returns a list of the event modules of this plugin. * @return A list of {@link EventModule} objects or null if the plugin does not define event modules as inner classes */ protected final List getEventModules() { return eventModules; } /** * Returns a list of all option modules of this plugin. * * @return A list of OptionModule objects or null */ protected final List getOptionModules() { return optionModules; } /** * Gets a list of the action names of the event actions of this plugin. * @return A list of strings or null if this module does not contain action event handlers */ public List getEventActionNames() { return eventActionNames; } /** * This convenience method retrieves an action from the action manager. * * @param actionName Action name * @return The action or null if no such action exists */ public JaspiraAction getAction(String actionName) { return ActionMgr.getInstance().getAction(actionName); } /** * Convenience method for retrieving an icon from the resourcepackage. * * @param iconName Icon resource name * @return The icon */ public MultiIcon getIcon(String iconName) { return (MultiIcon) getPluginResourceCollection().getRequiredObject(iconName); } ////////////////////////////////////////////////// // @@ Peer groups ////////////////////////////////////////////////// /** * Returns the peer groups this plugin has been added to. * * @return A set of objects denoting the peer groups or null if this plugin does not * belong to any peer group */ public Set getPeerGroups() { if (peerGroups == null || peerGroups.isEmpty()) return null; return new HashSet(peerGroups.values()); } /** * Returns the names of all peergroups that this plugin has been added to. * * @return A set of strings (peer group names) or null if this plugin does not * belong to any peer group */ public Set getPeerGroupNames() { if (peerGroups == null || peerGroups.isEmpty()) return null; return peerGroups.keySet(); } /** * Adds the plugin to a peer group given by key and object. * * @param peerName Peer group name * @param group Peer group object */ public void addToPeerGroup(String peerName, Object group) { if (peerGroups == null) peerGroups = new HashMap(); peerGroups.put(peerName, group); } /** * Gets the peer group for the given group name. * * @param peerName Name of the peer group * @return The peer group or null if this group is not defined for the plugin */ public Object getPeerGroup(String peerName) { return peerGroups != null ? peerGroups.get(peerName) : null; } /** * Removes the plugin from a peer group. * * @param peerName Peer group name */ public void removeFromPeerGroup(String peerName) { peerGroups.remove(peerName); } /** * Check if the plugin is a member of the given peer group. * Eithe the group or the group name must be given. * * @param peerName Name of the peer group to check * @param group Peer group to check * @param strict * true The peergroups must be exactly met.
* false null as group on one side is sufficient. */ public boolean matchesPeerGroup(String peerName, Object group, boolean strict) { // Null check if (peerGroups == null || peerGroups.isEmpty()) { if (! strict) // Null on either side matches return true; // No exact match possible return false; } if (group == null) { if (! strict) // Null on either side matches return true; // Only return true, if group is NOT set return ! peerGroups.containsKey(peerName); } // peergroups contains name - check if value is equal or null. // null is allowed and returns true Object o = peerGroups.get(peerName); if (o == null && ! strict) // Null on either side matches return true; return group.equals(o); } /** * Matches all peer groups of one plugin against another. * * @param plugin Plugin to match agains this plugin * @param strict * true All peer groups must match.
* false A single peer group must match. * @return * true If both plugins contain at least one common peergroup.
* If the strict parameter is true, there may not be any different groups.
* Plugins with empty peergroups ALWAYS match! * false Otherwise */ public boolean matchesPeerGroups(Plugin plugin, boolean strict) { // TODO Refactor 6: This will probably not work due to changes in the peer group stuff; however, peer groups are not used currently; revise and adjust if (peerGroups == null || peerGroups.isEmpty()) return true; Set those = plugin.getPeerGroups(); if (those == null || those.isEmpty()) return true; // We check for a single match. those.retainAll(peerGroups.entrySet()); if (those.isEmpty()) // No common elements return false; if (! strict) // not strict: we are finished! return true; // Determine relevant keys Set myKeys = getPeerGroupNames(); myKeys.retainAll(plugin.getPeerGroupNames()); // Check for other peer groups for (Iterator it = myKeys.iterator(); it.hasNext();) { String next = (String) it.next(); if (! plugin.matchesPeerGroup(next, getPeerGroup(next), false)) return false; } return false; } ///////////////////////////////////////////////////////////////////////// // @@ Module and action initialization ///////////////////////////////////////////////////////////////////////// /** * Initialises the option modules of this plugin. */ private final void installOptionModules() { optionModules = new ArrayList(1); // Strategy: we muster search for any inner classes that are // descendants of OptionModule (equals to installEventModules) Class[] innerClasses = getClass().getClasses(); for (int i = 0; i < innerClasses.length; i++) { if (OptionModule.class.isAssignableFrom(innerClasses[i])) { try { // We retrieve the Constructor of the module that uses one single parameter // of the type of our enclosing class (i.e. getClass ()) // Note that this constructor is an implicit, private constructor of the // inner class mechanism. Constructor ctor = innerClasses[i].getDeclaredConstructors()[0]; // Create a new instance of the module using us a reference parameter. OptionModule module = (OptionModule) ctor.newInstance(new Object[] { this }); module.install(); // add the module to the module list optionModules.add(module); } catch (InvocationTargetException e) { ExceptionUtil.printTrace(e); } catch (InstantiationException e) { ExceptionUtil.printTrace(e); } catch (IllegalAccessException e) { ExceptionUtil.printTrace(e); } } } // Initialize external modules Class[] externalModules = getExternalOptionModuleClasses(); if (externalModules != null) { for (int i = 0; i < externalModules.length; ++i) { Class cls = externalModules[i]; // Instantiate the event module class try { // Use the constructor that takes a plugin as parameter for instantiation Constructor ctor = cls.getConstructor(new Class[] { Plugin.class, }); // Create a new instance of the module using us a reference parameter. ExternalOptionModule module = (ExternalOptionModule) ctor.newInstance(new Object[] { this, }); module.install(); // Add the module to the module list optionModules.add(module); } catch (InstantiationException e) { ExceptionUtil.printTrace(e); } catch (IllegalAccessException e) { ExceptionUtil.printTrace(e); } catch (InvocationTargetException e) { ExceptionUtil.printTrace(e); } catch (NoSuchMethodException e) { ExceptionUtil.printTrace(e); } catch (ClassCastException e) { ExceptionUtil.printTrace(e); } } } } /** * Uninstalls the option modules of this plugin. */ private void uninstallOptionModules() { if (optionModules != null) { for (Iterator it = optionModules.iterator(); it.hasNext();) { ((OptionModule) it.next()).uninstall(); } } } /** * Initializes the event modules of this plugin. */ private final void installEventModules() { eventModules = new ArrayList(2); // Strategy: we muster search for any inner classes that are // descendants of EventModule Class[] innerClasses = getClass().getClasses(); for (int i = 0; i < innerClasses.length; i++) { Class cls = innerClasses[i]; if (EventModule.class.isAssignableFrom(cls)) { // Instantiate the event module class try { // We retrieve the Constructor of the module that uses one single parameter // of the type of our enclosing class (i.e. getClass ()) // Note that this constructor is an implicit, private constructor of the // inner class mechanism. Constructor ctor = cls.getDeclaredConstructors()[0]; // Create a new instance of the module using us a reference parameter. EventModule module = (EventModule) ctor.newInstance(new Object[] { this }); // Add the module to the module list eventModules.add(module); } catch (InvocationTargetException e) { ExceptionUtil.printTrace(e); } catch (InstantiationException e) { ExceptionUtil.printTrace(e); } catch (IllegalAccessException e) { ExceptionUtil.printTrace(e); } } } // Initialize external modules Class[] externalModules = getExternalEventModuleClasses(); if (externalModules != null) { for (int i = 0; i < externalModules.length; ++i) { Class cls = externalModules[i]; // Instantiate the event module class try { // Use the constructor that takes a plugin as parameter for instantiation Constructor ctor = cls.getConstructor(new Class[] { Plugin.class, }); // Create a new instance of the module using us a reference parameter. ExternalEventModule module = (ExternalEventModule) ctor.newInstance(new Object[] { this, }); // Add the module to the module list eventModules.add(module); } catch (InstantiationException e) { ExceptionUtil.printTrace(e); } catch (IllegalAccessException e) { ExceptionUtil.printTrace(e); } catch (InvocationTargetException e) { ExceptionUtil.printTrace(e); } catch (NoSuchMethodException e) { ExceptionUtil.printTrace(e); } catch (ClassCastException e) { ExceptionUtil.printTrace(e); } } } // Register the modules as event listeners if (eventModules != null) { for (Iterator it = eventModules.iterator(); it.hasNext();) { EventModule module = (EventModule) it.next(); if (module.getModuleType() != EventModule.MODULE_TREE) { // Global modules need to be registered with the event manager JaspiraEventMgr.getInstance().registerModule(module); continue; } // Local modules are registered with the plugin itself // Add to the module group list, so we have access to the local events // (i. e. event methods w/o '_') if (eventgroups == null) { eventgroups = new HashMap(); } String moduleName = module.getName(); Set modules = (Set) eventgroups.get(moduleName); if (modules == null) { modules = new TreeSet(JaspiraEventMgr.moduleComparator); eventgroups.put(moduleName, modules); } modules.add(module); // Add foreign events List foreignEventNames = module.getForeignEventNames(); if (foreignEventNames != null) { if (foreignEvents == null) { foreignEvents = new HashMap(); } int n = foreignEventNames.size(); for (int i = 0; i < n; ++i) { String event = (String) foreignEventNames.get(i); Set foreignModules = (Set) foreignEvents.get(event); if (foreignModules == null) { foreignModules = new TreeSet(JaspiraEventMgr.moduleComparator); foreignEvents.put(event, foreignModules); } foreignModules.add(module); } } } } } /** * Uninstalls the option modules of this plugin. */ private void uninstallEventModules() { for (Iterator iter = getEventModules().iterator(); iter.hasNext();) { EventModule module = (EventModule) iter.next(); if (module.getModuleType() != EventModule.MODULE_TREE) { JaspiraEventMgr.getInstance().unregisterModule(module); } } } /** * Initializes the actions of this plugin. * Creates an action and adds it to the action manager for each method of the event modules * of the plugin that returns an Integer and accepts a {@link JaspiraActionEvent} parameter. */ private final void installActions() { // Register the actions of the event modules if (eventModules != null) { for (Iterator it = eventModules.iterator(); it.hasNext();) { EventModule module = (EventModule) it.next(); // An through all EventActions of the modules List actionNames = module.getEventActionNames(); if (actionNames != null) { int n = actionNames.size(); for (int i = 0; i < n; i++) { String actionName = (String) actionNames.get(i); JaspiraAction action = new JaspiraAction(this, actionName); if (installAction(action)) { if (eventActionNames == null) { eventActionNames = new ArrayList(); } eventActionNames.add(actionName); } } } } } // Register the user actions Collection externalActions = getExternalActions(); if (externalActions != null) { for (Iterator it = externalActions.iterator(); it.hasNext();) { JaspiraAction action = (JaspiraAction) it.next(); installAction(action); } } // Register the action key sequences with the key manager if (actionsBySequence != null) { KeyMgr.getInstance().addSequences(actionsBySequence.keySet().iterator()); } } /** * Registers an action of the plugin if the action condition evalueates to true. * * @param action Action * @return * true If the action was installed.
* false If the condition evluated to false. */ private boolean installAction(JaspiraAction action) { if (! ConfigMgr.getInstance().evaluate(action.getCondition())) // Action condition evaluation failed, don't create return false; KeySequence[] sequence = action.getKeySequences(); if (sequence != null && sequence.length > 0) { for (int j = 0; j < sequence.length; j++) { addActionKeySequence(sequence[j], action); } } ActionMgr.getInstance().addAction(action); return true; } /** * Unregister the actions of the plugin from the keyboard/action manager. */ private void uninstallActions() { // Unregister the action key sequences from the key manager if (actionsBySequence != null) { KeyMgr.getInstance().removeSequences(actionsBySequence.keySet().iterator()); } ActionMgr actionMgr = ActionMgr.getInstance(); // Unregister the actions of the event modules if (eventActionNames != null) { for (Iterator it = eventActionNames.iterator(); it.hasNext();) { actionMgr.removeAction((String) it.next()); } } // Unregister the user actions actionMgr.removeAllActions(getExternalActions()); } ///////////////////////////////////////////////////////////////////////// // @@ Close support ///////////////////////////////////////////////////////////////////////// /** * Checks, whether this plugin can be closed. Note that this method * does not necessarily mean that the plugin WILL be closed. Overwrite this * method to handle unsafe states or user interaction. Calling of this method * is always followed by either {@link #preClose} or {@link #closeCanceled}. * @return True if this plugin can be closed right now */ public boolean canClose() { return true; } /** * Request the plugin to be closed. * The plugin should check whether this is currently possible an if so do it. * * The default implementation will issue a global.askclose veto event to all its * child plugins and itself.
* If this event has been vetoed, it will broadcast a global.closecanceled event * to itself and the childs. Otherwise, it will bradcast a global.doclose event, * which in turn will call {@link #preClose} and then remove the plugin instance. * So if a plugin is being closed, all its children will be closed, too. * * @return * true If the plugin has been closed
* false Otherwise */ public boolean requestClose() { VetoableEvent ve = new VetoableEvent(this, "global.askclose"); inheritEvent(ve); if (ve.isVetoed()) { inheritEvent(new JaspiraEvent(this, "global.closecanceled")); return false; } // The event handlers of the global.doclose event will add Runnables that // perform the actual call to the {@link #doClose} method. // This delayed execution has been chosen to prevent problems when shutting down the application StackActionEvent sae = new StackActionEvent(this, "global.doclose"); inheritEvent(sae); sae.performActions(); return true; } /** * Closes a plugin. * The method calls the {@link #preClose} template method and then removes itself from the plugin manager. * In order to implement custom behaviour, overwrite preClose (). */ void doClose() { preClose(); PluginMgr.getInstance().removeInstance(this); } /** * This method is called when actual closing is performed. */ protected void preClose() { } /** * This method is called when the closing has been vetoed by a plugin. */ protected void closeCanceled() { } ///////////////////////////////////////////////////////////////////////// // @@ Standard event module ///////////////////////////////////////////////////////////////////////// /** * Event module. */ public class StandardPluginEvents extends EventModule { public String getName() { return "global.page"; } /** * Event handler: Checks if this plugin can be closed. * * @event global.askclose * @param ve Event * @return The event status code */ public JaspiraEventHandlerCode global_askclose(VetoableEvent ve) { if (! canClose()) { ve.veto(); return EVENT_CONSUMED; } return EVENT_HANDLED; } /** * Event handler: Closes the plugin. * * @event global.doclose * @param ve Event * @return The event status code */ public JaspiraEventHandlerCode global_doclose(StackActionEvent ve) { ve.addAction(new Runnable() { public void run() { doClose(); } }); return EVENT_HANDLED; } /** * Event handler: Called when the close operation has been vetoed by a plugin. * * @event global.closecanceled * @param ve Event * @return The event status code */ public JaspiraEventHandlerCode global_closecanceled(JaspiraEvent ve) { closeCanceled(); return EVENT_HANDLED; } /** * Event handler: Receives key sequence events and passes them to the Plugin itself. * * @event global.keyevent * @param kse Event * @return The event status code
* Consumes the key event if the sequence can be handled by this plugin. */ public JaspiraEventHandlerCode global_keyevent(KeySequenceEvent kse) { return handleKeySequence(kse.getKeySequence()) ? EventModule.EVENT_CONSUMED : EventModule.EVENT_IGNORED; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy