/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*
* (C) Copyright 2001-2004, by :
* Corporate:
* EADS Corporate Research Center
* Individual:
* Nicolas Brodu
*
* $Id: PluginManager.java,v 1.5 2005/09/02 11:57:30 ogor Exp $
*
* Changes
* -------
* 19-Mar-2004 : Creation date (NB);
*
*/
package syn3d.base;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import syn3d.nodes.NodeResourcesManager;
/**
* This is a central repository for Syn3D plugins
*
* @author nicolas brodu
*/
public class PluginManager {
/** Vector of Syn3DPlugin objects, correspongin to all plugins already registerd */
public Vector plugins;
/**
* Vector of NodeFactory objects, constructed from the plugins capacities
* This Vector is guaranteed to be alphabetically sorted
*/
public Vector nodeFactories;
public PluginManager(){
plugins = new Vector();
nodeFactories = new Vector();
}
public void register(String className) {
Class c;
try {
c = Class.forName(className);
}
catch (ClassNotFoundException cnfe) {
return;
}
// Avoid duplicates. Can't use Set as we want to avoid duplication of class, not instance...
if (getPlugin(c)!=null) return; // plugin already loaded
Object o;
try {
o = c.newInstance();
} catch (Exception e) {
return;
}
if (o==null) return;
if (!(o instanceof Syn3DPlugin)) return;
// Now add this new plugin
Syn3DPlugin p = (Syn3DPlugin)o;
p.setPluginManager(this);
plugins.add(p);
String[] nodeTypes = p.getNodes();
// Insert the new factories in alpha order
for (int i=0; i<nodeTypes.length; ++i) {
int insertPoint = 0;
for(;insertPoint<nodeFactories.size(); ++insertPoint) {
if (nodeFactories.get(insertPoint).toString().compareTo(nodeTypes[i]) > 0) break;
}
nodeFactories.add(insertPoint, new NodeFactory(p,nodeTypes[i]));
}
};
public Syn3DPlugin getPlugin(Class c) {
for (Iterator it = plugins.iterator(); it.hasNext();) {
Syn3DPlugin p = (Syn3DPlugin)it.next();
if (p.getClass().equals(c)) return p;
}
return null;
}
/** Return a Vector of NodeFactory objects capable of creating a child for the parent, in alphabetical order.
* @param parent The ActiveNode for which a new child is desired
* @return a Vector, possibly empty, of NodeFactory objects capable of creating a child for the parent.
*/
public Vector getNodeFactoriesFor(ActiveNode parent) {
Vector ret = new Vector();
for (Iterator it = nodeFactories.iterator(); it.hasNext();) {
NodeFactory nf = (NodeFactory)it.next();
if (nf.canCreate(parent)) ret.add(nf);
}
return ret;
}
/**
* Utility wrapper around getNodeFactoriesFor method to create a ready to use popup menu.
* If the user selects an entry in the popup menu, the corresponding child will be created
* in the parent, and the given listener will be notified.
* @param parent The parent node for for which a new child is desired
* @param listener An optional listener (possibly null) which will be notified if a child was created.
* The corresponding ActionEvent source is the new child, id = 1, and cmd = "childCreated". Use
* (ActiveNode(child)).getParent() on to get the parent back, if needed.
* @return A ready to use popup menu, or null if no child can be created for this parent. The menu
* is ready to be shown, but the caller may add more entries if necessary.
*/
public JPopupMenu getChildMenu(final ActiveNode parent, final ActionListener listener) {
JPopupMenu ret = new JPopupMenu();
Vector factories = getNodeFactoriesFor(parent);
if (factories.size()==0) return null;
Vector currentItems = new Vector();
// Process all factories to build the popup menu
for (Iterator it = factories.iterator(); it.hasNext();) {
final NodeFactory nf = (NodeFactory)it.next();
String type = nf.getNodeType();
// Dispatch entries using the "|" separators to build sub-menus
StringTokenizer st = new StringTokenizer(type,"|");
for (int depth = 0; st.hasMoreTokens(); ++depth) {
String token = st.nextToken();
JMenuItem item;
// If more token exists, then this entry is a submenu, not just a single item
if (st.hasMoreTokens()) {
// If the menu already exists continue
if ((depth<currentItems.size())
&& (currentItems.get(depth) instanceof JMenu)
&& ((JMenu)currentItems.get(depth)).getText().equals(token))
continue;
// The menu does not correspond. Create a new one
item = new JMenu(token);
}
// So, it is a single item, either the last item in the list, or a at top-level
else {
item = new JMenuItem(token);
// This is a single item, it can lead to child creation => add the adequate listener
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ActiveNode child = nf.create(parent);
if(child!=null){
listener.actionPerformed(new ActionEvent(child,1,"childCreated"));
}
}
});
}
if (depth==0) {
// first entry => use top-level popup menu
ret.add(item);
// Remove any item in the list
currentItems.clear();
} else {
// previous item is menu by construction. Add this new item
((JMenu)currentItems.get(depth-1)).add(item);
// remove trailing items, including the old menu at this depth
while (depth<currentItems.size()) currentItems.remove(depth);
}
// Add this menu at its right place in all cases
currentItems.add(item);
}
}
// finished!
return ret;
}
public void showProperties(final ActiveNode parent, final ActionListener listener) {
parent.doAction(NodeResourcesManager.getResources().getString("Properties"));
}
}