--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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);
+
+}
+++ /dev/null
-package net.sf.openrocket.plugin;
-
-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);
-
-}
--- /dev/null
+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);
+
+}
+++ /dev/null
-package net.sf.openrocket.plugin;
-
-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;
-
- }
-}
+++ /dev/null
-package net.sf.openrocket.plugin;
-
-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);
-
-}
+++ /dev/null
-package net.sf.openrocket.plugin;
-
-import java.util.List;
-
-import net.xeoh.plugins.base.Plugin;
-
-public interface Service extends Plugin {
-
-
- public <E> List<E> getPlugins(Class<E> e, Object... args);
-
-
-}
--- /dev/null
+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);
+
+
+}
--- /dev/null
+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;
+ }
+
+
+}
package net.sf.openrocket.plugin.example;
-import net.sf.openrocket.plugin.JSPFPluginFactory;
-import net.sf.openrocket.plugin.PluginFactory;
+import net.sf.openrocket.plugin.framework.JSPFPluginFactory;
+import net.sf.openrocket.plugin.framework.PluginFactory;
public class ExampleMain {
import java.util.ArrayList;
import java.util.List;
-import net.sf.openrocket.plugin.AbstractService;
+import net.sf.openrocket.plugin.framework.AbstractService;
import net.xeoh.plugins.base.annotations.PluginImplementation;
@PluginImplementation
--- /dev/null
+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;
+ }
+
+
+}
--- /dev/null
+
+
+
+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>
+
+
--- /dev/null
+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);
+
+}
--- /dev/null
+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;
+
+ }
+}
--- /dev/null
+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);
+
+}
--- /dev/null
+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);
+
+
+}