Implement simulation extensions
Squashed commit of the following: commit 058f603c23accbcdbe9110645f19164da7d57c85 Author: Sampo Niskanen <sampo.niskanen@iki.fi> Date: Sat Apr 12 10:52:21 2014 +0300 Implmenent simulation extensions commit a1ca913c7d7793a9209a5f98235336270db6ce10 Author: Sampo Niskanen <sampo.niskanen@iki.fi> Date: Fri Apr 11 19:45:12 2014 +0300 WIP commit 916f0bc8961360c0906b413485ca4e3700033740 Author: Sampo Niskanen <sampo.niskanen@iki.fi> Date: Tue Apr 1 20:23:25 2014 +0300 WIP
This commit is contained in:
parent
11328bc1fc
commit
b6e3a57b47
@ -377,11 +377,9 @@ simedtdlg.lbl.Timestep = Time step:
|
||||
simedtdlg.lbl.ttip.Timestep1 = <html>The time between simulation steps.<br>A smaller time step results in a more accurate but slower simulation.<br>
|
||||
simedtdlg.lbl.ttip.Timestep2 = The 4<sup>th</sup> order simulation method is quite accurate with a time step of
|
||||
simedtdlg.but.ttip.resettodefault = Reset the time step to its default value (
|
||||
simedtdlg.border.Simlist = Simulator listeners
|
||||
simedtdlg.txt.longA1 = <html><i>Simulation listeners</i> is an advanced feature that allows user-written code to listen to and interact with the simulation.
|
||||
simedtdlg.txt.longA2 = For details on writing simulation listeners, see the OpenRocket technical documentation.
|
||||
simedtdlg.lbl.Curlist = Current listeners:
|
||||
simedtdlg.lbl.Addsimlist = Add simulation listener
|
||||
simedtdlg.border.SimExt = Simulation extensions
|
||||
simedtdlg.SimExt.desc = <html><i>Simulation extensions</i> enable advanced features and custom functionality during flight simulations. You can for example do hardware-in-the-loop testing with them.
|
||||
simedtdlg.SimExt.noExtensions = No simulation extensions defined
|
||||
simedtdlg.lbl.Noflightdata = No flight data available.
|
||||
simedtdlg.lbl.runsimfirst = Please run the simulation first.
|
||||
simedtdlg.chart.Simflight = Simulated flight
|
||||
@ -394,6 +392,12 @@ simedtdlg.IntensityDesc.High = High
|
||||
simedtdlg.IntensityDesc.Veryhigh = Very high
|
||||
simedtdlg.IntensityDesc.Extreme = Extreme
|
||||
|
||||
SimulationExtension.airstart.name = Air-start ({alt})
|
||||
SimulationExtension.javacode.name = Java code
|
||||
SimulationExtension.javacode.name.none = none
|
||||
SimulationExtension.javacode.desc = Add a custom SimulationListener to the simulation
|
||||
SimulationExtension.javacode.className = Fully-qualified Java class name:
|
||||
|
||||
SimulationEditDialog.btn.plot = Plot
|
||||
SimulationEditDialog.btn.export = Export
|
||||
SimulationEditDialog.btn.edit = Edit
|
||||
|
BIN
core/resources/pix/icons/configure.png
Normal file
BIN
core/resources/pix/icons/configure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 980 B |
@ -23,7 +23,7 @@ import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.customexpression.CustomExpression;
|
||||
import net.sf.openrocket.simulation.listeners.SimulationListener;
|
||||
import net.sf.openrocket.simulation.extension.SimulationExtension;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.ArrayList;
|
||||
|
||||
@ -146,17 +146,8 @@ public class OpenRocketDocument implements ComponentChangeListener {
|
||||
|
||||
// simulation listeners
|
||||
for (Simulation sim : simulations) {
|
||||
for (String className : sim.getSimulationListeners()) {
|
||||
SimulationListener l = null;
|
||||
try {
|
||||
Class<?> c = Class.forName(className);
|
||||
l = (SimulationListener) c.newInstance();
|
||||
|
||||
Collections.addAll(allTypes, l.getFlightDataTypes());
|
||||
//System.out.println("This document has expression datatype from "+l.getName());
|
||||
} catch (Exception e) {
|
||||
log.error("Could not instantiate listener: " + className);
|
||||
}
|
||||
for (SimulationExtension c : sim.getSimulationExtensions()) {
|
||||
allTypes.addAll(c.getFlightDataTypes());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ import net.sf.openrocket.simulation.SimulationEngine;
|
||||
import net.sf.openrocket.simulation.SimulationOptions;
|
||||
import net.sf.openrocket.simulation.SimulationStepper;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationListenerException;
|
||||
import net.sf.openrocket.simulation.extension.SimulationExtension;
|
||||
import net.sf.openrocket.simulation.listeners.SimulationListener;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.ArrayList;
|
||||
@ -76,7 +76,8 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
// TODO: HIGH: Change to use actual conditions class??
|
||||
private SimulationOptions options;
|
||||
|
||||
private ArrayList<String> simulationListeners = new ArrayList<String>();
|
||||
private ArrayList<SimulationExtension> simulationExtensions = new ArrayList<SimulationExtension>();
|
||||
|
||||
|
||||
private final Class<? extends SimulationEngine> simulationEngineClass = BasicEventSimulationEngine.class;
|
||||
private Class<? extends SimulationStepper> simulationStepperClass = RK4SimulationStepper.class;
|
||||
@ -116,7 +117,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
|
||||
|
||||
public Simulation(Rocket rocket, Status status, String name, SimulationOptions options,
|
||||
List<String> listeners, FlightData data) {
|
||||
List<SimulationExtension> extensions, FlightData data) {
|
||||
|
||||
if (rocket == null)
|
||||
throw new IllegalArgumentException("rocket cannot be null");
|
||||
@ -142,8 +143,8 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
this.options = options;
|
||||
options.addChangeListener(new ConditionListener());
|
||||
|
||||
if (listeners != null) {
|
||||
this.simulationListeners.addAll(listeners);
|
||||
if (extensions != null) {
|
||||
this.simulationExtensions.addAll(extensions);
|
||||
}
|
||||
|
||||
|
||||
@ -196,14 +197,14 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of simulation listeners. The returned list is the one used by
|
||||
* Get the list of simulation extensions. The returned list is the one used by
|
||||
* this object; changes to it will reflect changes in the simulation.
|
||||
*
|
||||
* @return the actual list of simulation listeners.
|
||||
* @return the actual list of simulation extensions.
|
||||
*/
|
||||
public List<String> getSimulationListeners() {
|
||||
public List<SimulationExtension> getSimulationExtensions() {
|
||||
mutex.verify();
|
||||
return simulationListeners;
|
||||
return simulationExtensions;
|
||||
}
|
||||
|
||||
|
||||
@ -293,16 +294,8 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
simulationConditions.getSimulationListenerList().add(l);
|
||||
}
|
||||
|
||||
for (String className : simulationListeners) {
|
||||
SimulationListener l = null;
|
||||
try {
|
||||
Class<?> c = Class.forName(className);
|
||||
l = (SimulationListener) c.newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new SimulationListenerException("Could not instantiate listener of " +
|
||||
"class: " + className, e);
|
||||
}
|
||||
simulationConditions.getSimulationListenerList().add(l);
|
||||
for (SimulationExtension extension : simulationExtensions) {
|
||||
extension.initialize(simulationConditions);
|
||||
}
|
||||
|
||||
long t1, t2;
|
||||
@ -410,7 +403,9 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
copy.mutex = SafetyMutex.newInstance();
|
||||
copy.status = Status.NOT_SIMULATED;
|
||||
copy.options = this.options.clone();
|
||||
copy.simulationListeners = this.simulationListeners.clone();
|
||||
for (SimulationExtension c : this.simulationExtensions) {
|
||||
copy.simulationExtensions.add(c.clone());
|
||||
}
|
||||
copy.listeners = new ArrayList<EventListener>();
|
||||
copy.simulatedConditions = null;
|
||||
copy.simulatedConfiguration = null;
|
||||
@ -442,7 +437,9 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
copy.name = this.name;
|
||||
copy.options.copyFrom(this.options);
|
||||
copy.simulatedConfiguration = this.simulatedConfiguration;
|
||||
copy.simulationListeners = this.simulationListeners.clone();
|
||||
for (SimulationExtension c : this.simulationExtensions) {
|
||||
copy.simulationExtensions.add(c.clone());
|
||||
}
|
||||
copy.simulationStepperClass = this.simulationStepperClass;
|
||||
copy.aerodynamicCalculatorClass = this.aerodynamicCalculatorClass;
|
||||
|
||||
|
@ -30,8 +30,10 @@ import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.simulation.SimulationOptions;
|
||||
import net.sf.openrocket.simulation.customexpression.CustomExpression;
|
||||
import net.sf.openrocket.simulation.extension.SimulationExtension;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.BuildProperties;
|
||||
import net.sf.openrocket.util.Config;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import net.sf.openrocket.util.Reflection;
|
||||
import net.sf.openrocket.util.TextUtil;
|
||||
@ -455,9 +457,18 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
indent--;
|
||||
writeln("</conditions>");
|
||||
|
||||
|
||||
for (String s : simulation.getSimulationListeners()) {
|
||||
writeElement("listener", TextUtil.escapeXML(s));
|
||||
for (SimulationExtension extension : simulation.getSimulationExtensions()) {
|
||||
Config config = extension.getConfig();
|
||||
writeln("<extension extensionid=\"" + TextUtil.escapeXML(extension.getId()) + "\">");
|
||||
indent++;
|
||||
if (config != null) {
|
||||
for (String key : config.keySet()) {
|
||||
Object value = config.get(key, null);
|
||||
writeEntry(key, value);
|
||||
}
|
||||
}
|
||||
indent--;
|
||||
writeln("</extension>");
|
||||
}
|
||||
|
||||
// Write basic simulation data
|
||||
@ -512,6 +523,39 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
}
|
||||
|
||||
|
||||
private void writeEntry(String key, Object value) throws IOException {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
String keyAttr;
|
||||
|
||||
if (key != null) {
|
||||
keyAttr = "key=\"" + key + "\" ";
|
||||
} else {
|
||||
keyAttr = "";
|
||||
}
|
||||
|
||||
if (value instanceof Boolean) {
|
||||
writeln("<entry " + keyAttr + "type=\"boolean\">" + value + "</entry>");
|
||||
} else if (value instanceof Number) {
|
||||
writeln("<entry " + keyAttr + "type=\"number\">" + value + "</entry>");
|
||||
} else if (value instanceof String) {
|
||||
writeln("<entry " + keyAttr + "type=\"string\">" + value + "</entry>");
|
||||
} else if (value instanceof List) {
|
||||
List<?> list = (List<?>) value;
|
||||
writeln("<entry " + keyAttr + "type=\"list\">");
|
||||
indent++;
|
||||
for (Object o : list) {
|
||||
writeEntry(null, o);
|
||||
}
|
||||
indent--;
|
||||
writeln("</entry>");
|
||||
} else {
|
||||
// Unknown type
|
||||
log.error("Unknown configuration value type " + value.getClass() + " value=" + value);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveFlightDataBranch(FlightDataBranch branch, double timeSkip)
|
||||
throws IOException {
|
||||
double previousTime = -100000;
|
||||
|
@ -0,0 +1,104 @@
|
||||
package net.sf.openrocket.file.openrocket.importt;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.WarningSet;
|
||||
import net.sf.openrocket.file.simplesax.AbstractElementHandler;
|
||||
import net.sf.openrocket.file.simplesax.ElementHandler;
|
||||
import net.sf.openrocket.file.simplesax.PlainTextHandler;
|
||||
import net.sf.openrocket.util.ArrayList;
|
||||
import net.sf.openrocket.util.Config;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
public class ConfigHandler extends AbstractElementHandler {
|
||||
|
||||
private ConfigHandler listHandler;
|
||||
private Config config = new Config();
|
||||
private List<Object> list = new ArrayList<Object>();
|
||||
|
||||
@Override
|
||||
public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) throws SAXException {
|
||||
if (element.equals("entry") && "list".equals(attributes.get("type"))) {
|
||||
listHandler = new ConfigHandler();
|
||||
return listHandler;
|
||||
} else {
|
||||
return PlainTextHandler.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings) throws SAXException {
|
||||
if (element.equals("entry")) {
|
||||
String key = attributes.get("key");
|
||||
String type = attributes.get("type");
|
||||
Object value = null;
|
||||
if ("boolean".equals(type)) {
|
||||
value = Boolean.valueOf(content);
|
||||
} else if ("string".equals(type)) {
|
||||
value = content;
|
||||
} else if ("number".equals(type)) {
|
||||
value = parseNumber(content);
|
||||
} else if ("list".equals(type)) {
|
||||
value = listHandler.list;
|
||||
}
|
||||
if (value != null) {
|
||||
if (key != null) {
|
||||
config.put(key, value);
|
||||
} else {
|
||||
list.add(value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
super.closeElement(element, attributes, content, warnings);
|
||||
}
|
||||
}
|
||||
|
||||
private Number parseNumber(String str) {
|
||||
try {
|
||||
str = str.trim();
|
||||
if (str.matches("^[+-]?[0-9]+$")) {
|
||||
BigInteger value = new BigInteger(str, 10);
|
||||
if (value.equals(BigInteger.valueOf(value.intValue()))) {
|
||||
return value.intValue();
|
||||
} else if (value.equals(BigInteger.valueOf(value.longValue()))) {
|
||||
return value.longValue();
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
} else {
|
||||
BigDecimal value = new BigDecimal(str);
|
||||
if (value.equals(BigDecimal.valueOf(value.doubleValue()))) {
|
||||
return value.doubleValue();
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Config getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
Random rnd = new Random();
|
||||
for (int i = 0; i < 1000000000; i++) {
|
||||
double d = Double.longBitsToDouble(rnd.nextLong());
|
||||
if (Double.isNaN(d))
|
||||
continue;
|
||||
String s = "" + d;
|
||||
BigDecimal dec = new BigDecimal(s);
|
||||
if (dec.doubleValue() != d) {
|
||||
System.out.println("Fail: d=" + d + " dec=" + dec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package net.sf.openrocket.file.openrocket.importt;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.WarningSet;
|
||||
import net.sf.openrocket.document.OpenRocketDocument;
|
||||
@ -14,6 +15,13 @@ import net.sf.openrocket.file.simplesax.ElementHandler;
|
||||
import net.sf.openrocket.file.simplesax.PlainTextHandler;
|
||||
import net.sf.openrocket.simulation.FlightData;
|
||||
import net.sf.openrocket.simulation.SimulationOptions;
|
||||
import net.sf.openrocket.simulation.extension.SimulationExtension;
|
||||
import net.sf.openrocket.simulation.extension.SimulationExtensionProvider;
|
||||
import net.sf.openrocket.simulation.extension.impl.JavaCode;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.StringUtil;
|
||||
|
||||
import com.google.inject.Key;
|
||||
|
||||
class SingleSimulationHandler extends AbstractElementHandler {
|
||||
|
||||
@ -24,9 +32,10 @@ class SingleSimulationHandler extends AbstractElementHandler {
|
||||
private String name;
|
||||
|
||||
private SimulationConditionsHandler conditionHandler;
|
||||
private ConfigHandler configHandler;
|
||||
private FlightDataHandler dataHandler;
|
||||
|
||||
private final List<String> listeners = new ArrayList<String>();
|
||||
private final List<SimulationExtension> extensions = new ArrayList<SimulationExtension>();
|
||||
|
||||
public SingleSimulationHandler(OpenRocketDocument doc, DocumentLoadingContext context) {
|
||||
this.doc = doc;
|
||||
@ -47,6 +56,9 @@ class SingleSimulationHandler extends AbstractElementHandler {
|
||||
} else if (element.equals("conditions")) {
|
||||
conditionHandler = new SimulationConditionsHandler(doc.getRocket(), context);
|
||||
return conditionHandler;
|
||||
} else if (element.equals("extension")) {
|
||||
configHandler = new ConfigHandler();
|
||||
return configHandler;
|
||||
} else if (element.equals("flightdata")) {
|
||||
dataHandler = new FlightDataHandler(this, context);
|
||||
return dataHandler;
|
||||
@ -71,7 +83,23 @@ class SingleSimulationHandler extends AbstractElementHandler {
|
||||
warnings.add("Unknown calculator '" + content.trim() + "' specified, ignoring.");
|
||||
}
|
||||
} else if (element.equals("listener") && content.trim().length() > 0) {
|
||||
listeners.add(content.trim());
|
||||
extensions.add(compatibilityExtension(content.trim()));
|
||||
} else if (element.equals("extension") && !StringUtil.isEmpty(attributes.get("extensionid"))) {
|
||||
String id = attributes.get("extensionid");
|
||||
SimulationExtension extension = null;
|
||||
Set<SimulationExtensionProvider> extensionProviders = Application.getInjector().getInstance(new Key<Set<SimulationExtensionProvider>>() {
|
||||
});
|
||||
for (SimulationExtensionProvider p : extensionProviders) {
|
||||
if (p.getIds().contains(id)) {
|
||||
extension = p.getInstance(id);
|
||||
}
|
||||
}
|
||||
if (extension != null) {
|
||||
extension.setConfig(configHandler.getConfig());
|
||||
extensions.add(extension);
|
||||
} else {
|
||||
warnings.add("Simulation extension with id '" + id + "' not found.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -105,8 +133,16 @@ class SingleSimulationHandler extends AbstractElementHandler {
|
||||
data = dataHandler.getFlightData();
|
||||
|
||||
Simulation simulation = new Simulation(doc.getRocket(), status, name,
|
||||
conditions, listeners, data);
|
||||
conditions, extensions, data);
|
||||
|
||||
doc.addSimulation(simulation);
|
||||
}
|
||||
|
||||
|
||||
private SimulationExtension compatibilityExtension(String className) {
|
||||
JavaCode extension = Application.getInjector().getInstance(JavaCode.class);
|
||||
extension.setClassName(className);
|
||||
return extension;
|
||||
}
|
||||
|
||||
}
|
@ -42,9 +42,8 @@ public class PluginModule extends AbstractModule {
|
||||
if (c.isInterface())
|
||||
continue;
|
||||
|
||||
for (Class<?> intf : c.getInterfaces()) {
|
||||
|
||||
if (interfaces.contains(intf)) {
|
||||
for (Class<?> intf : interfaces) {
|
||||
if (intf.isAssignableFrom(c)) {
|
||||
// Ugly hack to enable dynamic binding... Can this be done type-safely?
|
||||
Multibinder<Object> binder = (Multibinder<Object>) findBinder(intf);
|
||||
binder.addBinding().to(c);
|
||||
|
@ -0,0 +1,100 @@
|
||||
package net.sf.openrocket.simulation.extension;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.util.AbstractChangeSource;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.Config;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
/**
|
||||
* An abstract implementation of a SimulationExtension.
|
||||
*/
|
||||
public abstract class AbstractSimulationExtension extends AbstractChangeSource implements SimulationExtension, Cloneable {
|
||||
|
||||
@Inject
|
||||
protected Translator trans;
|
||||
|
||||
protected Config config = new Config();
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Use the current class name as the extension name. You should override
|
||||
* getName if you use this constructor.
|
||||
*/
|
||||
protected AbstractSimulationExtension() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the provided name as a static name for this extension.
|
||||
*/
|
||||
protected AbstractSimulationExtension(String name) {
|
||||
if (name != null) {
|
||||
this.name = name;
|
||||
} else {
|
||||
this.name = this.getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, returns the canonical name of this class.
|
||||
*/
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.getClass().getCanonicalName();
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, returns the name provided to the constructor.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, returns null.
|
||||
*/
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, returns an empty list.
|
||||
*/
|
||||
@Override
|
||||
public List<FlightDataType> getFlightDataTypes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, returns a new object obtained by calling Object.clone().
|
||||
*/
|
||||
@Override
|
||||
public SimulationExtension clone() {
|
||||
try {
|
||||
AbstractSimulationExtension copy = (AbstractSimulationExtension) super.clone();
|
||||
copy.config = this.config.clone();
|
||||
return copy;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new BugException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Config getConfig() {
|
||||
return config.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfig(Config config) {
|
||||
this.config = config.clone();
|
||||
fireChangeEvent();
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package net.sf.openrocket.simulation.extension;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
/**
|
||||
* An abstract implementation of a SimulationExtensionProvider. The constructor is
|
||||
* provided by the class of the SimulationExtension and the name of the extension.
|
||||
*/
|
||||
public abstract class AbstractSimulationExtensionProvider implements SimulationExtensionProvider {
|
||||
|
||||
@Inject
|
||||
private Injector injector;
|
||||
|
||||
@Inject
|
||||
protected Translator trans;
|
||||
|
||||
protected final Class<? extends SimulationExtension> extensionClass;
|
||||
private final String[] name;
|
||||
|
||||
/**
|
||||
* Sole constructor.
|
||||
*
|
||||
* @param extensionClass the simulation extension class
|
||||
* @param name the name returned by getName
|
||||
*/
|
||||
protected AbstractSimulationExtensionProvider(Class<? extends SimulationExtension> extensionClass, String... name) {
|
||||
this.extensionClass = extensionClass;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default returns the canonical name of the simulation extension class.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getIds() {
|
||||
return Arrays.asList(extensionClass.getCanonicalName());
|
||||
}
|
||||
|
||||
/**
|
||||
* By default returns the provided extension name for the first ID that getIds returns.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getName(String id) {
|
||||
if (id.equals(getIds().get(0))) {
|
||||
return Arrays.asList(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default returns a new instance of the simulation extension class instantiated by
|
||||
* Class.newInstance.
|
||||
*/
|
||||
@Override
|
||||
public SimulationExtension getInstance(String id) {
|
||||
return injector.getInstance(extensionClass);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package net.sf.openrocket.simulation.extension;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.SimulationConditions;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.util.Config;
|
||||
|
||||
public interface SimulationExtension {
|
||||
|
||||
/**
|
||||
* Return the simulation extension ID that is used when storing this
|
||||
* extension to a file.
|
||||
*
|
||||
* @return the extension ID
|
||||
*/
|
||||
public String getId();
|
||||
|
||||
/**
|
||||
* Return a short description of this extension. The name may contain
|
||||
* elements from the extension's configuration, for example
|
||||
* "Air start (150m)".
|
||||
*
|
||||
* @return a short name / description of this extension to be shown in the UI (must not be null)
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* Return a longer description text for this extension, if available.
|
||||
* This description may be shown in the UI as extra information about
|
||||
* the extension.
|
||||
*
|
||||
* @return a longer description about this extension, or null if not available
|
||||
*/
|
||||
public String getDescription();
|
||||
|
||||
/**
|
||||
* Initialize this simulation extension for running within a simulation.
|
||||
* This method is called before running a simulation. It can either modify
|
||||
* the simulation conditions or add simulation listeners to it.
|
||||
*
|
||||
* @param conditions the simulation conditions to be run
|
||||
* @param configuration the extension configuration
|
||||
*/
|
||||
public void initialize(SimulationConditions conditions) throws SimulationException;
|
||||
|
||||
/**
|
||||
* Return a list of any flight data types this simulation extension creates.
|
||||
* This should only contain new types created by this extension, not existing
|
||||
* types that the extension adds to the flight data.
|
||||
*/
|
||||
public List<FlightDataType> getFlightDataTypes();
|
||||
|
||||
|
||||
/**
|
||||
* Return a copy of this simulation extension, with all configuration deep-copied.
|
||||
*
|
||||
* @return a new copy of this simulation extension
|
||||
*/
|
||||
public SimulationExtension clone();
|
||||
|
||||
|
||||
/**
|
||||
* Return a Config object describing the current configuration of this simulation
|
||||
* extension. The extension may keep its configuration in a Config object, or create
|
||||
* it when requested.
|
||||
*
|
||||
* @return the simulation extension configuration.
|
||||
*/
|
||||
public Config getConfig();
|
||||
|
||||
/**
|
||||
* Set this simulation extension's configuration. The extension should load all its
|
||||
* configuration from the provided Config object.
|
||||
*
|
||||
* @param config the configuration to set
|
||||
*/
|
||||
public void setConfig(Config config);
|
||||
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
package net.sf.openrocket.simulation.extension;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.openrocket.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A map containing simulation extension configuration. This map can
|
||||
* store values of type int, long, float, double, boolean, String,
|
||||
* List and SimulationExtensionConfiguration.
|
||||
*/
|
||||
public final class SimulationExtensionConfiguration extends HashMap<String, Object> {
|
||||
|
||||
private SimulationExtension extension;
|
||||
|
||||
|
||||
public SimulationExtension getExtension() {
|
||||
return extension;
|
||||
}
|
||||
|
||||
public void setExtension(SimulationExtension extension) {
|
||||
this.extension = extension;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object put(String key, Object value) {
|
||||
Class<?> c = value.getClass();
|
||||
if (c != Long.class && c != Integer.class &&
|
||||
c != Double.class && c != Float.class &&
|
||||
c != Boolean.class &&
|
||||
!(value instanceof SimulationExtensionConfiguration) &&
|
||||
!(value instanceof List)) {
|
||||
throw new UnsupportedOperationException("Invalid configuration parameter type: " + c + " key=" + key + " value=" + value);
|
||||
}
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
|
||||
public long getLong(String key, long def) {
|
||||
Object o = get(key);
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).longValue();
|
||||
} else {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
public int getInt(String key, int def) {
|
||||
Object o = get(key);
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).intValue();
|
||||
} else {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
public double getDouble(String key, double def) {
|
||||
Object o = get(key);
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).doubleValue();
|
||||
} else {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
public float getFloat(String key, float def) {
|
||||
Object o = get(key);
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).floatValue();
|
||||
} else {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getBoolean(String key, boolean def) {
|
||||
Object o = get(key);
|
||||
if (o instanceof Boolean) {
|
||||
return (Boolean) o;
|
||||
} else {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
public String getString(String key, String def) {
|
||||
Object o = get(key);
|
||||
if (o instanceof String) {
|
||||
return (String) o;
|
||||
} else {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deep-clone this object.
|
||||
|
||||
*/
|
||||
@Override
|
||||
public SimulationExtensionConfiguration clone() {
|
||||
SimulationExtensionConfiguration copy = new SimulationExtensionConfiguration();
|
||||
copy.extension = this.extension;
|
||||
for (String key : this.keySet()) {
|
||||
Object value = this.get(key);
|
||||
if (value instanceof SimulationExtensionConfiguration) {
|
||||
copy.put(key, ((SimulationExtensionConfiguration) value).clone());
|
||||
} else if (value instanceof List) {
|
||||
copy.put(key, cloneList((List<?>) value));
|
||||
} else {
|
||||
copy.put(key, value);
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
private Object cloneList(List<?> original) {
|
||||
ArrayList<Object> list = new ArrayList<Object>();
|
||||
for (Object value : original) {
|
||||
if (value instanceof SimulationExtensionConfiguration) {
|
||||
list.add(((SimulationExtensionConfiguration) value).clone());
|
||||
} else if (value instanceof List) {
|
||||
list.add(cloneList((List<?>) value));
|
||||
} else {
|
||||
list.add(value);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package net.sf.openrocket.simulation.extension;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.openrocket.plugin.Plugin;
|
||||
|
||||
@Plugin
|
||||
public interface SimulationExtensionProvider {
|
||||
|
||||
/**
|
||||
* Return a list of simulation extension ID's that this provider supports.
|
||||
* The ID is used to identify the plugin when storing files. It should follow
|
||||
* the conventions of Java package and class naming.
|
||||
*
|
||||
* @return a list of ID strings
|
||||
*/
|
||||
public List<String> getIds();
|
||||
|
||||
/**
|
||||
* Return the UI name for a simulation extension. The first values
|
||||
* are nested menus, with the last one the actual entry, for example
|
||||
* ["Launch conditions", "Air-start"].
|
||||
*
|
||||
* If the ID does not represent an extension that should be displayed
|
||||
* in the UI, this method must return null. For example, if an extension
|
||||
* has multiple ID's, this method must return the menu name for only one
|
||||
* of the ID's.
|
||||
*
|
||||
* These can be localized, and the system may attempt to localize
|
||||
* English-language names automatically (mainly for the menus).
|
||||
*
|
||||
* @param id the extension ID
|
||||
* @return the UI name for the extension, or null for no display
|
||||
*/
|
||||
public List<String> getName(String id);
|
||||
|
||||
/**
|
||||
* Return a new instance of a simulation extension. This is a new instance
|
||||
* that should have some default configuration.
|
||||
*
|
||||
* @param id the extension ID
|
||||
* @return a new simulation extension instance
|
||||
*/
|
||||
public SimulationExtension getInstance(String id);
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package net.sf.openrocket.simulation.extension.impl;
|
||||
|
||||
import net.sf.openrocket.l10n.L10N;
|
||||
import net.sf.openrocket.simulation.SimulationConditions;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;
|
||||
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
|
||||
public class AirStart extends AbstractSimulationExtension {
|
||||
|
||||
@Override
|
||||
public void initialize(SimulationConditions conditions) throws SimulationException {
|
||||
conditions.getSimulationListenerList().add(new AirStartListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
String name = trans.get("SimulationExtension.airstart.name");
|
||||
return L10N.replace(name, "{alt}", UnitGroup.UNITS_DISTANCE.toStringUnit(getLaunchAltitude()));
|
||||
}
|
||||
|
||||
public double getLaunchAltitude() {
|
||||
return config.getDouble("launchAltitude", 0.0);
|
||||
}
|
||||
|
||||
public void setLaunchAltitude(double launchAltitude) {
|
||||
config.put("launchAltitude", launchAltitude);
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
|
||||
private class AirStartListener extends AbstractSimulationListener {
|
||||
@Override
|
||||
public void startSimulation(SimulationStatus status) throws SimulationException {
|
||||
status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package net.sf.openrocket.simulation.extension.impl;
|
||||
|
||||
import net.sf.openrocket.plugin.Plugin;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;
|
||||
|
||||
@Plugin
|
||||
public class AirStartProvider extends AbstractSimulationExtensionProvider {
|
||||
|
||||
public AirStartProvider() {
|
||||
super(AirStart.class, "Launch conditions", "Air-start");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package net.sf.openrocket.simulation.extension.impl;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.openrocket.simulation.SimulationConditions;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;
|
||||
import net.sf.openrocket.simulation.listeners.SimulationListener;
|
||||
import net.sf.openrocket.util.ArrayList;
|
||||
import net.sf.openrocket.util.StringUtil;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class JavaCode extends AbstractSimulationExtension {
|
||||
|
||||
@Inject
|
||||
private Injector injector;
|
||||
|
||||
public JavaCode() {
|
||||
config.put("my_string", "foobar");
|
||||
config.put("my_int", 123);
|
||||
config.put("my_long", 123456789012345L);
|
||||
config.put("my_float", 12.345f);
|
||||
config.put("my_double", 12.345e99);
|
||||
config.put("my_bigint", new BigInteger("12345678901234567890", 10));
|
||||
config.put("my_bool", true);
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
list.add(true);
|
||||
list.add(123);
|
||||
list.add(123.456);
|
||||
list.add(Arrays.asList(1, 2, 3));
|
||||
list.add("foo");
|
||||
config.put("my_list", list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(SimulationConditions conditions) throws SimulationException {
|
||||
String className = getClassName();
|
||||
try {
|
||||
if (!StringUtil.isEmpty(className)) {
|
||||
Class<?> clazz = Class.forName(className);
|
||||
if (!SimulationListener.class.isAssignableFrom(clazz)) {
|
||||
throw new SimulationException("Class " + className + " does not implement SimulationListener");
|
||||
}
|
||||
SimulationListener listener = (SimulationListener) injector.getInstance(clazz);
|
||||
conditions.getSimulationListenerList().add(listener);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new SimulationException("Could not find class " + className);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
String name = trans.get("SimulationExtension.javacode.name") + ": ";
|
||||
String className = getClassName();
|
||||
if (!StringUtil.isEmpty(className)) {
|
||||
name = name + className;
|
||||
} else {
|
||||
name = name + trans.get("SimulationExtension.javacode.name.none");
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return config.getString("className", "");
|
||||
}
|
||||
|
||||
public void setClassName(String className) {
|
||||
config.put("className", className);
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package net.sf.openrocket.simulation.extension.impl;
|
||||
|
||||
import net.sf.openrocket.plugin.Plugin;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;
|
||||
|
||||
@Plugin
|
||||
public class JavaCodeProvider extends AbstractSimulationExtensionProvider {
|
||||
|
||||
public JavaCodeProvider() {
|
||||
super(JavaCode.class, "User code", "Java code");
|
||||
}
|
||||
|
||||
}
|
152
core/src/net/sf/openrocket/util/Config.java
Normal file
152
core/src/net/sf/openrocket/util/Config.java
Normal file
@ -0,0 +1,152 @@
|
||||
package net.sf.openrocket.util;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
public class Config {
|
||||
|
||||
private LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
|
||||
|
||||
public void put(String key, String value) {
|
||||
validateType(value);
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
public void put(String key, Number value) {
|
||||
validateType(value);
|
||||
map.put(key, clone(value));
|
||||
}
|
||||
|
||||
public void put(String key, Boolean value) {
|
||||
validateType(value);
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
public void put(String key, List<?> value) {
|
||||
validateType(value);
|
||||
map.put(key, clone(value));
|
||||
}
|
||||
|
||||
public void put(String key, Object value) {
|
||||
validateType(value);
|
||||
map.put(key, clone(value));
|
||||
}
|
||||
|
||||
|
||||
public Object get(String key, Object def) {
|
||||
return get(key, def, Object.class);
|
||||
}
|
||||
|
||||
public Boolean getBoolean(String key, Boolean def) {
|
||||
return get(key, def, Boolean.class);
|
||||
}
|
||||
|
||||
public Integer getInt(String key, Integer def) {
|
||||
Number number = get(key, null, Number.class);
|
||||
if (number == null) {
|
||||
return def;
|
||||
} else {
|
||||
return number.intValue();
|
||||
}
|
||||
}
|
||||
|
||||
public Long getLong(String key, Long def) {
|
||||
Number number = get(key, null, Number.class);
|
||||
if (number == null) {
|
||||
return def;
|
||||
} else {
|
||||
return number.longValue();
|
||||
}
|
||||
}
|
||||
|
||||
public Double getDouble(String key, Double def) {
|
||||
Number number = get(key, null, Number.class);
|
||||
if (number == null) {
|
||||
return def;
|
||||
} else {
|
||||
return number.doubleValue();
|
||||
}
|
||||
}
|
||||
|
||||
public String getString(String key, String def) {
|
||||
return get(key, def, String.class);
|
||||
}
|
||||
|
||||
public List<?> getList(String key, List<?> def) {
|
||||
return get(key, def, List.class);
|
||||
}
|
||||
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
return map.containsKey(key);
|
||||
}
|
||||
|
||||
public Set<String> keySet() {
|
||||
return Collections.unmodifiableMap(map).keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Config clone() {
|
||||
Config copy = new Config();
|
||||
for (Entry<String, Object> entry : map.entrySet()) {
|
||||
copy.map.put(entry.getKey(), clone(entry.getValue()));
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T get(String key, T def, Class<T> type) {
|
||||
Object value = map.get(key);
|
||||
if (type.isInstance(value)) {
|
||||
return (T) value;
|
||||
} else {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void validateType(Object value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException("Attempting to add null value to Config object");
|
||||
} else if (value instanceof Boolean) {
|
||||
// ok
|
||||
} else if (value instanceof Number) {
|
||||
// ok
|
||||
} else if (value instanceof String) {
|
||||
// ok
|
||||
} else if (value instanceof List<?>) {
|
||||
List<?> list = (List<?>) value;
|
||||
for (Object v : list) {
|
||||
validateType(v);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Attempting to add value of type " + value.getClass() + " to Config object, value=" + value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Object clone(Object value) {
|
||||
if (value instanceof Byte || value instanceof Short || value instanceof Integer || value instanceof Long ||
|
||||
value instanceof Float || value instanceof Double || value instanceof Boolean || value instanceof String) {
|
||||
// immutable
|
||||
return value;
|
||||
} else if (value instanceof Number) {
|
||||
return new BigDecimal(value.toString());
|
||||
} else if (value instanceof List<?>) {
|
||||
List<?> list = (List<?>) value;
|
||||
ArrayList<Object> copy = new ArrayList<Object>(list.size());
|
||||
for (Object o : list) {
|
||||
copy.add(clone(o));
|
||||
}
|
||||
return copy;
|
||||
} else {
|
||||
throw new IllegalStateException("Config contained value = " + value + " type = " + ((value != null) ? value.getClass() : "null"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
164
core/test/net/sf/openrocket/util/TestConfig.java
Normal file
164
core/test/net/sf/openrocket/util/TestConfig.java
Normal file
@ -0,0 +1,164 @@
|
||||
package net.sf.openrocket.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestConfig {
|
||||
|
||||
private Config config = new Config();
|
||||
|
||||
@Test
|
||||
public void testDoubles() {
|
||||
config.put("double", Math.PI);
|
||||
config.put("bigdecimal", new BigDecimal(Math.PI));
|
||||
assertEquals(Math.PI, config.getDouble("double", null), 0);
|
||||
assertEquals(Math.PI, config.getDouble("bigdecimal", null), 0);
|
||||
assertEquals(3, (int) config.getInt("double", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInts() {
|
||||
config.put("int", 123);
|
||||
config.put("biginteger", new BigDecimal(Math.PI));
|
||||
config.put("bigdecimal", new BigDecimal(Math.PI));
|
||||
assertEquals(123, (int) config.getInt("int", null));
|
||||
assertEquals(3, (int) config.getInt("bigdecimal", null));
|
||||
assertEquals(3, (int) config.getInt("biginteger", null));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDefaultValue() {
|
||||
assertEquals(true, config.getBoolean("foo", true));
|
||||
assertEquals(123, (int) config.getInt("foo", 123));
|
||||
assertEquals(123L, (long) config.getLong("foo", 123L));
|
||||
assertEquals(1.23, (double) config.getDouble("foo", 1.23), 0);
|
||||
assertEquals("bar", config.getString("foo", "bar"));
|
||||
assertEquals(Arrays.asList("foo"), config.getList("foo", Arrays.asList("foo")));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNullDefaultValue() {
|
||||
assertEquals(null, config.getBoolean("foo", null));
|
||||
assertEquals(null, config.getInt("foo", null));
|
||||
assertEquals(null, config.getLong("foo", null));
|
||||
assertEquals(null, config.getDouble("foo", null));
|
||||
assertEquals(null, config.getString("foo", null));
|
||||
assertEquals(null, config.getList("foo", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoringList() {
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
list.add("Foo");
|
||||
list.add(123);
|
||||
list.add(Math.PI);
|
||||
list.add(true);
|
||||
config.put("list", list);
|
||||
assertEquals(Arrays.asList("Foo", 123, Math.PI, true), config.getList("list", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModifyingStoredList() {
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
list.add("Foo");
|
||||
list.add(123);
|
||||
list.add(Math.PI);
|
||||
list.add(true);
|
||||
config.put("list", list);
|
||||
list.add("hello");
|
||||
assertEquals(Arrays.asList("Foo", 123, Math.PI, true), config.getList("list", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModifyingStoredNumber() {
|
||||
AtomicInteger ai = new AtomicInteger(100);
|
||||
config.put("ai", ai);
|
||||
ai.incrementAndGet();
|
||||
assertEquals(100, (int) config.getInt("ai", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClone() {
|
||||
config.put("string", "foo");
|
||||
config.put("int", 123);
|
||||
config.put("double", Math.PI);
|
||||
|
||||
AtomicInteger ai = new AtomicInteger(100);
|
||||
config.put("atomicinteger", ai);
|
||||
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
list.add("Foo");
|
||||
config.put("list", list);
|
||||
|
||||
Config copy = config.clone();
|
||||
|
||||
config.put("extra", "foo");
|
||||
ai.incrementAndGet();
|
||||
|
||||
assertFalse(copy.containsKey("extra"));
|
||||
assertEquals("foo", copy.getString("string", null));
|
||||
assertEquals(123, (int) copy.getInt("int", null));
|
||||
assertEquals(100, (int) copy.getInt("atomicinteger", null));
|
||||
assertEquals(Math.PI, (double) copy.getDouble("double", null), 0);
|
||||
assertEquals(Arrays.asList("Foo"), copy.getList("list", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoringNullValue() {
|
||||
try {
|
||||
config.put("foo", (Boolean) null);
|
||||
fail();
|
||||
} catch (NullPointerException e) {
|
||||
}
|
||||
try {
|
||||
config.put("foo", (String) null);
|
||||
fail();
|
||||
} catch (NullPointerException e) {
|
||||
}
|
||||
try {
|
||||
config.put("foo", (Number) null);
|
||||
fail();
|
||||
} catch (NullPointerException e) {
|
||||
}
|
||||
try {
|
||||
config.put("foo", (List<?>) null);
|
||||
fail();
|
||||
} catch (NullPointerException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoringListWithInvalidTypes() {
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
list.add("Foo");
|
||||
list.add(new Date());
|
||||
try {
|
||||
config.put("foo", list);
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoringListWithNull() {
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
list.add("Foo");
|
||||
list.add(new Date());
|
||||
try {
|
||||
config.put("foo", list);
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/java-6-openjdk-amd64"/>
|
||||
<classpathentry kind="lib" path="lib/jogl/jogl-all.jar"/>
|
||||
<classpathentry kind="lib" path="lib/iText-5.0.2.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jcommon-1.0.18.jar"/>
|
||||
|
@ -5,7 +5,15 @@ org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonN
|
||||
org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
|
||||
org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
|
||||
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
|
||||
org.eclipse.jdt.core.compiler.problem.deadCode=warning
|
||||
@ -14,6 +22,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
|
||||
org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.fallthroughCase=error
|
||||
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
|
||||
@ -34,7 +43,7 @@ org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
|
||||
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
|
||||
org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
|
||||
@ -87,3 +96,4 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
|
||||
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
|
||||
org.eclipse.jdt.core.compiler.source=1.6
|
||||
|
@ -26,6 +26,7 @@ import net.sf.openrocket.gui.util.GUIUtil;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.rocketcomponent.Configuration;
|
||||
import net.sf.openrocket.simulation.SimulationOptions;
|
||||
import net.sf.openrocket.simulation.extension.SimulationExtension;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
|
||||
|
||||
@ -91,8 +92,10 @@ public class SimulationEditDialog extends JDialog {
|
||||
if (simulation.length > 1) {
|
||||
for (int i = 1; i < simulation.length; i++) {
|
||||
simulation[i].getOptions().copyConditionsFrom(simulation[0].getOptions());
|
||||
simulation[i].getSimulationListeners().clear();
|
||||
simulation[i].getSimulationListeners().addAll(simulation[0].getSimulationListeners());
|
||||
simulation[i].getSimulationExtensions().clear();
|
||||
for (SimulationExtension c : simulation[0].getSimulationExtensions()) {
|
||||
simulation[i].getSimulationExtensions().add(c.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,26 @@
|
||||
package net.sf.openrocket.gui.simulation;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dialog.ModalityType;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.AbstractListModel;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.ListCellRenderer;
|
||||
import javax.swing.MenuElement;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
@ -25,23 +30,31 @@ import net.sf.openrocket.gui.adaptors.DoubleModel;
|
||||
import net.sf.openrocket.gui.adaptors.EnumModel;
|
||||
import net.sf.openrocket.gui.components.BasicSlider;
|
||||
import net.sf.openrocket.gui.components.DescriptionArea;
|
||||
import net.sf.openrocket.gui.components.StyledLabel;
|
||||
import net.sf.openrocket.gui.components.StyledLabel.Style;
|
||||
import net.sf.openrocket.gui.components.UnitSelector;
|
||||
import net.sf.openrocket.gui.util.GUIUtil;
|
||||
import net.sf.openrocket.gui.util.Icons;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.simulation.RK4SimulationStepper;
|
||||
import net.sf.openrocket.simulation.SimulationOptions;
|
||||
import net.sf.openrocket.simulation.listeners.SimulationListener;
|
||||
import net.sf.openrocket.simulation.listeners.example.CSVSaveListener;
|
||||
import net.sf.openrocket.simulation.extension.SimulationExtension;
|
||||
import net.sf.openrocket.simulation.extension.SimulationExtensionProvider;
|
||||
import net.sf.openrocket.simulation.extension.SwingSimulationExtensionConfigurator;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.GeodeticComputationStrategy;
|
||||
|
||||
import com.google.inject.Key;
|
||||
|
||||
class SimulationOptionsPanel extends JPanel {
|
||||
|
||||
private static final Translator trans = Application.getTranslator();
|
||||
|
||||
final Simulation simulation;
|
||||
|
||||
private JPanel currentExtensions;
|
||||
|
||||
SimulationOptionsPanel(final Simulation simulation) {
|
||||
super(new MigLayout("fill"));
|
||||
this.simulation = simulation;
|
||||
@ -162,133 +175,255 @@ class SimulationOptionsPanel extends JPanel {
|
||||
|
||||
|
||||
|
||||
//// Simulation listeners
|
||||
//// Simulation extensions
|
||||
sub = new JPanel(new MigLayout("fill, gap 0 0"));
|
||||
//// Simulator listeners
|
||||
sub.setBorder(BorderFactory.createTitledBorder(trans.get("simedtdlg.border.Simlist")));
|
||||
this.add(sub, "growx, growy");
|
||||
sub.setBorder(BorderFactory.createTitledBorder(trans.get("simedtdlg.border.SimExt")));
|
||||
this.add(sub, "wmin 300lp, growx, growy");
|
||||
|
||||
|
||||
DescriptionArea desc = new DescriptionArea(5);
|
||||
//// <html><i>Simulation listeners</i> is an advanced feature that allows user-written code to listen to and interact with the simulation.
|
||||
//// For details on writing simulation listeners, see the OpenRocket technical documentation.
|
||||
desc.setText(trans.get("simedtdlg.txt.longA1") +
|
||||
trans.get("simedtdlg.txt.longA2"));
|
||||
sub.add(desc, "aligny 0, growx, wrap para");
|
||||
desc.setText(trans.get("simedtdlg.SimExt.desc"));
|
||||
sub.add(desc, "aligny 0, hmin 100lp, growx, wrap para");
|
||||
|
||||
//// Current listeners:
|
||||
label = new JLabel(trans.get("simedtdlg.lbl.Curlist"));
|
||||
sub.add(label, "spanx, wrap rel");
|
||||
|
||||
final ListenerListModel listenerModel = new ListenerListModel();
|
||||
final JList list = new JList(listenerModel);
|
||||
list.setCellRenderer(new ListenerCellRenderer());
|
||||
JScrollPane scroll = new JScrollPane(list);
|
||||
// scroll.setPreferredSize(new Dimension(1,1));
|
||||
sub.add(scroll, "height 1px, grow, wrap rel");
|
||||
final JButton addExtension = new JButton("Add extension");
|
||||
final JPopupMenu menu = getExtensionMenu();
|
||||
addExtension.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
menu.show(addExtension, 5, addExtension.getBounds().height);
|
||||
}
|
||||
});
|
||||
sub.add(addExtension, "growx, wrap 0");
|
||||
|
||||
//// Add button
|
||||
button = new JButton(trans.get("simedtdlg.but.add"));
|
||||
currentExtensions = new JPanel(new MigLayout("fillx, gap 0 0, ins 0"));
|
||||
JScrollPane scroll = new JScrollPane(currentExtensions);
|
||||
// &#$%! scroll pane will not honor "growy"...
|
||||
sub.add(scroll, "growx, growy, h 100%");
|
||||
|
||||
updateCurrentExtensions();
|
||||
|
||||
}
|
||||
|
||||
private JPopupMenu getExtensionMenu() {
|
||||
Set<SimulationExtensionProvider> extensions = Application.getInjector().getInstance(new Key<Set<SimulationExtensionProvider>>() {
|
||||
});
|
||||
|
||||
JPopupMenu basemenu = new JPopupMenu();
|
||||
|
||||
for (final SimulationExtensionProvider provider : extensions) {
|
||||
List<String> ids = provider.getIds();
|
||||
for (final String id : ids) {
|
||||
List<String> menuItems = provider.getName(id);
|
||||
if (menuItems != null) {
|
||||
JComponent menu = findMenu(basemenu, menuItems);
|
||||
JMenuItem item = new JMenuItem(menuItems.get(menuItems.size() - 1));
|
||||
item.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
SimulationExtension e = provider.getInstance(id);
|
||||
simulation.getSimulationExtensions().add(e);
|
||||
updateCurrentExtensions();
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return basemenu;
|
||||
}
|
||||
|
||||
private JComponent findMenu(MenuElement menu, List<String> menuItems) {
|
||||
for (int i = 0; i < menuItems.size() - 1; i++) {
|
||||
String menuItem = menuItems.get(i);
|
||||
|
||||
MenuElement found = null;
|
||||
for (MenuElement e : menu.getSubElements()) {
|
||||
if (e instanceof JMenu && ((JMenu) e).getText().equals(menuItem)) {
|
||||
found = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found != null) {
|
||||
menu = found;
|
||||
} else {
|
||||
JMenu m = new JMenu(menuItem);
|
||||
((JComponent) menu).add(m);
|
||||
menu = m;
|
||||
}
|
||||
}
|
||||
return (JComponent) menu;
|
||||
}
|
||||
|
||||
|
||||
private void updateCurrentExtensions() {
|
||||
currentExtensions.removeAll();
|
||||
|
||||
if (simulation.getSimulationExtensions().isEmpty()) {
|
||||
StyledLabel l = new StyledLabel(trans.get("simedtdlg.SimExt.noExtensions"), Style.ITALIC);
|
||||
l.setForeground(Color.DARK_GRAY);
|
||||
currentExtensions.add(l, "growx, pad 5 5 5 5, wrap");
|
||||
} else {
|
||||
for (SimulationExtension e : simulation.getSimulationExtensions()) {
|
||||
currentExtensions.add(new SimulationExtensionPanel(e), "growx, wrap");
|
||||
}
|
||||
}
|
||||
// Both needed:
|
||||
this.revalidate();
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
|
||||
private class SimulationExtensionPanel extends JPanel {
|
||||
|
||||
public SimulationExtensionPanel(final SimulationExtension extension) {
|
||||
super(new MigLayout("fillx, gapx 0"));
|
||||
|
||||
this.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
|
||||
this.add(new JLabel(extension.getName()), "spanx, growx, wrap");
|
||||
|
||||
JButton button;
|
||||
|
||||
this.add(new JPanel(), "spanx, split, growx, right");
|
||||
|
||||
if (findConfigurator(extension) != null) {
|
||||
button = new JButton(Icons.CONFIGURE);
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String previous = Application.getPreferences().getString("previousListenerName", "");
|
||||
String input = (String) JOptionPane.showInputDialog(SwingUtilities.getRoot(SimulationOptionsPanel.this),
|
||||
new Object[] {
|
||||
//// Type the full Java class name of the simulation listener, for example:
|
||||
"Type the full Java class name of the simulation listener, for example:",
|
||||
"<html><tt>" + CSVSaveListener.class.getName() + "</tt>" },
|
||||
//// Add simulation listener
|
||||
trans.get("simedtdlg.lbl.Addsimlist"),
|
||||
JOptionPane.QUESTION_MESSAGE,
|
||||
null, null,
|
||||
previous
|
||||
);
|
||||
if (input == null || input.equals(""))
|
||||
return;
|
||||
|
||||
Application.getPreferences().putString("previousListenerName", input);
|
||||
simulation.getSimulationListeners().add(input);
|
||||
listenerModel.fireContentsChanged();
|
||||
findConfigurator(extension).configure(extension, simulation,
|
||||
SwingUtilities.windowForComponent(SimulationOptionsPanel.this));
|
||||
updateCurrentExtensions();
|
||||
}
|
||||
});
|
||||
sub.add(button, "split 2, sizegroup buttons, alignx 50%, gapright para");
|
||||
this.add(button, "right");
|
||||
}
|
||||
|
||||
//// Remove button
|
||||
button = new JButton(trans.get("simedtdlg.but.remove"));
|
||||
if (extension.getDescription() != null) {
|
||||
button = new JButton(Icons.HELP);
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
int[] selected = list.getSelectedIndices();
|
||||
Arrays.sort(selected);
|
||||
for (int i = selected.length - 1; i >= 0; i--) {
|
||||
simulation.getSimulationListeners().remove(selected[i]);
|
||||
}
|
||||
listenerModel.fireContentsChanged();
|
||||
final JDialog dialog = new JDialog(SwingUtilities.windowForComponent(SimulationOptionsPanel.this),
|
||||
extension.getName(), ModalityType.APPLICATION_MODAL);
|
||||
JPanel panel = new JPanel(new MigLayout("fill"));
|
||||
DescriptionArea area = new DescriptionArea(extension.getDescription(), 10, 0);
|
||||
panel.add(area, "width 400lp, wrap para");
|
||||
JButton close = new JButton(trans.get("button.close"));
|
||||
close.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
dialog.setVisible(false);
|
||||
}
|
||||
});
|
||||
sub.add(button, "sizegroup buttons, alignx 50%");
|
||||
|
||||
|
||||
panel.add(close, "right");
|
||||
dialog.add(panel);
|
||||
GUIUtil.setDisposableDialogOptions(dialog, close);
|
||||
dialog.setLocationRelativeTo(SwingUtilities.windowForComponent(SimulationOptionsPanel.this));
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
});
|
||||
this.add(button, "right");
|
||||
}
|
||||
|
||||
private class ListenerCellRenderer extends JLabel implements ListCellRenderer {
|
||||
|
||||
button = new JButton(Icons.DELETE);
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList list, Object value,
|
||||
int index, boolean isSelected, boolean cellHasFocus) {
|
||||
String s = value.toString();
|
||||
setText(s);
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
Iterator<SimulationExtension> iter = simulation.getSimulationExtensions().iterator();
|
||||
while (iter.hasNext()) {
|
||||
// Compare with identity
|
||||
if (iter.next() == extension) {
|
||||
iter.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
updateCurrentExtensions();
|
||||
}
|
||||
});
|
||||
this.add(button, "right");
|
||||
|
||||
// Attempt instantiating, catch any exceptions
|
||||
Exception ex = null;
|
||||
try {
|
||||
Class<?> c = Class.forName(s);
|
||||
@SuppressWarnings("unused")
|
||||
SimulationListener l = (SimulationListener) c.newInstance();
|
||||
} catch (Exception e) {
|
||||
ex = e;
|
||||
}
|
||||
|
||||
if (ex == null) {
|
||||
setIcon(Icons.SIMULATION_LISTENER_OK);
|
||||
//// Listener instantiated successfully.
|
||||
setToolTipText("Listener instantiated successfully.");
|
||||
} else {
|
||||
setIcon(Icons.SIMULATION_LISTENER_ERROR);
|
||||
//// <html>Unable to instantiate listener due to exception:<br>
|
||||
setToolTipText("<html>Unable to instantiate listener due to exception:<br>" +
|
||||
ex.toString());
|
||||
}
|
||||
|
||||
if (isSelected) {
|
||||
setBackground(list.getSelectionBackground());
|
||||
setForeground(list.getSelectionForeground());
|
||||
} else {
|
||||
setBackground(list.getBackground());
|
||||
setForeground(list.getForeground());
|
||||
}
|
||||
setOpaque(true);
|
||||
return this;
|
||||
private SwingSimulationExtensionConfigurator findConfigurator(SimulationExtension extension) {
|
||||
Set<SwingSimulationExtensionConfigurator> configurators = Application.getInjector().getInstance(new Key<Set<SwingSimulationExtensionConfigurator>>() {
|
||||
});
|
||||
for (SwingSimulationExtensionConfigurator c : configurators) {
|
||||
if (c.support(extension)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
private class ListenerListModel extends AbstractListModel {
|
||||
@Override
|
||||
public String getElementAt(int index) {
|
||||
if (index < 0 || index >= getSize())
|
||||
return null;
|
||||
return simulation.getSimulationListeners().get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return simulation.getSimulationListeners().size();
|
||||
}
|
||||
|
||||
public void fireContentsChanged() {
|
||||
super.fireContentsChanged(this, 0, getSize());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// private class ExtensionListModel extends AbstractListModel {
|
||||
// @Override
|
||||
// public SimulationExtensionConfiguration getElementAt(int index) {
|
||||
// if (index < 0 || index >= getSize())
|
||||
// return null;
|
||||
// return simulation.getSimulationExtensions().get(index);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int getSize() {
|
||||
// return simulation.getSimulationExtensions().size();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// private class ExtensionCellRenderer extends JPanel implements ListCellRenderer {
|
||||
// private JLabel label;
|
||||
//
|
||||
// public ExtensionCellRenderer() {
|
||||
// super(new MigLayout("fill"));
|
||||
// label = new JLabel();
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Component getListCellRendererComponent(JList list, Object value,
|
||||
// int index, boolean isSelected, boolean cellHasFocus) {
|
||||
// SimulationExtensionConfiguration config = (SimulationExtensionConfiguration) value;
|
||||
//
|
||||
//
|
||||
//
|
||||
// String s = value.toString();
|
||||
// setText(s);
|
||||
//
|
||||
// // Attempt instantiating, catch any exceptions
|
||||
// Exception ex = null;
|
||||
// try {
|
||||
// Class<?> c = Class.forName(s);
|
||||
// @SuppressWarnings("unused")
|
||||
// SimulationListener l = (SimulationListener) c.newInstance();
|
||||
// } catch (Exception e) {
|
||||
// ex = e;
|
||||
// }
|
||||
//
|
||||
// if (ex == null) {
|
||||
// setIcon(Icons.SIMULATION_LISTENER_OK);
|
||||
// //// Listener instantiated successfully.
|
||||
// setToolTipText("Listener instantiated successfully.");
|
||||
// } else {
|
||||
// setIcon(Icons.SIMULATION_LISTENER_ERROR);
|
||||
// //// <html>Unable to instantiate listener due to exception:<br>
|
||||
// setToolTipText("<html>Unable to instantiate listener due to exception:<br>" +
|
||||
// ex.toString());
|
||||
// }
|
||||
//
|
||||
// if (isSelected) {
|
||||
// setBackground(list.getSelectionBackground());
|
||||
// setForeground(list.getSelectionForeground());
|
||||
// } else {
|
||||
// setBackground(list.getBackground());
|
||||
// setForeground(list.getForeground());
|
||||
// }
|
||||
// setOpaque(true);
|
||||
// return this;
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
@ -71,9 +71,6 @@ import javax.swing.tree.DefaultTreeSelectionModel;
|
||||
import javax.swing.tree.TreeModel;
|
||||
import javax.swing.tree.TreeSelectionModel;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.sf.openrocket.gui.Resettable;
|
||||
import net.sf.openrocket.logging.Markers;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
@ -81,6 +78,9 @@ import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.Invalidatable;
|
||||
import net.sf.openrocket.util.MemoryManagement;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class GUIUtil {
|
||||
private static final Logger log = LoggerFactory.getLogger(GUIUtil.class);
|
||||
|
||||
@ -147,6 +147,7 @@ public class GUIUtil {
|
||||
installEscapeCloseOperation(dialog);
|
||||
setWindowIcons(dialog);
|
||||
addModelNullingListener(dialog);
|
||||
dialog.setLocationRelativeTo(dialog.getOwner());
|
||||
dialog.setLocationByPlatform(true);
|
||||
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
||||
dialog.pack();
|
||||
|
@ -8,13 +8,13 @@ import java.util.Map;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
public class Icons {
|
||||
private static final Logger log = LoggerFactory.getLogger(Icons.class);
|
||||
@ -77,6 +77,8 @@ public class Icons {
|
||||
|
||||
public static final Icon DELETE = loadImageIcon("pix/icons/delete.png", "Delete");
|
||||
public static final Icon EDIT = loadImageIcon("pix/icons/pencil.png", "Edit");
|
||||
public static final Icon CONFIGURE = loadImageIcon("pix/icons/configure.png", "Configure");
|
||||
public static final Icon HELP = loadImageIcon("pix/icons/help-about.png", "Help");
|
||||
public static final Icon UP = loadImageIcon("pix/icons/up.png", "Up");
|
||||
public static final Icon DOWN = loadImageIcon("pix/icons/down.png", "Down");
|
||||
|
||||
|
@ -0,0 +1,69 @@
|
||||
package net.sf.openrocket.simulation.extension;
|
||||
|
||||
import java.awt.Dialog.ModalityType;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.gui.util.GUIUtil;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public abstract class AbstractSwingSimulationExtensionConfigurator<E extends SimulationExtension> implements SwingSimulationExtensionConfigurator {
|
||||
|
||||
@Inject
|
||||
protected Translator trans;
|
||||
|
||||
private final Class<E> extensionClass;
|
||||
|
||||
protected AbstractSwingSimulationExtensionConfigurator(Class<E> extensionClass) {
|
||||
this.extensionClass = extensionClass;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean support(SimulationExtension extension) {
|
||||
return extensionClass.isInstance(extension);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void configure(SimulationExtension extension, Simulation simulation, Window parent) {
|
||||
final JDialog dialog = new JDialog(parent, getTitle(extension, simulation), ModalityType.APPLICATION_MODAL);
|
||||
JPanel panel = new JPanel(new MigLayout("fill"));
|
||||
JPanel sub = new JPanel(new MigLayout("fill, ins 0"));
|
||||
|
||||
panel.add(getConfigurationComponent((E) extension, simulation, sub), "grow, wrap para");
|
||||
|
||||
JButton close = new JButton(trans.get("button.close"));
|
||||
close.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
dialog.setVisible(false);
|
||||
}
|
||||
});
|
||||
panel.add(close, "right");
|
||||
|
||||
dialog.add(panel);
|
||||
GUIUtil.setDisposableDialogOptions(dialog, close);
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a title for the dialog window. By default uses the extension's name.
|
||||
*/
|
||||
protected String getTitle(SimulationExtension extension, Simulation simulation) {
|
||||
return extension.getName();
|
||||
}
|
||||
|
||||
protected abstract JComponent getConfigurationComponent(E extension, Simulation simulation, JPanel panel);
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package net.sf.openrocket.simulation.extension;
|
||||
|
||||
import java.awt.Window;
|
||||
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.plugin.Plugin;
|
||||
|
||||
@Plugin
|
||||
public interface SwingSimulationExtensionConfigurator {
|
||||
|
||||
/**
|
||||
* Test whether this configurator supports configuring an extension.
|
||||
*
|
||||
* @param extension the extension to test
|
||||
* @return true if this configurator can configure the specified extension
|
||||
*/
|
||||
public boolean support(SimulationExtension extension);
|
||||
|
||||
/**
|
||||
* Open an application-modal dialog for configuring a simulation extension.
|
||||
* Close the dialog when ready.
|
||||
*
|
||||
* @param extension the extension to configure
|
||||
* @param simulation the simulation the extension is attached to
|
||||
* @param parent the parent window for the dialog
|
||||
*/
|
||||
public void configure(SimulationExtension extension, Simulation simulation, Window parent);
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package net.sf.openrocket.simulation.extension.impl;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSpinner;
|
||||
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.gui.SpinnerEditor;
|
||||
import net.sf.openrocket.gui.adaptors.DoubleModel;
|
||||
import net.sf.openrocket.gui.components.BasicSlider;
|
||||
import net.sf.openrocket.gui.components.UnitSelector;
|
||||
import net.sf.openrocket.plugin.Plugin;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
|
||||
@Plugin
|
||||
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator<AirStart> {
|
||||
|
||||
public AirStartConfigurator() {
|
||||
super(AirStart.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {
|
||||
panel.add(new JLabel("Launch altitude:"));
|
||||
|
||||
DoubleModel m = new DoubleModel(extension, "LaunchAltitude", UnitGroup.UNITS_DISTANCE, 0);
|
||||
|
||||
JSpinner spin = new JSpinner(m.getSpinnerModel());
|
||||
spin.setEditor(new SpinnerEditor(spin));
|
||||
panel.add(spin, "w 65lp!");
|
||||
|
||||
UnitSelector unit = new UnitSelector(m);
|
||||
panel.add(unit, "w 25");
|
||||
|
||||
BasicSlider slider = new BasicSlider(m.getSliderModel(0, 1000));
|
||||
panel.add(slider, "w 75lp, wrap");
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package net.sf.openrocket.simulation.extension.impl;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.plugin.Plugin;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;
|
||||
|
||||
@Plugin
|
||||
public class JavaCodeConfigurator extends AbstractSwingSimulationExtensionConfigurator<JavaCode> {
|
||||
|
||||
public JavaCodeConfigurator() {
|
||||
super(JavaCode.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent getConfigurationComponent(final JavaCode extension, Simulation simulation, JPanel panel) {
|
||||
panel.add(new JLabel(trans.get("SimulationExtension.javacode.desc")), "wrap para");
|
||||
panel.add(new JLabel(trans.get("SimulationExtension.javacode.className")), "wrap rel");
|
||||
final JTextField textField = new JTextField(extension.getClassName());
|
||||
textField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
update();
|
||||
}
|
||||
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
update();
|
||||
}
|
||||
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
update();
|
||||
}
|
||||
|
||||
public void update() {
|
||||
extension.setClassName(textField.getText());
|
||||
}
|
||||
});
|
||||
panel.add(textField, "growx");
|
||||
return panel;
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user