Guice module for loading plugins
This commit is contained in:
parent
640cbc1179
commit
5622b4a2b5
@ -30,5 +30,9 @@
|
|||||||
<classpathentry kind="lib" path="lib/jogl/gluegen-rt.jar"/>
|
<classpathentry kind="lib" path="lib/jogl/gluegen-rt.jar"/>
|
||||||
<classpathentry kind="lib" path="lib/jogl/jogl.all.jar"/>
|
<classpathentry kind="lib" path="lib/jogl/jogl.all.jar"/>
|
||||||
<classpathentry kind="lib" path="lib/OrangeExtensions-1.2.jar"/>
|
<classpathentry kind="lib" path="lib/OrangeExtensions-1.2.jar"/>
|
||||||
|
<classpathentry kind="lib" path="lib/guice-3.0.jar"/>
|
||||||
|
<classpathentry kind="lib" path="lib/guice-multibindings-3.0.jar"/>
|
||||||
|
<classpathentry kind="lib" path="lib/javax.inject.jar"/>
|
||||||
|
<classpathentry kind="lib" path="lib/aopalliance.jar"/>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
BIN
core/example.jar
Normal file
BIN
core/example.jar
Normal file
Binary file not shown.
BIN
core/lib/aopalliance.jar
Normal file
BIN
core/lib/aopalliance.jar
Normal file
Binary file not shown.
BIN
core/lib/guice-3.0.jar
Normal file
BIN
core/lib/guice-3.0.jar
Normal file
Binary file not shown.
BIN
core/lib/guice-multibindings-3.0.jar
Normal file
BIN
core/lib/guice-multibindings-3.0.jar
Normal file
Binary file not shown.
BIN
core/lib/javax.inject.jar
Normal file
BIN
core/lib/javax.inject.jar
Normal file
Binary file not shown.
@ -1,16 +0,0 @@
|
|||||||
package net.sf.openrocket.gui.plugin;
|
|
||||||
|
|
||||||
import javax.swing.Action;
|
|
||||||
|
|
||||||
import net.sf.openrocket.document.OpenRocketDocument;
|
|
||||||
import net.sf.openrocket.gui.main.BasicFrame;
|
|
||||||
|
|
||||||
public class DoSomethingPlugin extends OpenRocketSwingMenuPlugin {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Action getAction(BasicFrame frame, OpenRocketDocument document) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package net.sf.openrocket.gui.plugin;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.sf.openrocket.plugin.framework.Service;
|
|
||||||
|
|
||||||
public abstract class OpenRocketSwingMenuPlugin implements Service, SwingMenuPlugin {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getMenuPosition() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <E> List<E> getPlugins(Class<E> type, Object... args) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
package net.sf.openrocket.gui.plugin;
|
|
||||||
|
|
||||||
import javax.swing.Action;
|
|
||||||
|
|
||||||
import net.sf.openrocket.document.OpenRocketDocument;
|
|
||||||
import net.sf.openrocket.gui.main.BasicFrame;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A plugin that provides a menu item to the Swing GUI menus.
|
|
||||||
* This may open a dialog window or perform some other action on
|
|
||||||
* the current document.
|
|
||||||
* <p>
|
|
||||||
* During plugin discovery, the BasicFrame and OpenRocketDocument
|
|
||||||
* objects are passed to the plugin.
|
|
||||||
*
|
|
||||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
|
||||||
*/
|
|
||||||
public interface SwingMenuPlugin {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the menu position where the action is placed.
|
|
||||||
* The first string in the array indicates the menu to place
|
|
||||||
* the item in, the second is the sub-menu, the third is the
|
|
||||||
* sub-sub-menu etc.
|
|
||||||
* <p>
|
|
||||||
* The strings are translated menu names.
|
|
||||||
*
|
|
||||||
* @return the menu position for the action
|
|
||||||
*/
|
|
||||||
public String[] getMenuPosition();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the Action that the menu item performs. This contains
|
|
||||||
* the menu item text and may contain an icon.
|
|
||||||
*
|
|
||||||
* @return the action to perform on the menu item.
|
|
||||||
*/
|
|
||||||
public Action getAction(BasicFrame frame, OpenRocketDocument document);
|
|
||||||
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
package net.sf.openrocket.plugin;
|
|
||||||
|
|
||||||
public interface Configurable {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the plugin ID. This is a text string uniquely identifying this plugin.
|
|
||||||
* The recommended format is similar to the fully-qualified class name of the
|
|
||||||
* plugin, though a shorter format starting with the developer's domain name
|
|
||||||
* is also possible for future compatibility.
|
|
||||||
*
|
|
||||||
* @return the plugin ID
|
|
||||||
*/
|
|
||||||
public String getPluginID();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test whether this plugin provides functionality corresponding to the specified
|
|
||||||
* plugin ID. This provides backwards compatibility if the plugin ID should change.
|
|
||||||
*
|
|
||||||
* @param pluginID the plugin ID to test
|
|
||||||
* @return whether this plugin provides the requested functionality
|
|
||||||
*/
|
|
||||||
public boolean isCompatible(String pluginID);
|
|
||||||
|
|
||||||
public void loadFromXML(Object... objects);
|
|
||||||
|
|
||||||
public void saveToXML(Object... objects);
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,10 @@
|
|||||||
|
package net.sf.openrocket.plugin;
|
||||||
|
|
||||||
|
public class CopyOfExamplePluginImpl implements ExamplePlugin {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doit() {
|
||||||
|
System.out.println("CopyOfExamplePluginImpl.doit() called");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
8
core/src/net/sf/openrocket/plugin/ExamplePlugin.java
Normal file
8
core/src/net/sf/openrocket/plugin/ExamplePlugin.java
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package net.sf.openrocket.plugin;
|
||||||
|
|
||||||
|
@Plugin
|
||||||
|
public interface ExamplePlugin {
|
||||||
|
|
||||||
|
public void doit();
|
||||||
|
|
||||||
|
}
|
10
core/src/net/sf/openrocket/plugin/ExamplePluginImpl.java
Normal file
10
core/src/net/sf/openrocket/plugin/ExamplePluginImpl.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package net.sf.openrocket.plugin;
|
||||||
|
|
||||||
|
public class ExamplePluginImpl implements ExamplePlugin {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doit() {
|
||||||
|
System.out.println("ExamplePluginImpl.doit() called");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
19
core/src/net/sf/openrocket/plugin/Plugin.java
Normal file
19
core/src/net/sf/openrocket/plugin/Plugin.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package net.sf.openrocket.plugin;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation that defines an interface to be a plugin interface.
|
||||||
|
* Plugin interfaces are automatically discovered from plugin JARs and
|
||||||
|
* registered as plugins in Guice.
|
||||||
|
*
|
||||||
|
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||||
|
*/
|
||||||
|
@Target(value = { ElementType.TYPE })
|
||||||
|
@Retention(value = RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Plugin {
|
||||||
|
|
||||||
|
}
|
122
core/src/net/sf/openrocket/plugin/PluginModule.java
Normal file
122
core/src/net/sf/openrocket/plugin/PluginModule.java
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package net.sf.openrocket.plugin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.multibindings.Multibinder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guice module definition that searches for plugins in a list of provided
|
||||||
|
* JAR files and registers each found plugin to the corresponding plugin interface.
|
||||||
|
*
|
||||||
|
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||||
|
*/
|
||||||
|
public class PluginModule extends AbstractModule {
|
||||||
|
|
||||||
|
private final File[] jars;
|
||||||
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
|
private Map<Class<?>, Multibinder<?>> binders = new HashMap<Class<?>, Multibinder<?>>();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sole constructor.
|
||||||
|
*
|
||||||
|
* @param jars the JAR files to search for plugins
|
||||||
|
* @param classLoader the class loader used to load classes from the JAR files
|
||||||
|
*/
|
||||||
|
public PluginModule(File[] jars, ClassLoader classLoader) {
|
||||||
|
this.jars = jars.clone();
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
for (File jar : jars) {
|
||||||
|
List<String> classNames = readClassNames(jar);
|
||||||
|
for (String className : classNames) {
|
||||||
|
checkForPlugin(className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void checkForPlugin(String className) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
Class<?> c = classLoader.loadClass(className);
|
||||||
|
for (Class<?> intf : c.getInterfaces()) {
|
||||||
|
System.out.println("Testing class " + c + " interface " + intf);
|
||||||
|
|
||||||
|
if (isPluginInterface(intf)) {
|
||||||
|
System.out.println("BINDING");
|
||||||
|
// Ugly hack to enable dynamic binding... Can this be done type-safely?
|
||||||
|
Multibinder<Object> binder = (Multibinder<Object>) findBinder(intf);
|
||||||
|
binder.addBinding().to(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
System.err.println("Could not load class " + className + ": " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isPluginInterface(Class<?> intf) {
|
||||||
|
return intf.isAnnotationPresent(Plugin.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Multibinder<?> findBinder(Class<?> intf) {
|
||||||
|
Multibinder<?> binder = binders.get(intf);
|
||||||
|
if (binder == null) {
|
||||||
|
binder = Multibinder.newSetBinder(binder(), intf);
|
||||||
|
binders.put(intf, binder);
|
||||||
|
}
|
||||||
|
return binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<String> readClassNames(File jar) {
|
||||||
|
List<String> classNames = new ArrayList<String>();
|
||||||
|
|
||||||
|
JarFile file = null;
|
||||||
|
try {
|
||||||
|
file = new JarFile(jar);
|
||||||
|
Enumeration<JarEntry> entries = file.entries();
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
JarEntry entry = entries.nextElement();
|
||||||
|
String name = entry.getName();
|
||||||
|
if (name.toLowerCase().endsWith(".class") && !name.contains("$")) {
|
||||||
|
name = name.substring(0, name.length() - 6);
|
||||||
|
name = name.replace('/', '.');
|
||||||
|
classNames.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("Error reading JAR file " + jar);
|
||||||
|
} finally {
|
||||||
|
if (file != null) {
|
||||||
|
try {
|
||||||
|
file.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Curse all checked exceptions...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return classNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,37 +0,0 @@
|
|||||||
package net.sf.openrocket.plugin;
|
|
||||||
|
|
||||||
import java.awt.Component;
|
|
||||||
|
|
||||||
import net.xeoh.plugins.base.Plugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface that defined a Swing configurator for a plugin.
|
|
||||||
* The implemeting class should be a plugin that provides the
|
|
||||||
* capability "<pluginID>:config" where <pluginID> is the
|
|
||||||
* plugin ID of the plugin to configure.
|
|
||||||
* <p>
|
|
||||||
*
|
|
||||||
* @param <P> The plugin class that is being configured
|
|
||||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
|
||||||
*/
|
|
||||||
public interface SwingConfigurator<P> extends Plugin {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether this plugin is configurable or not.
|
|
||||||
*
|
|
||||||
* @param plugin the plugin to test.
|
|
||||||
* @return whether the plugin has a configuration component.
|
|
||||||
*/
|
|
||||||
public boolean isConfigurable(P plugin);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the configuration component for configuring the
|
|
||||||
* provided plugin.
|
|
||||||
*
|
|
||||||
* @param plugin the plugin to configure.
|
|
||||||
* @return a Swing component for configuring the plugin.
|
|
||||||
*/
|
|
||||||
public Component getConfigurationComponent(P plugin);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
35
core/src/net/sf/openrocket/plugin/Test.java
Normal file
35
core/src/net/sf/openrocket/plugin/Test.java
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package net.sf.openrocket.plugin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.google.inject.Guice;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
|
||||||
|
public class Test {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Set<ExamplePlugin> impls;
|
||||||
|
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
System.out.println("Plugin count: " + impls.size());
|
||||||
|
for (ExamplePlugin i : impls) {
|
||||||
|
i.doit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) throws MalformedURLException {
|
||||||
|
|
||||||
|
File[] jars = { new File("/home/sampo/Projects/OpenRocket/core/example.jar") };
|
||||||
|
URL[] urls = { new File("/home/sampo/Projects/OpenRocket/core/example.jar").toURL() };
|
||||||
|
URLClassLoader classLoader = new URLClassLoader(urls);
|
||||||
|
Injector injector = Guice.createInjector(new PluginModule(jars, classLoader));
|
||||||
|
injector.getInstance(Test.class).run();
|
||||||
|
}
|
||||||
|
}
|
@ -1,42 +0,0 @@
|
|||||||
package net.sf.openrocket.plugin.example;
|
|
||||||
|
|
||||||
import java.awt.Component;
|
|
||||||
|
|
||||||
public class AirStartSimulationExtension extends OpenRocketSimulationListener {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "Air-start";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getMenuPosition() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loadFromXML(Object... objects) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveToXML(Object... objects) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isConfigurable() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getConfigurationComponent() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
package net.sf.openrocket.plugin.example;
|
|
||||||
|
|
||||||
import net.sf.openrocket.plugin.framework.JSPFPluginFactory;
|
|
||||||
import net.sf.openrocket.plugin.framework.PluginFactory;
|
|
||||||
|
|
||||||
public class ExampleMain {
|
|
||||||
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
PluginFactory factory = new JSPFPluginFactory();
|
|
||||||
|
|
||||||
System.out.println("Plugins:");
|
|
||||||
System.out.println("---------");
|
|
||||||
for (ExamplePluginInterface plugin : factory.getPlugins(ExamplePluginInterface.class)) {
|
|
||||||
plugin.print();
|
|
||||||
}
|
|
||||||
System.out.println("---------");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package net.sf.openrocket.plugin.example;
|
|
||||||
|
|
||||||
|
|
||||||
public class ExamplePlugin implements ExamplePluginInterface {
|
|
||||||
|
|
||||||
private final String str;
|
|
||||||
|
|
||||||
public ExamplePlugin(String str) {
|
|
||||||
this.str = str;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print() {
|
|
||||||
System.out.println("ExamplePlugin: " + str);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package net.sf.openrocket.plugin.example;
|
|
||||||
|
|
||||||
import net.xeoh.plugins.base.Plugin;
|
|
||||||
|
|
||||||
public interface ExamplePluginInterface extends Plugin {
|
|
||||||
|
|
||||||
public void print();
|
|
||||||
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
package net.sf.openrocket.plugin.example;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.sf.openrocket.plugin.framework.AbstractService;
|
|
||||||
import net.xeoh.plugins.base.annotations.PluginImplementation;
|
|
||||||
|
|
||||||
@PluginImplementation
|
|
||||||
public class ExampleService extends AbstractService<ExamplePluginInterface> {
|
|
||||||
|
|
||||||
protected ExampleService() {
|
|
||||||
super(ExamplePluginInterface.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<ExamplePluginInterface> getPlugins(Object... args) {
|
|
||||||
List<ExamplePluginInterface> plugins = new ArrayList<ExamplePluginInterface>();
|
|
||||||
plugins.add(new ExamplePlugin("a"));
|
|
||||||
plugins.add(new ExamplePlugin("b"));
|
|
||||||
return plugins;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package net.sf.openrocket.plugin.example;
|
|
||||||
|
|
||||||
import net.xeoh.plugins.base.annotations.PluginImplementation;
|
|
||||||
|
|
||||||
@PluginImplementation
|
|
||||||
public class ExampleSingletonPlugin implements ExamplePluginInterface {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print() {
|
|
||||||
System.out.println("ExampleSingletonPlugin");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
package net.sf.openrocket.plugin.example;
|
|
||||||
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.sf.openrocket.plugin.Configurable;
|
|
||||||
import net.sf.openrocket.plugin.SwingConfigurator;
|
|
||||||
import net.sf.openrocket.plugin.framework.Service;
|
|
||||||
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
|
|
||||||
import net.sf.openrocket.util.BugException;
|
|
||||||
import net.xeoh.plugins.base.Plugin;
|
|
||||||
import net.xeoh.plugins.base.annotations.Capabilities;
|
|
||||||
|
|
||||||
public abstract class OpenRocketSimulationListener extends AbstractSimulationListener
|
|
||||||
implements Plugin, Service, SwingConfigurator<OpenRocketSimulationListener>, Configurable {
|
|
||||||
|
|
||||||
private final String[] ids;
|
|
||||||
private final String[] capabilities;
|
|
||||||
|
|
||||||
public OpenRocketSimulationListener(String... ids) {
|
|
||||||
if (ids.length == 0) {
|
|
||||||
ids = new String[] { this.getClass().getCanonicalName() };
|
|
||||||
}
|
|
||||||
|
|
||||||
this.ids = ids.clone();
|
|
||||||
this.capabilities = new String[ids.length * 2];
|
|
||||||
for (int i = 0; i < ids.length; i++) {
|
|
||||||
capabilities[i * 2] = ids[i] + ":service";
|
|
||||||
capabilities[i * 2 + 1] = ids[i] + ":config";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Capabilities
|
|
||||||
public String[] capabilities() {
|
|
||||||
return capabilities.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public <E> List<E> getPlugins(Class<E> e, Object... args) {
|
|
||||||
if (e != this.getClass()) {
|
|
||||||
throw new BugException("Attempting to get plugin of type " + e + " but I am of type " + this.getClass());
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return (List<E>) Arrays.asList(this.getClass().newInstance());
|
|
||||||
} catch (IllegalAccessException e1) {
|
|
||||||
throw new BugException("Could not instantiate new object of type " + this.getClass(), e1);
|
|
||||||
} catch (InstantiationException e2) {
|
|
||||||
throw new BugException("Could not instantiate new object of type " + this.getClass(), e2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isConfigurable(OpenRocketSimulationListener plugin) {
|
|
||||||
return plugin.isConfigurable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getConfigurationComponent(OpenRocketSimulationListener plugin) {
|
|
||||||
return plugin.getConfigurationComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract boolean isConfigurable();
|
|
||||||
|
|
||||||
public abstract Component getConfigurationComponent();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getPluginID() {
|
|
||||||
return ids[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCompatible(String pluginID) {
|
|
||||||
for (String id : ids) {
|
|
||||||
if (pluginID.equals(id)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,135 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Plugin:
|
|
||||||
|
|
||||||
foo()
|
|
||||||
bar()
|
|
||||||
getValue()
|
|
||||||
setValue()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Service:
|
|
||||||
|
|
||||||
getPlugins(args...)
|
|
||||||
capabilities() -> "pluginid:service"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SwingConfigurator:
|
|
||||||
|
|
||||||
getConfigurationComponent(plugin)
|
|
||||||
capabilities() -> "pluginid:config"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
OpenRocketSimulationListener extends SimulationListener implements Service, SwingConfigurator:
|
|
||||||
|
|
||||||
|
|
||||||
constructor:
|
|
||||||
pluginid = class name
|
|
||||||
|
|
||||||
|
|
||||||
getPlugins():
|
|
||||||
return new this
|
|
||||||
|
|
||||||
capabilities: pluginid:service, pluginid:config
|
|
||||||
|
|
||||||
getConfigurationComponent(plugin)
|
|
||||||
plugin.getConfigurationComponent()
|
|
||||||
|
|
||||||
abstract getConfigurationComponent()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Types of plugins:
|
|
||||||
|
|
||||||
|
|
||||||
AtmosphericModel
|
|
||||||
- Name -> dropdown
|
|
||||||
- Config component -> dialog window (or button)
|
|
||||||
- stateful, non-dynamic
|
|
||||||
- stored
|
|
||||||
|
|
||||||
|
|
||||||
SimulationListener
|
|
||||||
- Name + menu position -> Add extension menu
|
|
||||||
- Config component -> dialog after edit button
|
|
||||||
- stateful, (dynamic?)
|
|
||||||
- stored
|
|
||||||
|
|
||||||
|
|
||||||
OptimizationModifier
|
|
||||||
- contains its own name, description, related object
|
|
||||||
- config N/A
|
|
||||||
- stateful, dynamic
|
|
||||||
- not stored
|
|
||||||
|
|
||||||
|
|
||||||
OptimizationParameter
|
|
||||||
- name
|
|
||||||
- config N/A
|
|
||||||
- stateful, (dynamic?)
|
|
||||||
- not stored
|
|
||||||
|
|
||||||
|
|
||||||
PluginDialogWindow
|
|
||||||
- name + menu position -> Menu
|
|
||||||
- stateful, non-dynamic
|
|
||||||
- not stored
|
|
||||||
|
|
||||||
|
|
||||||
Motor
|
|
||||||
- Name -> Config tab
|
|
||||||
- Config component -> tab contents ????
|
|
||||||
- or a separate configuration interface?
|
|
||||||
- stored
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Name is common - out, instead have name separately in plugin interfaces?
|
|
||||||
Menu position used twice - out
|
|
||||||
|
|
||||||
Leave common configuration out
|
|
||||||
-> :config supported by those that make sense
|
|
||||||
-> may have separate interface from SwingConfigurator (e.g. SwingMotorConfigurator)
|
|
||||||
|
|
||||||
Motor
|
|
||||||
-> :loader separately? for injecting placeholders
|
|
||||||
or store data and call loaders later
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<extension pluginid="com.example.MyFancyExtension">
|
|
||||||
<param type="double" key="altitude">100.0</param>
|
|
||||||
<param type="material" key="mat">
|
|
||||||
<name>Gold</name>
|
|
||||||
<type>bulk</type>
|
|
||||||
<density>16000</density>
|
|
||||||
</param>
|
|
||||||
</extension>
|
|
||||||
|
|
||||||
|
|
||||||
<extension pluginid="com.example.MyFancyExtension">
|
|
||||||
<param type="double" key="altitude">100.0</param>
|
|
||||||
<param type="material" key="mat">
|
|
||||||
<name>Gold</name>
|
|
||||||
<type>bulk</type>
|
|
||||||
<density>16000</density>
|
|
||||||
</param>
|
|
||||||
</extension>
|
|
||||||
|
|
||||||
|
|
||||||
<extension pluginid="com.example.MyFancyExtension">
|
|
||||||
</extension>
|
|
||||||
|
|
||||||
|
|
||||||
<extension pluginid="com.example.MyFancyExtension">
|
|
||||||
</extension>
|
|
||||||
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
|||||||
package net.sf.openrocket.plugin.framework;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.sf.openrocket.util.BugException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An abstract service implementation that returns plugins of type P.
|
|
||||||
*
|
|
||||||
* @param <P> the plugin type that this service returns.
|
|
||||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
|
||||||
*/
|
|
||||||
public abstract class AbstractService<P> implements Service {
|
|
||||||
|
|
||||||
private final Class<P> type;
|
|
||||||
|
|
||||||
protected AbstractService(Class<P> type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public <E> List<E> getPlugins(Class<E> e, Object... args) {
|
|
||||||
|
|
||||||
if (e != type) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<P> plugins = getPlugins(args);
|
|
||||||
|
|
||||||
// Check list content types to avoid mysterious bugs later on
|
|
||||||
for (P p : plugins) {
|
|
||||||
if (!type.isInstance(p)) {
|
|
||||||
throw new BugException("Requesting plugins of type " + type + " but received " +
|
|
||||||
((p != null) ? p.getClass() : "null"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (List<E>) plugins;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract List<P> getPlugins(Object... args);
|
|
||||||
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
package net.sf.openrocket.plugin.framework;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.sf.openrocket.util.BugException;
|
|
||||||
import net.xeoh.plugins.base.Plugin;
|
|
||||||
import net.xeoh.plugins.base.PluginManager;
|
|
||||||
import net.xeoh.plugins.base.impl.PluginManagerFactory;
|
|
||||||
import net.xeoh.plugins.base.util.JSPFProperties;
|
|
||||||
import net.xeoh.plugins.base.util.PluginManagerUtil;
|
|
||||||
|
|
||||||
public class JSPFPluginFactory implements PluginFactory {
|
|
||||||
|
|
||||||
private final PluginManager pluginManager;
|
|
||||||
|
|
||||||
public JSPFPluginFactory() {
|
|
||||||
|
|
||||||
final JSPFProperties props = new JSPFProperties();
|
|
||||||
|
|
||||||
// props.setProperty(PluginManager.class, "cache.enabled", "true");
|
|
||||||
// props.setProperty(PluginManager.class, "cache.mode", "weak"); //optional
|
|
||||||
// props.setProperty(PluginManager.class, "cache.file", "jspf.cache");
|
|
||||||
|
|
||||||
try {
|
|
||||||
pluginManager = PluginManagerFactory.createPluginManager(props);
|
|
||||||
pluginManager.addPluginsFrom(new URI("classpath://*"));
|
|
||||||
} catch (URISyntaxException e) {
|
|
||||||
throw new BugException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <E extends Plugin> List<E> getPlugins(Class<E> e, Object... args) {
|
|
||||||
|
|
||||||
List<E> plugins = new ArrayList<E>();
|
|
||||||
|
|
||||||
PluginManagerUtil pluginManagerUtil = new PluginManagerUtil(pluginManager);
|
|
||||||
plugins.addAll(pluginManagerUtil.getPlugins(e));
|
|
||||||
|
|
||||||
for (Service s : pluginManagerUtil.getPlugins(Service.class)) {
|
|
||||||
plugins.addAll(s.getPlugins(e, args));
|
|
||||||
}
|
|
||||||
|
|
||||||
return plugins;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package net.sf.openrocket.plugin.framework;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.xeoh.plugins.base.Plugin;
|
|
||||||
|
|
||||||
public interface PluginFactory {
|
|
||||||
|
|
||||||
public <E extends Plugin> List<E> getPlugins(Class<E> e, Object... args);
|
|
||||||
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
package net.sf.openrocket.plugin.framework;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.xeoh.plugins.base.Plugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A discovery service that returns plugins of a specified type with
|
|
||||||
* provided arguments.
|
|
||||||
*
|
|
||||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
|
||||||
*/
|
|
||||||
public interface Service extends Plugin {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the plugins that match the provided type and are applicable
|
|
||||||
* for the arguments. The arguments depend on the class type.
|
|
||||||
* <p>
|
|
||||||
* This method may return different plugins for different arguments.
|
|
||||||
* For example, if the arguments contain the OpenRocketDocument, the
|
|
||||||
* service may return only plugins applicable for the specified document.
|
|
||||||
*
|
|
||||||
* @param type the plugin interface type
|
|
||||||
* @param args arguments for the interface.
|
|
||||||
* @return the plugin instances applicable.
|
|
||||||
*/
|
|
||||||
public <E> List<E> getPlugins(Class<E> type, Object... args);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user