/*FreeMind - A Program for creating and viewing Mindmaps
*Copyright (C) 2000-2001 Joerg Mueller <joergmueller@bigfoot.com>
*See COPYING for Details
*
*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 freemind.modes.mindmapmode.hooks;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;
import org.jibx.runtime.IUnmarshallingContext;
import freemind.common.XmlBindingTools;
import freemind.controller.actions.generated.instance.Plugin;
import freemind.controller.actions.generated.instance.PluginAction;
import freemind.controller.actions.generated.instance.PluginClasspath;
import freemind.controller.actions.generated.instance.PluginMode;
import freemind.controller.actions.generated.instance.PluginRegistration;
import freemind.extensions.HookDescriptorPluginAction;
import freemind.extensions.HookDescriptorRegistration;
import freemind.extensions.HookFactoryAdapter;
import freemind.extensions.HookInstanciationMethod;
import freemind.extensions.ImportWizard;
import freemind.extensions.MindMapHook;
import freemind.extensions.MindMapHook.PluginBaseClassSearcher;
import freemind.extensions.ModeControllerHook;
import freemind.extensions.NodeHook;
import freemind.main.FreeMindMain;
import freemind.modes.mindmapmode.MindMapController;
/**
* @author christianfoltin
*
* @file HookFactory.java
* @package freemind.modes
*/
/**
* @author foltin
*
*/
public class MindMapHookFactory extends HookFactoryAdapter {
/**
* Match xml files in the accessories/plugin directory and not in its
* subdirectories.
*/
private final static String pluginPrefixRegEx = ".*(accessories(/|\\\\)|)plugins(/|\\\\)[^/\\\\]*";
private FreeMindMain frame;
// Logging:
private java.util.logging.Logger logger;
private static HashMap pluginInfo = null;
private static Vector allPlugins = null;
private static ImportWizard importWizard = null;
/** Contains PluginRegistrationType -> PluginType relations. */
protected static HashSet allRegistrations;
/**
*
*/
public MindMapHookFactory(FreeMindMain frame) {
this.frame = frame;
logger = frame.getLogger(this.getClass().getName());
allRegistrationInstances = new HashMap();
}
/**
* @return a string vector with representatives for plugins.
*/
public Vector getPossibleNodeHooks() {
return searchFor(NodeHook.class, MindMapController.class);
}
/**
* @return a string vector with representatives for plugins.
*/
public Vector getPossibleModeControllerHooks() {
return searchFor(ModeControllerHook.class, MindMapController.class);
}
/**
* @return a string vector with representatives for plugins.
*/
private Vector searchFor(Class baseClass, Class mode) {
actualizePlugins();
Vector returnValue = new Vector();
String modeName = mode.getPackage().getName();
for (Iterator i = allPlugins.iterator(); i.hasNext();) {
String label = (String) i.next();
HookDescriptorPluginAction descriptor = getHookDescriptor(label);
// Properties prop = descriptor.properties;
try {
logger.finest("Loading: " + label);
if (baseClass.isAssignableFrom(Class.forName(descriptor
.getBaseClass()))) {
// the plugin inherits from the baseClass, we carry on to
// look for the mode
for (Iterator j = descriptor.getModes().iterator(); j
.hasNext();) {
String pmode = (String) j.next();
if (pmode.equals(modeName)) {
// add the class:
returnValue.add(label);
}
}
}
} catch (ClassNotFoundException e) {
logger.severe("Class not found.");
freemind.main.Resources.getInstance().logException(e);
}
}
return returnValue;
}
/**
*
*/
private void actualizePlugins() {
if (importWizard == null) {
importWizard = new ImportWizard(frame);
importWizard.CLASS_LIST.clear();
importWizard.buildClassList();
pluginInfo = new HashMap();
allPlugins = new Vector();
allRegistrations = new HashSet();
// the unmarshaller:
IUnmarshallingContext unmarshaller = XmlBindingTools.getInstance()
.createUnmarshaller();
// the loop
for (Iterator i = importWizard.CLASS_LIST.iterator(); i.hasNext();) {
String xmlPluginFile = (String) i.next();
if (xmlPluginFile.matches(pluginPrefixRegEx)) {
// make file name:
/*
* Here, this is not the File.separatorChar!!!
*/
xmlPluginFile = xmlPluginFile.replace('\\', '/')
+ importWizard.lookFor;
// this is one of our plugins:
URL pluginURL = frame.getFreeMindClassLoader().getResource(
xmlPluginFile);
// unmarshal xml:
Plugin plugin = null;
try {
logger.finest("Reading: " + xmlPluginFile + " from "
+ pluginURL);
InputStream in = pluginURL.openStream();
plugin = (Plugin) unmarshaller.unmarshalDocument(in,
null);
} catch (Exception e) {
// error case
freemind.main.Resources.getInstance().logException(e);
continue;
}
// plugin is loaded.
for (Iterator j = plugin.getListChoiceList().iterator(); j
.hasNext();) {
Object obj = j.next();
if (obj instanceof PluginAction) {
PluginAction action = (PluginAction) obj;
pluginInfo.put(action.getLabel(),
new HookDescriptorPluginAction(frame,
xmlPluginFile, plugin, action));
allPlugins.add(action.getLabel());
} else if (obj instanceof PluginRegistration) {
PluginRegistration registration = (PluginRegistration) obj;
allRegistrations
.add(new HookDescriptorRegistration(frame,
xmlPluginFile, plugin, registration));
// logger.info("Added registration " +
// registration.getClassName() +
// " to allRegistrations=" + allRegistrations);
}
}
}
}
}
}
public ModeControllerHook createModeControllerHook(String hookName) {
HookDescriptorPluginAction descriptor = getHookDescriptor(hookName);
return (ModeControllerHook) createJavaHook(hookName, descriptor);
}
private MindMapHook createJavaHook(String hookName,
HookDescriptorPluginAction descriptor) {
try {
// constructed.
ClassLoader loader = descriptor.getPluginClassLoader();
Class hookClass = Class.forName(descriptor.getClassName(), true,
loader);
MindMapHook hook = (MindMapHook) hookClass.newInstance();
decorateHook(hookName, descriptor, hook);
return hook;
} catch (Throwable e) {
String path = "";
for (Iterator it = descriptor.getPluginClasspath().iterator(); it.hasNext();) {
PluginClasspath plPath = (PluginClasspath) it.next();
path += plPath.getJar() + ";";
}
freemind.main.Resources.getInstance().logException(
e,
"Error occurred loading hook: " + descriptor.getClassName()
+ "\nClasspath: " + path + "\nException:");
return null;
}
}
/**
* Do not call this method directly. Call ModeController.createNodeHook
* instead.
*/
public NodeHook createNodeHook(String hookName) {
logger.finest("CreateNodeHook: " + hookName);
HookDescriptorPluginAction descriptor = getHookDescriptor(hookName);
return (NodeHook) createJavaHook(hookName, descriptor);
}
private void decorateHook(String hookName,
final HookDescriptorPluginAction descriptor, MindMapHook hook) {
hook.setProperties(descriptor.getProperties());
hook.setName(hookName);
PluginBaseClassSearcher pluginBaseClassSearcher = new PluginBaseClassSearcher() {
public Object getPluginBaseObject() {
return getPluginBaseClass(descriptor);
}
};
hook.setPluginBaseClass(pluginBaseClassSearcher);
}
/**
*/
public void decorateAction(String hookName, AbstractAction action) {
HookDescriptorPluginAction descriptor = getHookDescriptor(hookName);
String name = descriptor.getName();
if (name != null) {
action.putValue(AbstractAction.NAME, name);
} else {
action.putValue(AbstractAction.NAME, descriptor.getClassName());
}
String docu = descriptor.getDocumentation();
if (docu != null) {
action.putValue(AbstractAction.SHORT_DESCRIPTION, docu);
action.putValue(AbstractAction.LONG_DESCRIPTION, docu);
}
String icon = descriptor.getIconPath();
if (icon != null) {
ImageIcon imageIcon = new ImageIcon(descriptor
.getPluginClassLoader().getResource(icon));
action.putValue(AbstractAction.SMALL_ICON, imageIcon);
}
String key = descriptor.getKeyStroke();
if (key != null)
action.putValue(AbstractAction.ACCELERATOR_KEY,
KeyStroke.getKeyStroke(key));
}
/**
* @return returns a list of menu position strings for the
* StructuredMenuHolder.
*/
public List getHookMenuPositions(String hookName) {
HookDescriptorPluginAction descriptor = getHookDescriptor(hookName);
return descriptor.menuPositions;
}
/**
*/
public HookInstanciationMethod getInstanciationMethod(String hookName) {
HookDescriptorPluginAction descriptor = getHookDescriptor(hookName);
return descriptor.getInstanciationMethod();
}
/**
* Each Plugin can have a list of HookRegistrations that are called after
* the corresponding mode is enabled. (Like singletons.) One of these can
* operate as the pluginBase that is accessible to every normal
* plugin_action via the getPluginBaseClass method.
*
* @return A list of RegistrationContainer elements. The field
* hookRegistrationClass of RegistrationContainer is a class that is
* (probably) of HookRegistration type. You have to register every
* registration via the registerRegistrationContainer method when
* instanciated (this is typically done in the ModeController).
*/
public List getRegistrations() {
Class mode = MindMapController.class;
actualizePlugins();
Vector returnValue = new Vector();
for (Iterator i = allRegistrations.iterator(); i.hasNext();) {
HookDescriptorRegistration descriptor = (HookDescriptorRegistration) i
.next();
// PluginRegistration registration =
// descriptor.getPluginRegistration();
boolean modeFound = false;
for (Iterator j = (descriptor.getListPluginModeList()).iterator(); j
.hasNext();) {
PluginMode possibleMode = (PluginMode) j.next();
if (mode.getPackage().getName()
.equals(possibleMode.getClassName())) {
modeFound = true;
}
}
if (!modeFound)
continue;
try {
Plugin plugin = descriptor.getPluginBase();
ClassLoader loader = descriptor.getPluginClassLoader();
Class hookRegistrationClass = Class.forName(
descriptor.getClassName(), true, loader);
RegistrationContainer container = new RegistrationContainer();
container.hookRegistrationClass = hookRegistrationClass;
container.correspondingPlugin = plugin;
container.isPluginBase = descriptor.getIsPluginBase();
returnValue.add(container);
} catch (ClassNotFoundException e) {
freemind.main.Resources.getInstance().logException(e);
}
}
return returnValue;
}
/**
* A plugin base class is a common registration class of multiple plugins.
* It is useful to embrace several related plugins (example: EncryptedNote
* -> Registration).
*
* @return the base class if declared and successfully instanciated or NULL.
*/
public Object getPluginBaseClass(String hookName) {
logger.finest("getPluginBaseClass: " + hookName);
HookDescriptorPluginAction descriptor = getHookDescriptor(hookName);
return getPluginBaseClass(descriptor);
}
/**
*/
private Object getPluginBaseClass(HookDescriptorPluginAction descriptor) {
// test if registration is present:
Object baseClass = null;
String label = descriptor.getPluginBase().getLabel();
if (allRegistrationInstances.containsKey(label)) {
baseClass = allRegistrationInstances.get(label);
}
return baseClass;
}
/**
*/
private HookDescriptorPluginAction getHookDescriptor(String hookName) {
HookDescriptorPluginAction descriptor = (HookDescriptorPluginAction) pluginInfo
.get(hookName);
if (hookName == null || descriptor == null)
throw new IllegalArgumentException("Unknown hook name " + hookName);
return descriptor;
}
public JMenuItem getMenuItem(String pHookName, AbstractAction pHookAction) {
HookDescriptorPluginAction descriptor = getHookDescriptor(pHookName);
if (descriptor.isSelectable()) {
return new JCheckBoxMenuItem(pHookAction);
} else {
return new JMenuItem(pHookAction);
}
}
}