commit
073c868a1e
@ -47,3 +47,6 @@ The following file format versions exist:
|
||||
1.6: Introduced with OpenRocket 13.04. Added component Appearances (decals & paint)
|
||||
Added configurable parameters to recovery devices, motor ignition and separation.
|
||||
|
||||
1.7: Introduced with OpenRocket 15.01. Added simulation extensions and related
|
||||
configuration.
|
||||
|
@ -402,11 +402,11 @@ 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.SimExt.add = Add extension
|
||||
simedtdlg.SimExt.copyExtension = Copy extension
|
||||
simedtdlg.lbl.Noflightdata = No flight data available.
|
||||
simedtdlg.lbl.runsimfirst = Please run the simulation first.
|
||||
simedtdlg.chart.Simflight = Simulated flight
|
||||
@ -419,6 +419,26 @@ simedtdlg.IntensityDesc.High = High
|
||||
simedtdlg.IntensityDesc.Veryhigh = Very high
|
||||
simedtdlg.IntensityDesc.Extreme = Extreme
|
||||
|
||||
SimulationExtension.airstart.name.alt = Air-start ({alt})
|
||||
SimulationExtension.airstart.name.altvel = Air-start ({alt}, {vel})
|
||||
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:
|
||||
|
||||
SimulationExtension.scripting.name = {language} script
|
||||
SimulationExtension.scripting.desc = Extend OpenRocket simulations by custom scripts.
|
||||
SimulationExtension.scripting.language.label = Language:
|
||||
SimulationExtension.scripting.warning.disabled = Untrusted scripts have been disabled. You need to manually enable them in the Simulation options.
|
||||
SimulationExtension.scripting.text.enabled = Enable script
|
||||
SimulationExtension.scripting.text.enabled.ttip = The script is run only when enabled.
|
||||
SimulationExtension.scripting.text.trusted = Trust this script on this computer
|
||||
SimulationExtension.scripting.text.trusted.msg = Untrusted scripts are disabled when loading the document
|
||||
SimulationExtension.scripting.text.trusted.clear = Clear trusted scripts
|
||||
SimulationExtension.scripting.text.trusted.clear.ttip = Clear the trusted status of all scripts on this computer
|
||||
SimulationExtension.scripting.text.trusted.cleared = All scripts are now untrusted on this computer.
|
||||
SimulationExtension.scripting.text.trusted.cleared.title = Cleared
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,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;
|
||||
@ -86,7 +86,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;
|
||||
@ -126,7 +127,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");
|
||||
@ -152,8 +153,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);
|
||||
}
|
||||
|
||||
|
||||
@ -206,14 +207,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;
|
||||
}
|
||||
|
||||
|
||||
@ -325,16 +326,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;
|
||||
@ -442,7 +435,10 @@ 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();
|
||||
copy.simulationExtensions = new ArrayList<SimulationExtension>();
|
||||
for (SimulationExtension c : this.simulationExtensions) {
|
||||
copy.simulationExtensions.add(c.clone());
|
||||
}
|
||||
copy.listeners = new ArrayList<EventListener>();
|
||||
copy.simulatedConditions = null;
|
||||
copy.simulatedConfiguration = null;
|
||||
@ -474,7 +470,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;
|
||||
@ -217,6 +219,11 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
*/
|
||||
private int calculateNecessaryFileVersion(OpenRocketDocument document, StorageOptions opts) {
|
||||
/*
|
||||
* NOTE: Remember to update the supported versions in DocumentConfig as well!
|
||||
*
|
||||
* File version 1.7 is required for:
|
||||
* - simulation extensions
|
||||
*
|
||||
* File version 1.6 is required for:
|
||||
* - saving files using appearances and textures, flight configurations.
|
||||
*
|
||||
@ -236,6 +243,16 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
* Otherwise use version 1.0.
|
||||
*/
|
||||
|
||||
/////////////////
|
||||
// Version 1.7 //
|
||||
/////////////////
|
||||
for (Simulation sim : document.getSimulations()) {
|
||||
if (!sim.getSimulationExtensions().isEmpty()) {
|
||||
return FILE_VERSION_DIVISOR + 7;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////
|
||||
// Version 1.6 //
|
||||
/////////////////
|
||||
@ -455,9 +472,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 +538,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\">" + TextUtil.escapeXML((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,89 @@
|
||||
package net.sf.openrocket.file.openrocket.importt;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -48,7 +48,7 @@ import net.sf.openrocket.util.Reflection;
|
||||
class DocumentConfig {
|
||||
|
||||
/* Remember to update OpenRocketSaver as well! */
|
||||
public static final String[] SUPPORTED_VERSIONS = { "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6" };
|
||||
public static final String[] SUPPORTED_VERSIONS = { "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7" };
|
||||
|
||||
/**
|
||||
* Divisor used in converting an integer version to the point-represented version.
|
||||
|
@ -13,6 +13,7 @@ import net.sf.openrocket.file.RocketLoadException;
|
||||
import net.sf.openrocket.file.simplesax.SimpleSAX;
|
||||
import net.sf.openrocket.simulation.FlightDataBranch;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.extension.SimulationExtension;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -77,12 +78,18 @@ public class OpenRocketLoader extends AbstractRocketLoader {
|
||||
previousTime = time;
|
||||
}
|
||||
}
|
||||
// Round value
|
||||
timeSkip = Math.rint(timeSkip * 100) / 100;
|
||||
|
||||
doc.getDefaultStorageOptions().setSimulationTimeSkip(timeSkip);
|
||||
doc.getDefaultStorageOptions().setExplicitlySet(false);
|
||||
|
||||
// Call simulation extensions
|
||||
for (Simulation sim : doc.getSimulations()) {
|
||||
for (SimulationExtension ext : sim.getSimulationExtensions()) {
|
||||
ext.documentLoaded(doc, sim, warnings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
doc.clearUndo();
|
||||
log.info("Loading done");
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -12,6 +12,7 @@ import net.sf.openrocket.models.wind.WindModel;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.simulation.listeners.SimulationListener;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.GeodeticComputationStrategy;
|
||||
import net.sf.openrocket.util.Monitorable;
|
||||
import net.sf.openrocket.util.WorldCoordinate;
|
||||
@ -38,11 +39,14 @@ public class SimulationConditions implements Monitorable, Cloneable {
|
||||
/** Launch rod direction, 0 = north */
|
||||
private double launchRodDirection = 0;
|
||||
|
||||
// TODO: Depreciate these and use worldCoordinate only.
|
||||
//private double launchAltitude = 0;
|
||||
//private double launchLatitude = 45;
|
||||
//private double launchLongitude = 0;
|
||||
// Launch site location (lat, lon, alt)
|
||||
private WorldCoordinate launchSite = new WorldCoordinate(0, 0, 0);
|
||||
|
||||
// Launch location in simulation coordinates (normally always 0, air-start would override this)
|
||||
private Coordinate launchPosition = Coordinate.NUL;
|
||||
|
||||
private Coordinate launchVelocity = Coordinate.NUL;
|
||||
|
||||
private GeodeticComputationStrategy geodeticComputation = GeodeticComputationStrategy.SPHERICAL;
|
||||
|
||||
|
||||
@ -166,6 +170,29 @@ public class SimulationConditions implements Monitorable, Cloneable {
|
||||
}
|
||||
|
||||
|
||||
public Coordinate getLaunchPosition() {
|
||||
return launchPosition;
|
||||
}
|
||||
|
||||
public void setLaunchPosition(Coordinate launchPosition) {
|
||||
if (this.launchPosition.equals(launchPosition))
|
||||
return;
|
||||
this.launchPosition = launchPosition;
|
||||
this.modID++;
|
||||
}
|
||||
|
||||
public Coordinate getLaunchVelocity() {
|
||||
return launchVelocity;
|
||||
}
|
||||
|
||||
public void setLaunchVelocity(Coordinate launchVelocity) {
|
||||
if (this.launchVelocity.equals(launchVelocity))
|
||||
return;
|
||||
this.launchVelocity = launchVelocity;
|
||||
this.modID++;
|
||||
}
|
||||
|
||||
|
||||
public GeodeticComputationStrategy getGeodeticComputation() {
|
||||
return geodeticComputation;
|
||||
}
|
||||
|
@ -95,8 +95,8 @@ public class SimulationStatus implements Monitorable {
|
||||
|
||||
this.time = 0;
|
||||
this.previousTimeStep = this.simulationConditions.getTimeStep();
|
||||
this.position = Coordinate.NUL;
|
||||
this.velocity = Coordinate.NUL;
|
||||
this.position = this.simulationConditions.getLaunchPosition();
|
||||
this.velocity = this.simulationConditions.getLaunchVelocity();
|
||||
this.worldPosition = this.simulationConditions.getLaunchSite();
|
||||
|
||||
// Initialize to roll angle with least stability w.r.t. the wind
|
||||
|
@ -0,0 +1,122 @@
|
||||
package net.sf.openrocket.simulation.extension;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.WarningSet;
|
||||
import net.sf.openrocket.document.OpenRocketDocument;
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* By default, this method returns the canonical name of this class.
|
||||
*/
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.getClass().getCanonicalName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* By default, this method returns the name provided to the constructor.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* By default, this method returns null.
|
||||
*/
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* By default, this method returns an empty list.
|
||||
*/
|
||||
@Override
|
||||
public List<FlightDataType> getFlightDataTypes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* By default, this method does nothing.
|
||||
*/
|
||||
@Override
|
||||
public void documentLoaded(OpenRocketDocument document, Simulation simulation, WarningSet warnings) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, returns a new object obtained by calling Object.clone() and
|
||||
* cloning the config object.
|
||||
*/
|
||||
@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,94 @@
|
||||
package net.sf.openrocket.simulation.extension;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.WarningSet;
|
||||
import net.sf.openrocket.document.OpenRocketDocument;
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
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();
|
||||
|
||||
/**
|
||||
* Called once for each simulation this extension is attached to when loading a document.
|
||||
* This may perform necessary changes to the document at load time.
|
||||
*
|
||||
* @param document the loaded document
|
||||
* @param simulation the simulation this extension is attached to
|
||||
* @param warnings the document loading warnings
|
||||
*/
|
||||
public void documentLoaded(OpenRocketDocument document, Simulation simulation, WarningSet warnings);
|
||||
|
||||
/**
|
||||
* 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,58 @@
|
||||
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;
|
||||
if (getLaunchVelocity() > 0.01) {
|
||||
name = trans.get("SimulationExtension.airstart.name.altvel");
|
||||
} else {
|
||||
name = trans.get("SimulationExtension.airstart.name.alt");
|
||||
}
|
||||
name = L10N.replace(name, "{alt}", UnitGroup.UNITS_DISTANCE.toStringUnit(getLaunchAltitude()));
|
||||
name = L10N.replace(name, "{vel}", UnitGroup.UNITS_VELOCITY.toStringUnit(getLaunchVelocity()));
|
||||
return name;
|
||||
}
|
||||
|
||||
public double getLaunchAltitude() {
|
||||
return config.getDouble("launchAltitude", 0.0);
|
||||
}
|
||||
|
||||
public void setLaunchAltitude(double launchAltitude) {
|
||||
config.put("launchAltitude", launchAltitude);
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
public double getLaunchVelocity() {
|
||||
return config.getDouble("launchVelocity", 0.0);
|
||||
}
|
||||
|
||||
public void setLaunchVelocity(double launchVelocity) {
|
||||
config.put("launchVelocity", launchVelocity);
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
|
||||
private class AirStartListener extends AbstractSimulationListener {
|
||||
@Override
|
||||
public void startSimulation(SimulationStatus status) throws SimulationException {
|
||||
status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));
|
||||
status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));
|
||||
}
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
package net.sf.openrocket.simulation.extension.impl;
|
||||
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.Warning;
|
||||
import net.sf.openrocket.aerodynamics.WarningSet;
|
||||
import net.sf.openrocket.document.OpenRocketDocument;
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.l10n.L10N;
|
||||
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 com.google.inject.Inject;
|
||||
|
||||
public class ScriptingExtension extends AbstractSimulationExtension {
|
||||
|
||||
private static final String DEFAULT_LANGUAGE = "JavaScript";
|
||||
|
||||
@Inject
|
||||
private ScriptingUtil util;
|
||||
|
||||
|
||||
public ScriptingExtension() {
|
||||
setLanguage(DEFAULT_LANGUAGE);
|
||||
setScript("");
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
String name = trans.get("SimulationExtension.scripting.name");
|
||||
name = L10N.replace(name, "{language}", getLanguage());
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return trans.get("SimulationExtension.scripting.desc");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void documentLoaded(OpenRocketDocument document, Simulation simulation, WarningSet warnings) {
|
||||
/*
|
||||
* Scripts that the user has not explicitly indicated as trusted are disabled
|
||||
* when loading from a file. This is to prevent trojans.
|
||||
*/
|
||||
if (isEnabled()) {
|
||||
if (!util.isTrustedScript(getLanguage(), getScript())) {
|
||||
setEnabled(false);
|
||||
warnings.add(Warning.fromString(trans.get("SimulationExtension.scripting.warning.disabled")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(SimulationConditions conditions) throws SimulationException {
|
||||
if (isEnabled()) {
|
||||
conditions.getSimulationListenerList().add(getListener());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String getScript() {
|
||||
return config.getString("script", "");
|
||||
}
|
||||
|
||||
public void setScript(String script) {
|
||||
config.put("script", script);
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return config.getString("language", DEFAULT_LANGUAGE);
|
||||
}
|
||||
|
||||
public void setLanguage(String language) {
|
||||
config.put("language", language);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return config.getBoolean("enabled", false);
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
config.put("enabled", enabled);
|
||||
}
|
||||
|
||||
|
||||
SimulationListener getListener() throws SimulationException {
|
||||
ScriptEngineManager manager = new ScriptEngineManager();
|
||||
ScriptEngine engine = manager.getEngineByName(getLanguage());
|
||||
if (engine == null) {
|
||||
throw new SimulationException("Your JRE does not support the scripting language '" + getLanguage() + "'");
|
||||
}
|
||||
|
||||
try {
|
||||
engine.eval(getScript());
|
||||
} catch (ScriptException e) {
|
||||
throw new SimulationException("Invalid script: " + e.getMessage());
|
||||
}
|
||||
|
||||
if (!(engine instanceof Invocable)) {
|
||||
throw new SimulationException("The scripting language '" + getLanguage() + "' does not implement the Invocable interface");
|
||||
}
|
||||
return new ScriptingSimulationListener((Invocable) engine);
|
||||
}
|
||||
|
||||
}
|
@ -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 ScriptingProvider extends AbstractSimulationExtensionProvider {
|
||||
|
||||
public ScriptingProvider() {
|
||||
super(ScriptingExtension.class, "User code", "Scripts");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,227 @@
|
||||
package net.sf.openrocket.simulation.extension.impl;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.AerodynamicForces;
|
||||
import net.sf.openrocket.aerodynamics.FlightConditions;
|
||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
import net.sf.openrocket.motor.MotorId;
|
||||
import net.sf.openrocket.motor.MotorInstance;
|
||||
import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
|
||||
import net.sf.openrocket.simulation.AccelerationData;
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.simulation.MassData;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationListenerException;
|
||||
import net.sf.openrocket.simulation.listeners.SimulationComputationListener;
|
||||
import net.sf.openrocket.simulation.listeners.SimulationEventListener;
|
||||
import net.sf.openrocket.simulation.listeners.SimulationListener;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ScriptingSimulationListener implements SimulationListener, SimulationComputationListener, SimulationEventListener, Cloneable {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(ScriptingSimulationListener.class);
|
||||
|
||||
/*
|
||||
* NOTE: This class is used instead of using the scripting interface API
|
||||
* so that unimplemented script methods are not called unnecessarily.
|
||||
*/
|
||||
|
||||
private Invocable invocable;
|
||||
private Set<String> missing = new HashSet<String>();
|
||||
|
||||
|
||||
public ScriptingSimulationListener(Invocable invocable) {
|
||||
this.invocable = invocable;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isSystemListener() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SimulationListener clone() {
|
||||
try {
|
||||
ScriptingSimulationListener clone = (ScriptingSimulationListener) super.clone();
|
||||
clone.missing = new HashSet<String>(missing);
|
||||
return clone;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new BugException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//// SimulationListener ////
|
||||
|
||||
@Override
|
||||
public void startSimulation(SimulationStatus status) throws SimulationException {
|
||||
invoke(Void.class, null, "startSimulation", status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endSimulation(SimulationStatus status, SimulationException exception) {
|
||||
try {
|
||||
invoke(Void.class, null, "endSimulation", status, exception);
|
||||
} catch (SimulationException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preStep(SimulationStatus status) throws SimulationException {
|
||||
return invoke(Boolean.class, true, "preStep", status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStep(SimulationStatus status) throws SimulationException {
|
||||
invoke(Void.class, null, "postStep", status);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//// SimulationEventListener ////
|
||||
|
||||
@Override
|
||||
public boolean addFlightEvent(SimulationStatus status, FlightEvent event) throws SimulationException {
|
||||
return invoke(Boolean.class, true, "addFlightEvent", status, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) throws SimulationException {
|
||||
return invoke(Boolean.class, true, "handleFlightEvent", status, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean motorIgnition(SimulationStatus status, MotorId motorId, MotorMount mount, MotorInstance instance) throws SimulationException {
|
||||
return invoke(Boolean.class, true, "motorIgnition", status, motorId, mount, instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean recoveryDeviceDeployment(SimulationStatus status, RecoveryDevice recoveryDevice) throws SimulationException {
|
||||
return invoke(Boolean.class, true, "recoveryDeviceDeployment", status, recoveryDevice);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//// SimulationComputationListener ////
|
||||
|
||||
@Override
|
||||
public AccelerationData preAccelerationCalculation(SimulationStatus status) throws SimulationException {
|
||||
return invoke(AccelerationData.class, null, "preAccelerationCalculation", status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AerodynamicForces preAerodynamicCalculation(SimulationStatus status) throws SimulationException {
|
||||
return invoke(AerodynamicForces.class, null, "preAerodynamicCalculation", status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtmosphericConditions preAtmosphericModel(SimulationStatus status) throws SimulationException {
|
||||
return invoke(AtmosphericConditions.class, null, "preAtmosphericModel", status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlightConditions preFlightConditions(SimulationStatus status) throws SimulationException {
|
||||
return invoke(FlightConditions.class, null, "preFlightConditions", status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double preGravityModel(SimulationStatus status) throws SimulationException {
|
||||
return invoke(Double.class, Double.NaN, "preGravityModel", status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MassData preMassCalculation(SimulationStatus status) throws SimulationException {
|
||||
return invoke(MassData.class, null, "preMassCalculation", status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double preSimpleThrustCalculation(SimulationStatus status) throws SimulationException {
|
||||
return invoke(Double.class, Double.NaN, "preSimpleThrustCalculation", status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate preWindModel(SimulationStatus status) throws SimulationException {
|
||||
return invoke(Coordinate.class, null, "preWindModel", status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccelerationData postAccelerationCalculation(SimulationStatus status, AccelerationData acceleration) throws SimulationException {
|
||||
return invoke(AccelerationData.class, null, "postAccelerationCalculation", status, acceleration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AerodynamicForces postAerodynamicCalculation(SimulationStatus status, AerodynamicForces forces) throws SimulationException {
|
||||
return invoke(AerodynamicForces.class, null, "postAerodynamicCalculation", status, forces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtmosphericConditions postAtmosphericModel(SimulationStatus status, AtmosphericConditions atmosphericConditions) throws SimulationException {
|
||||
return invoke(AtmosphericConditions.class, null, "postAtmosphericModel", status, atmosphericConditions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) throws SimulationException {
|
||||
return invoke(FlightConditions.class, null, "postFlightConditions", status, flightConditions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double postGravityModel(SimulationStatus status, double gravity) throws SimulationException {
|
||||
return invoke(Double.class, Double.NaN, "postGravityModel", status, gravity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MassData postMassCalculation(SimulationStatus status, MassData massData) throws SimulationException {
|
||||
return invoke(MassData.class, null, "postMassCalculation", status, massData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double postSimpleThrustCalculation(SimulationStatus status, double thrust) throws SimulationException {
|
||||
return invoke(Double.class, Double.NaN, "postSimpleThrustCalculation", status, thrust);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate postWindModel(SimulationStatus status, Coordinate wind) throws SimulationException {
|
||||
return invoke(Coordinate.class, null, "postWindModel", status, wind);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T invoke(Class<T> retType, T def, String method, Object... args) throws SimulationException {
|
||||
try {
|
||||
if (!missing.contains(method)) {
|
||||
Object o = invocable.invokeFunction(method, args);
|
||||
if (o == null) {
|
||||
// Use default/null if function returns nothing
|
||||
return def;
|
||||
} else if (!o.getClass().equals(retType)) {
|
||||
throw new SimulationListenerException("Custom script function " + method + " returned type " +
|
||||
o.getClass().getSimpleName() + ", expected " + retType.getSimpleName());
|
||||
} else {
|
||||
return (T) o;
|
||||
}
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
missing.add(method);
|
||||
// fall-through
|
||||
} catch (ScriptException e) {
|
||||
logger.warn("Script exception in " + method + ": " + e, e);
|
||||
throw new SimulationException("Script failed: " + e.getMessage());
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
package net.sf.openrocket.simulation.extension.impl;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.prefs.BackingStoreException;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
import net.sf.openrocket.startup.Preferences;
|
||||
import net.sf.openrocket.util.ArrayList;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
/**
|
||||
* Utility class used by the scripting extension and its configurator.
|
||||
*/
|
||||
public class ScriptingUtil {
|
||||
|
||||
static final String NODE_ID = ScriptingExtension.class.getCanonicalName();
|
||||
|
||||
private static final List<String> DEFAULT_TRUSTED_HASHES = Arrays.asList(
|
||||
// Roll control script in roll control example file:
|
||||
"SHA-256:9bf364ce4d4a75f09b29178bf9d6872b232084f73dae20dc7b5b073e54e95a42"
|
||||
);
|
||||
|
||||
/** The name to be chosen from a list of alternatives. If not found, will use the default name. */
|
||||
private static final List<String> PREFERRED_LANGUAGE_NAMES = Arrays.asList("JavaScript");
|
||||
|
||||
@Inject
|
||||
Preferences prefs;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the preferred internal language name based on a script language name.
|
||||
*
|
||||
* @return the preferred language name, or null if the language is not supported.
|
||||
*/
|
||||
public String getLanguage(String language) {
|
||||
if (language == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ScriptEngineManager manager = new ScriptEngineManager();
|
||||
ScriptEngine engine = manager.getEngineByName(language);
|
||||
if (engine == null) {
|
||||
return null;
|
||||
}
|
||||
return getLanguage(engine.getFactory());
|
||||
}
|
||||
|
||||
|
||||
public List<String> getLanguages() {
|
||||
List<String> langs = new ArrayList<String>();
|
||||
ScriptEngineManager manager = new ScriptEngineManager();
|
||||
for (ScriptEngineFactory factory : manager.getEngineFactories()) {
|
||||
langs.add(getLanguage(factory));
|
||||
}
|
||||
return langs;
|
||||
}
|
||||
|
||||
|
||||
private String getLanguage(ScriptEngineFactory factory) {
|
||||
for (String name : factory.getNames()) {
|
||||
if (PREFERRED_LANGUAGE_NAMES.contains(name)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
return factory.getLanguageName();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Test whether the user has indicated this script to be trusted,
|
||||
* or if it is an internally trusted script.
|
||||
*/
|
||||
public boolean isTrustedScript(String language, String script) {
|
||||
if (language == null || script == null) {
|
||||
return false;
|
||||
}
|
||||
script = normalize(script);
|
||||
if (script.length() == 0) {
|
||||
return true;
|
||||
}
|
||||
String hash = hash(language, script);
|
||||
if (DEFAULT_TRUSTED_HASHES.contains(hash)) {
|
||||
return true;
|
||||
}
|
||||
return prefs.getNode(NODE_ID).getBoolean(hash, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a script as trusted.
|
||||
*/
|
||||
public void setTrustedScript(String language, String script, boolean trusted) {
|
||||
script = normalize(script);
|
||||
String hash = hash(language, script);
|
||||
if (trusted) {
|
||||
prefs.getNode(NODE_ID).putBoolean(hash, true);
|
||||
} else {
|
||||
prefs.getNode(NODE_ID).remove(hash);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all trusted scripts.
|
||||
*/
|
||||
public void clearTrustedScripts() {
|
||||
try {
|
||||
prefs.getNode(NODE_ID).clear();
|
||||
} catch (BackingStoreException e) {
|
||||
throw new BugException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static String normalize(String script) {
|
||||
return script.replaceAll("\r", "").trim();
|
||||
}
|
||||
|
||||
static String hash(String language, String script) {
|
||||
/*
|
||||
* NOTE: Hash length must be max 80 chars, the max length of a key in a Properties object.
|
||||
*/
|
||||
|
||||
String output;
|
||||
MessageDigest digest;
|
||||
|
||||
try {
|
||||
digest = MessageDigest.getInstance("SHA-256");
|
||||
digest.update(language.getBytes("UTF-8"));
|
||||
digest.update((byte) '|');
|
||||
byte[] hash = digest.digest(script.getBytes("UTF-8"));
|
||||
BigInteger bigInt = new BigInteger(1, hash);
|
||||
output = bigInt.toString(16);
|
||||
while (output.length() < 64) {
|
||||
output = "0" + output;
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new BugException("JRE does not support SHA-256 hash algorithm", e);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new BugException(e);
|
||||
}
|
||||
|
||||
return digest.getAlgorithm() + ":" + output;
|
||||
}
|
||||
|
||||
}
|
@ -8,11 +8,11 @@ import net.sf.openrocket.motor.MotorInstance;
|
||||
import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
|
||||
import net.sf.openrocket.simulation.AccelerationData;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.simulation.MassData;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
|
||||
|
||||
@ -24,20 +24,10 @@ import net.sf.openrocket.util.Coordinate;
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public class AbstractSimulationListener implements SimulationListener, SimulationComputationListener,
|
||||
SimulationEventListener {
|
||||
SimulationEventListener, Cloneable {
|
||||
|
||||
//// SimulationListener ////
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getMenuPosition() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSimulation(SimulationStatus status) throws SimulationException {
|
||||
// No-op
|
||||
@ -68,14 +58,6 @@ public class AbstractSimulationListener implements SimulationListener, Simulatio
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of any flight data types this listener creates.
|
||||
*/
|
||||
@Override
|
||||
public FlightDataType[] getFlightDataTypes() {
|
||||
return new FlightDataType[] {};
|
||||
}
|
||||
|
||||
|
||||
//// SimulationEventListener ////
|
||||
|
||||
@ -184,8 +166,12 @@ public class AbstractSimulationListener implements SimulationListener, Simulatio
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractSimulationListener clone() throws CloneNotSupportedException {
|
||||
return (AbstractSimulationListener) super.clone();
|
||||
public AbstractSimulationListener clone() {
|
||||
try {
|
||||
return (AbstractSimulationListener) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new BugException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import net.sf.openrocket.aerodynamics.AerodynamicForces;
|
||||
import net.sf.openrocket.aerodynamics.FlightConditions;
|
||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
import net.sf.openrocket.simulation.AccelerationData;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.MassData;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
@ -17,20 +16,20 @@ import net.sf.openrocket.util.Coordinate;
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public interface SimulationComputationListener extends SimulationListener {
|
||||
|
||||
|
||||
|
||||
|
||||
//////// Computation/modeling related callbacks ////////
|
||||
|
||||
public AccelerationData preAccelerationCalculation(SimulationStatus status) throws SimulationException;
|
||||
|
||||
public AccelerationData postAccelerationCalculation(SimulationStatus status, AccelerationData acceleration)
|
||||
throws SimulationException;
|
||||
throws SimulationException;
|
||||
|
||||
public AtmosphericConditions preAtmosphericModel(SimulationStatus status)
|
||||
throws SimulationException;
|
||||
throws SimulationException;
|
||||
|
||||
public AtmosphericConditions postAtmosphericModel(SimulationStatus status, AtmosphericConditions atmosphericConditions)
|
||||
throws SimulationException;
|
||||
throws SimulationException;
|
||||
|
||||
|
||||
public Coordinate preWindModel(SimulationStatus status) throws SimulationException;
|
||||
@ -42,29 +41,27 @@ public interface SimulationComputationListener extends SimulationListener {
|
||||
|
||||
public double postGravityModel(SimulationStatus status, double gravity) throws SimulationException;
|
||||
|
||||
|
||||
|
||||
public FlightConditions preFlightConditions(SimulationStatus status)
|
||||
throws SimulationException;
|
||||
throws SimulationException;
|
||||
|
||||
public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions)
|
||||
throws SimulationException;
|
||||
throws SimulationException;
|
||||
|
||||
|
||||
public AerodynamicForces preAerodynamicCalculation(SimulationStatus status)
|
||||
throws SimulationException;
|
||||
throws SimulationException;
|
||||
|
||||
public AerodynamicForces postAerodynamicCalculation(SimulationStatus status, AerodynamicForces forces)
|
||||
throws SimulationException;
|
||||
throws SimulationException;
|
||||
|
||||
public MassData preMassCalculation(SimulationStatus status) throws SimulationException;
|
||||
|
||||
public MassData postMassCalculation(SimulationStatus status, MassData massData) throws SimulationException;
|
||||
|
||||
|
||||
|
||||
|
||||
public double preSimpleThrustCalculation(SimulationStatus status) throws SimulationException;
|
||||
|
||||
public double postSimpleThrustCalculation(SimulationStatus status, double thrust) throws SimulationException;
|
||||
|
||||
@Override
|
||||
public FlightDataType[] getFlightDataTypes();
|
||||
|
||||
}
|
||||
|
@ -4,14 +4,13 @@ import net.sf.openrocket.motor.MotorId;
|
||||
import net.sf.openrocket.motor.MotorInstance;
|
||||
import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
|
||||
public interface SimulationEventListener {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Called before adding a flight event to the event queue.
|
||||
*
|
||||
@ -23,7 +22,7 @@ public interface SimulationEventListener {
|
||||
public boolean addFlightEvent(SimulationStatus status, FlightEvent event) throws SimulationException;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Called before handling a flight event.
|
||||
*
|
||||
@ -57,10 +56,6 @@ public interface SimulationEventListener {
|
||||
*/
|
||||
public boolean recoveryDeviceDeployment(SimulationStatus status, RecoveryDevice recoveryDevice)
|
||||
throws SimulationException;
|
||||
|
||||
|
||||
|
||||
public FlightDataType[] getFlightDataTypes();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package net.sf.openrocket.simulation.listeners;
|
||||
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
|
||||
@ -10,26 +9,7 @@ import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
* If the implementation maintains any state, it should be properly cloned.
|
||||
*
|
||||
*/
|
||||
public interface SimulationListener extends Cloneable {
|
||||
|
||||
/**
|
||||
* Get the name of this simulation listener. Ideally this should be localized, as
|
||||
* it can be displayed in the UI.
|
||||
*
|
||||
* @return the name of this simulation listener.
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
|
||||
/**
|
||||
* Get the menu position of this simulation listener. This should be an array
|
||||
* of localized submenu names in descending order, or an empty array for positioning
|
||||
* in the base menu.
|
||||
*
|
||||
* @return the menu position of this simulation listener.
|
||||
*/
|
||||
public String[] getMenuPosition();
|
||||
|
||||
public interface SimulationListener {
|
||||
|
||||
/**
|
||||
* Called when starting a simulation.
|
||||
@ -83,9 +63,7 @@ public interface SimulationListener extends Cloneable {
|
||||
|
||||
|
||||
/**
|
||||
* Return a list of any flight data types this listener creates.
|
||||
* Return a deep copy of this simulation listener including its state.
|
||||
*/
|
||||
public FlightDataType[] getFlightDataTypes();
|
||||
|
||||
public SimulationListener clone() throws CloneNotSupportedException;
|
||||
public SimulationListener clone();
|
||||
}
|
||||
|
@ -20,20 +20,7 @@ import net.sf.openrocket.util.Coordinate;
|
||||
public class DampingMoment extends AbstractSimulationListener {
|
||||
|
||||
private static final FlightDataType type = FlightDataType.getType("Damping moment coefficient", "Cdm", UnitGroup.UNITS_COEFFICIENT);
|
||||
private static final FlightDataType[] typeList = {type};
|
||||
|
||||
@Override
|
||||
public String getName(){
|
||||
return "Damping moment listener";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of any flight data types this listener creates.
|
||||
*/
|
||||
@Override
|
||||
public FlightDataType[] getFlightDataTypes(){
|
||||
return typeList;
|
||||
}
|
||||
private static final FlightDataType[] typeList = { type };
|
||||
|
||||
@Override
|
||||
public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) throws SimulationException {
|
||||
@ -42,11 +29,11 @@ public class DampingMoment extends AbstractSimulationListener {
|
||||
|
||||
//status.getFlightData().setValue(type, aerodynamicPart + propulsivePart);
|
||||
status.getFlightData().setValue(type, calculate(status, flightConditions));
|
||||
|
||||
|
||||
return flightConditions;
|
||||
}
|
||||
|
||||
private double calculate(SimulationStatus status, FlightConditions flightConditions){
|
||||
private double calculate(SimulationStatus status, FlightConditions flightConditions) {
|
||||
|
||||
// Work out the propulsive/jet damping part of the moment.
|
||||
|
||||
@ -55,15 +42,15 @@ public class DampingMoment extends AbstractSimulationListener {
|
||||
|
||||
List<Double> mpAll = data.get(FlightDataType.TYPE_PROPELLANT_MASS);
|
||||
List<Double> time = data.get(FlightDataType.TYPE_TIME);
|
||||
if (mpAll == null || time == null){
|
||||
if (mpAll == null || time == null) {
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
int len = mpAll.size();
|
||||
|
||||
// This isn't as accurate as I would like
|
||||
double mdot=Double.NaN;
|
||||
if (len > 2){
|
||||
double mdot = Double.NaN;
|
||||
if (len > 2) {
|
||||
// Using polynomial interpolator for derivative. Doesn't help much
|
||||
//double[] x = { time.get(len-5), time.get(len-4), time.get(len-3), time.get(len-2), time.get(len-1) };
|
||||
//double[] y = { mpAll.get(len-5), mpAll.get(len-4), mpAll.get(len-3), mpAll.get(len-2), mpAll.get(len-1) };
|
||||
@ -71,22 +58,22 @@ public class DampingMoment extends AbstractSimulationListener {
|
||||
//double[] coeff = interp.interpolator(y);
|
||||
//double dt = .01;
|
||||
//mdot = (interp.eval(x[4], coeff) - interp.eval(x[4]-dt, coeff))/dt;
|
||||
|
||||
mdot = (mpAll.get(len-1) - mpAll.get(len-2)) / (time.get(len-1) - time.get(len-2));
|
||||
|
||||
mdot = (mpAll.get(len - 1) - mpAll.get(len - 2)) / (time.get(len - 1) - time.get(len - 2));
|
||||
}
|
||||
|
||||
double cg = data.getLast(FlightDataType.TYPE_CG_LOCATION);
|
||||
|
||||
// find the maximum distance from nose to nozzle.
|
||||
double nozzleDistance = 0;
|
||||
for (MotorId id: status.getMotorConfiguration().getMotorIDs()){
|
||||
for (MotorId id : status.getMotorConfiguration().getMotorIDs()) {
|
||||
MotorInstanceConfiguration config = status.getMotorConfiguration();
|
||||
Coordinate position = config.getMotorPosition(id);
|
||||
|
||||
double x = position.x + config.getMotorInstance(id).getParentMotor().getLength();
|
||||
if (x > nozzleDistance){
|
||||
if (x > nozzleDistance) {
|
||||
nozzleDistance = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now can get the propulsive part
|
||||
@ -99,11 +86,12 @@ public class DampingMoment extends AbstractSimulationListener {
|
||||
|
||||
// Must go through each component ...
|
||||
Map<RocketComponent, AerodynamicForces> forces = aerocalc.getForceAnalysis(status.getConfiguration(), flightConditions, null);
|
||||
for (Map.Entry<RocketComponent, AerodynamicForces> entry : forces.entrySet()){
|
||||
for (Map.Entry<RocketComponent, AerodynamicForces> entry : forces.entrySet()) {
|
||||
|
||||
RocketComponent comp = entry.getKey();
|
||||
|
||||
if (!comp.isAerodynamic()) continue;
|
||||
if (!comp.isAerodynamic())
|
||||
continue;
|
||||
|
||||
//System.out.println(comp.toString());
|
||||
|
||||
@ -111,7 +99,7 @@ public class DampingMoment extends AbstractSimulationListener {
|
||||
double Cp = entry.getValue().getCP().length();
|
||||
double z = comp.getPositionValue(); //?
|
||||
|
||||
aerodynamicPart += CNa*Math.pow(z-Cp, 2);
|
||||
aerodynamicPart += CNa * Math.pow(z - Cp, 2);
|
||||
}
|
||||
|
||||
double v = flightConditions.getVelocity();
|
||||
@ -121,7 +109,7 @@ public class DampingMoment extends AbstractSimulationListener {
|
||||
aerodynamicPart = aerodynamicPart * .5 * rho * v * ar;
|
||||
|
||||
return aerodynamicPart + propulsivePart;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import net.sf.openrocket.database.ComponentPresetDao;
|
||||
import net.sf.openrocket.database.motor.MotorDatabase;
|
||||
import net.sf.openrocket.database.motor.ThrustCurveMotorSetDatabase;
|
||||
import net.sf.openrocket.l10n.ClassBasedTranslator;
|
||||
import net.sf.openrocket.l10n.DebugTranslator;
|
||||
import net.sf.openrocket.l10n.ExceptionSuppressingTranslator;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
|
||||
@ -33,6 +34,10 @@ public final class Application {
|
||||
}
|
||||
|
||||
private static Translator getBaseTranslator() {
|
||||
if (injector == null) {
|
||||
// Occurs in some unit tests
|
||||
return new DebugTranslator(null);
|
||||
}
|
||||
return injector.getInstance(Translator.class);
|
||||
}
|
||||
|
||||
|
@ -124,6 +124,8 @@ public abstract class Preferences implements ChangeSource {
|
||||
|
||||
public abstract void putString(String directory, String key, String value);
|
||||
|
||||
public abstract java.util.prefs.Preferences getNode(String nodeName);
|
||||
|
||||
/*
|
||||
* ******************************************************************************************
|
||||
*/
|
||||
|
@ -278,6 +278,7 @@ public class UnitGroup {
|
||||
|
||||
UNITS_ROLL = new UnitGroup();
|
||||
UNITS_ROLL.addUnit(new GeneralUnit(1, "rad/s"));
|
||||
UNITS_ROLL.addUnit(new GeneralUnit(Math.PI / 180, DEGREE + "/s"));
|
||||
UNITS_ROLL.addUnit(new GeneralUnit(2 * Math.PI, "r/s"));
|
||||
UNITS_ROLL.addUnit(new GeneralUnit(2 * Math.PI / 60, "rpm"));
|
||||
UNITS_ROLL.setDefaultUnit(1);
|
||||
|
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"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -48,6 +48,7 @@ import net.sf.openrocket.rocketcomponent.TubeCoupler;
|
||||
import net.sf.openrocket.simulation.SimulationOptions;
|
||||
import net.sf.openrocket.simulation.customexpression.CustomExpression;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.extension.impl.ScriptingExtension;
|
||||
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
|
||||
import net.sf.openrocket.simulation.listeners.SimulationListener;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
@ -241,7 +242,7 @@ public class TestRockets {
|
||||
}
|
||||
|
||||
|
||||
public Rocket makeSmallFlyable() {
|
||||
public static Rocket makeSmallFlyable() {
|
||||
double noseconeLength = 0.10, noseconeRadius = 0.01;
|
||||
double bodytubeLength = 0.20, bodytubeRadius = 0.01, bodytubeThickness = 0.001;
|
||||
|
||||
@ -281,8 +282,12 @@ public class TestRockets {
|
||||
String id = rocket.newFlightConfigurationID();
|
||||
bodytube.setMotorMount(true);
|
||||
|
||||
Motor m = Application.getMotorSetDatabase().findMotors(null, null, "B4", Double.NaN, Double.NaN).get(0);
|
||||
bodytube.getMotorConfiguration().get(id).setMotor(m);
|
||||
MotorConfiguration motorConfig = new MotorConfiguration();
|
||||
ThrustCurveMotor motor = getTestMotor();
|
||||
motorConfig.setMotor(motor);
|
||||
motorConfig.setEjectionDelay(5);
|
||||
|
||||
bodytube.getMotorConfiguration().set(id, motorConfig);
|
||||
bodytube.setMotorOverhang(0.005);
|
||||
rocket.getDefaultConfiguration().setFlightConfigurationID(id);
|
||||
|
||||
@ -643,11 +648,7 @@ public class TestRockets {
|
||||
|
||||
// create motor config and add a motor to it
|
||||
MotorConfiguration motorConfig = new MotorConfiguration();
|
||||
ThrustCurveMotor motor = new ThrustCurveMotor(
|
||||
Manufacturer.getManufacturer("A"),
|
||||
"F12X", "Desc", Motor.Type.UNKNOWN, new double[] {},
|
||||
0.024, 0.07, new double[] { 0, 1, 2 }, new double[] { 0, 1, 0 },
|
||||
new Coordinate[] { Coordinate.NUL, Coordinate.NUL, Coordinate.NUL }, "digestA");
|
||||
ThrustCurveMotor motor = getTestMotor();
|
||||
motorConfig.setMotor(motor);
|
||||
motorConfig.setEjectionDelay(5);
|
||||
|
||||
@ -685,11 +686,7 @@ public class TestRockets {
|
||||
|
||||
// create motor config and add a motor to it
|
||||
MotorConfiguration motorConfig = new MotorConfiguration();
|
||||
ThrustCurveMotor motor = new ThrustCurveMotor(
|
||||
Manufacturer.getManufacturer("A"),
|
||||
"F12X", "Desc", Motor.Type.UNKNOWN, new double[] {},
|
||||
0.024, 0.07, new double[] { 0, 1, 2 }, new double[] { 0, 1, 0 },
|
||||
new Coordinate[] { Coordinate.NUL, Coordinate.NUL, Coordinate.NUL }, "digestA");
|
||||
ThrustCurveMotor motor = getTestMotor();
|
||||
motorConfig.setMotor(motor);
|
||||
motorConfig.setEjectionDelay(5);
|
||||
|
||||
@ -918,6 +915,20 @@ public class TestRockets {
|
||||
return OpenRocketDocumentFactory.createDocumentFromRocket(rocket);
|
||||
}
|
||||
|
||||
|
||||
public static OpenRocketDocument makeTestRocket_v107_withSimulationExtension(String script) {
|
||||
Rocket rocket = makeSmallFlyable();
|
||||
OpenRocketDocument document = OpenRocketDocumentFactory.createDocumentFromRocket(rocket);
|
||||
Simulation sim = new Simulation(rocket);
|
||||
ScriptingExtension ext = new ScriptingExtension();
|
||||
ext.setEnabled(true);
|
||||
ext.setLanguage("JavaScript");
|
||||
ext.setScript(script);
|
||||
sim.getSimulationExtensions().add(ext);
|
||||
document.addSimulation(sim);
|
||||
return document;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new test rocket for testing OpenRocketSaver.estimateFileSize()
|
||||
*/
|
||||
@ -991,4 +1002,15 @@ public class TestRockets {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static ThrustCurveMotor getTestMotor() {
|
||||
return new ThrustCurveMotor(
|
||||
Manufacturer.getManufacturer("A"),
|
||||
"F12X", "Desc", Motor.Type.UNKNOWN, new double[] {},
|
||||
0.024, 0.07, new double[] { 0, 1, 2 }, new double[] { 0, 1, 0 },
|
||||
new Coordinate[] { Coordinate.NUL, Coordinate.NUL, Coordinate.NUL }, "digestA");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.prefs.BackingStoreException;
|
||||
|
||||
import net.sf.openrocket.formatting.RocketDescriptor;
|
||||
import net.sf.openrocket.formatting.RocketDescriptorImpl;
|
||||
@ -62,6 +63,8 @@ public class ServicesForTesting extends AbstractModule {
|
||||
|
||||
public static class PreferencesForTesting extends Preferences {
|
||||
|
||||
private static java.util.prefs.Preferences root = null;
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String key, boolean defaultValue) {
|
||||
// TODO Auto-generated method stub
|
||||
@ -151,5 +154,28 @@ public class ServicesForTesting extends AbstractModule {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.util.prefs.Preferences getNode(String nodeName) {
|
||||
return getBaseNode().node(nodeName);
|
||||
}
|
||||
|
||||
private java.util.prefs.Preferences getBaseNode() {
|
||||
if (root == null) {
|
||||
final String name = "OpenRocket-unittest-" + System.currentTimeMillis();
|
||||
root = java.util.prefs.Preferences.userRoot().node(name);
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
root.removeNode();
|
||||
} catch (BackingStoreException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.plugin.PluginModule;
|
||||
import net.sf.openrocket.simulation.extension.impl.ScriptingExtension;
|
||||
import net.sf.openrocket.simulation.extension.impl.ScriptingUtil;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.TestRockets;
|
||||
|
||||
@ -47,6 +49,10 @@ public class OpenRocketSaverTest {
|
||||
private OpenRocketSaver saver = new OpenRocketSaver();
|
||||
private static final String TMP_DIR = "./tmp/";
|
||||
|
||||
public static final String SIMULATION_EXTENSION_SCRIPT = "// Test < &\n// >\n// <![CDATA[";
|
||||
|
||||
private static Injector injector;
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
Module applicationModule = new ServicesForTesting();
|
||||
@ -61,7 +67,7 @@ public class OpenRocketSaverTest {
|
||||
}
|
||||
};
|
||||
|
||||
Injector injector = Guice.createInjector(Modules.override(applicationModule).with(dbOverrides), pluginModule);
|
||||
injector = Guice.createInjector(Modules.override(applicationModule).with(dbOverrides), pluginModule);
|
||||
Application.setInjector(injector);
|
||||
|
||||
File tmpDir = new File("./tmp");
|
||||
@ -122,6 +128,7 @@ public class OpenRocketSaverTest {
|
||||
rocketDocs.add(TestRockets.makeTestRocket_v106_withMotorMountIgnitionConfig());
|
||||
rocketDocs.add(TestRockets.makeTestRocket_v106_withRecoveryDeviceDeploymentConfig());
|
||||
rocketDocs.add(TestRockets.makeTestRocket_v106_withStageSeparationConfig());
|
||||
rocketDocs.add(TestRockets.makeTestRocket_v107_withSimulationExtension(SIMULATION_EXTENSION_SCRIPT));
|
||||
rocketDocs.add(TestRockets.makeTestRocket_for_estimateFileSize());
|
||||
|
||||
StorageOptions options = new StorageOptions();
|
||||
@ -135,6 +142,35 @@ public class OpenRocketSaverTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUntrustedScriptDisabledOnLoad() {
|
||||
OpenRocketDocument rocketDoc = TestRockets.makeTestRocket_v107_withSimulationExtension(SIMULATION_EXTENSION_SCRIPT);
|
||||
StorageOptions options = new StorageOptions();
|
||||
File file = saveRocket(rocketDoc, options);
|
||||
OpenRocketDocument rocketDocLoaded = loadRocket(file.getPath());
|
||||
assertEquals(1, rocketDocLoaded.getSimulations().size());
|
||||
assertEquals(1, rocketDocLoaded.getSimulations().get(0).getSimulationExtensions().size());
|
||||
ScriptingExtension ext = (ScriptingExtension) rocketDocLoaded.getSimulations().get(0).getSimulationExtensions().get(0);
|
||||
assertEquals(false, ext.isEnabled());
|
||||
assertEquals(SIMULATION_EXTENSION_SCRIPT, ext.getScript());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTrustedScriptEnabledOnLoad() {
|
||||
OpenRocketDocument rocketDoc = TestRockets.makeTestRocket_v107_withSimulationExtension("TESTING");
|
||||
injector.getInstance(ScriptingUtil.class).setTrustedScript("JavaScript", "TESTING", true);
|
||||
StorageOptions options = new StorageOptions();
|
||||
File file = saveRocket(rocketDoc, options);
|
||||
OpenRocketDocument rocketDocLoaded = loadRocket(file.getPath());
|
||||
assertEquals(1, rocketDocLoaded.getSimulations().size());
|
||||
assertEquals(1, rocketDocLoaded.getSimulations().get(0).getSimulationExtensions().size());
|
||||
ScriptingExtension ext = (ScriptingExtension) rocketDocLoaded.getSimulations().get(0).getSimulationExtensions().get(0);
|
||||
assertEquals(true, ext.isEnabled());
|
||||
assertEquals("TESTING", ext.getScript());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Test how accurate estimatedFileSize is.
|
||||
*
|
||||
@ -258,6 +294,17 @@ public class OpenRocketSaverTest {
|
||||
assertEquals(106, getCalculatedFileVersion(rocketDoc));
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// Tests for File Version 1.7 //
|
||||
////////////////////////////////
|
||||
|
||||
@Test
|
||||
public void testFileVersion107_withSimulationExtension() {
|
||||
OpenRocketDocument rocketDoc = TestRockets.makeTestRocket_v107_withSimulationExtension(SIMULATION_EXTENSION_SCRIPT);
|
||||
assertEquals(107, getCalculatedFileVersion(rocketDoc));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Utility Functions
|
||||
*/
|
||||
@ -273,6 +320,7 @@ public class OpenRocketSaverTest {
|
||||
try {
|
||||
rocketDoc = loader.load();
|
||||
} catch (RocketLoadException e) {
|
||||
e.printStackTrace();
|
||||
fail("RocketLoadException while loading file " + fileName + " : " + e.getMessage());
|
||||
}
|
||||
return rocketDoc;
|
||||
|
@ -25,7 +25,7 @@ public class DocumentConfigTest extends BaseTestCase {
|
||||
public void testAllVersionsTested() {
|
||||
|
||||
// Update this after creating new unit tests in OpenRocketSaver for a new OR file version
|
||||
String[] testedVersionsStr = { "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6" };
|
||||
String[] testedVersionsStr = { "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7" };
|
||||
|
||||
List<String> supportedVersions = Arrays.asList(DocumentConfig.SUPPORTED_VERSIONS);
|
||||
List<String> testedVersions = Arrays.asList(testedVersionsStr);
|
||||
|
@ -0,0 +1,88 @@
|
||||
package net.sf.openrocket.simulation.extension.impl;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import net.sf.openrocket.startup.MockPreferences;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestScriptingUtil {
|
||||
|
||||
private static final String HASH_JavaScript_foobar = "SHA-256:8f06133e0235d239355b5ca8ca0b43dece803c29b2a563222519d982abd3fc43";
|
||||
|
||||
private ScriptingUtil util;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
util = new ScriptingUtil();
|
||||
util.prefs = new MockPreferences();
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: This class assumes that the JRE supports JavaScript scripting.
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testGetLanguage() {
|
||||
assertEquals(null, util.getLanguage(null));
|
||||
assertEquals(null, util.getLanguage(""));
|
||||
assertEquals(null, util.getLanguage("foobar"));
|
||||
assertEquals("JavaScript", util.getLanguage("JavaScript"));
|
||||
assertEquals("JavaScript", util.getLanguage("javascript"));
|
||||
assertEquals("JavaScript", util.getLanguage("ECMAScript"));
|
||||
assertEquals("JavaScript", util.getLanguage("js"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetLanguages() {
|
||||
assertTrue(util.getLanguages().size() >= 1);
|
||||
assertTrue(util.getLanguages().contains("JavaScript"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsTrustedScript() {
|
||||
util.setTrustedScript("JavaScript", "foobar", true);
|
||||
assertTrue(util.isTrustedScript("JavaScript", "foobar"));
|
||||
assertTrue(util.isTrustedScript("JavaScript", " \n foobar \n\t\r"));
|
||||
assertFalse(util.isTrustedScript("JavaScript", "foo\nbar"));
|
||||
assertFalse(util.isTrustedScript("Javascript", "foobar"));
|
||||
|
||||
// Empty script is always considered trusted
|
||||
assertFalse(util.isTrustedScript("foo", null));
|
||||
assertTrue(util.isTrustedScript("foo", ""));
|
||||
assertTrue(util.isTrustedScript("foo", " \n\r\t "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetTrustedScript() {
|
||||
util.setTrustedScript("JavaScript", " \n foobar \n\r ", true);
|
||||
assertTrue(util.prefs.getNode(ScriptingUtil.NODE_ID).getBoolean(HASH_JavaScript_foobar, false));
|
||||
util.setTrustedScript("JavaScript", " foobar ", false);
|
||||
assertTrue(util.prefs.getNode(ScriptingUtil.NODE_ID).getBoolean(HASH_JavaScript_foobar, true));
|
||||
assertFalse(util.prefs.getNode(ScriptingUtil.NODE_ID).getBoolean(HASH_JavaScript_foobar, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearTrustedScripts() {
|
||||
util.setTrustedScript("JavaScript", "foobar", true);
|
||||
assertTrue(util.isTrustedScript("JavaScript", "foobar"));
|
||||
util.clearTrustedScripts();
|
||||
assertFalse(util.isTrustedScript("JavaScript", "foobar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalize() {
|
||||
assertEquals("foo", ScriptingUtil.normalize("foo"));
|
||||
assertEquals("foo bar", ScriptingUtil.normalize(" \n\r\t foo \r bar \n\t\r "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHash() {
|
||||
assertEquals("SHA-256:12e6a78889b96a16d305b8e4af81119545f89eccba5fb37cc3a1ec2c53eab514", ScriptingUtil.hash("JS", ""));
|
||||
assertEquals("SHA-256:000753e5deb2d8fa80e602ca03bcdb8e12a6b14b2b4a4d0abecdc976ad26e3ef", ScriptingUtil.hash("foo", "1165"));
|
||||
assertEquals(HASH_JavaScript_foobar, ScriptingUtil.hash("JavaScript", "foobar"));
|
||||
}
|
||||
}
|
108
core/test/net/sf/openrocket/startup/MockPreferences.java
Normal file
108
core/test/net/sf/openrocket/startup/MockPreferences.java
Normal file
@ -0,0 +1,108 @@
|
||||
package net.sf.openrocket.startup;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.prefs.BackingStoreException;
|
||||
|
||||
import net.sf.openrocket.material.Material;
|
||||
import net.sf.openrocket.preset.ComponentPreset;
|
||||
import net.sf.openrocket.preset.ComponentPreset.Type;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
|
||||
public class MockPreferences extends Preferences {
|
||||
|
||||
private final String NODENAME = "OpenRocket-test-mock";
|
||||
private final java.util.prefs.Preferences NODE;
|
||||
|
||||
public MockPreferences() {
|
||||
java.util.prefs.Preferences root = java.util.prefs.Preferences.userRoot();
|
||||
try {
|
||||
if (root.nodeExists(NODENAME)) {
|
||||
root.node(NODENAME).removeNode();
|
||||
}
|
||||
} catch (BackingStoreException e) {
|
||||
throw new BugException("Unable to clear preference node", e);
|
||||
}
|
||||
NODE = root.node(NODENAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String key, boolean def) {
|
||||
return NODE.getBoolean(key, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putBoolean(String key, boolean value) {
|
||||
NODE.putBoolean(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String key, int def) {
|
||||
return NODE.getInt(key, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putInt(String key, int value) {
|
||||
NODE.putInt(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(String key, double def) {
|
||||
return NODE.getDouble(key, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putDouble(String key, double value) {
|
||||
NODE.putDouble(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String key, String def) {
|
||||
return NODE.get(key, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putString(String key, String value) {
|
||||
NODE.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String directory, String key, String def) {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putString(String directory, String key, String value) {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.util.prefs.Preferences getNode(String nodeName) {
|
||||
return NODE.node(nodeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUserMaterial(Material m) {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Material> getUserMaterials() {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserMaterial(Material m) {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComponentFavorite(ComponentPreset preset, Type type, boolean favorite) {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getComponentFavorites(Type type) {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
}
|
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,25 +1,26 @@
|
||||
<?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="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"/>
|
||||
<classpathentry kind="lib" path="lib/jfreechart-1.0.15.jar"/>
|
||||
<classpathentry kind="lib" path="lib/OrangeExtensions-1.2.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jogl/gluegen-rt.jar"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/OpenRocket Core"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/slf4j-api-1.7.5.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/aopalliance.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/guice-3.0.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/guice-multibindings-3.0.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/javax.inject.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/opencsv-2.3.jar"/>
|
||||
<classpathentry kind="lib" path="lib/logback-classic-1.0.12.jar"/>
|
||||
<classpathentry kind="lib" path="lib/logback-core-1.0.12.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/resources"/>
|
||||
<classpathentry kind="lib" path="resources"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/annotation-detector-3.0.2.jar"/>
|
||||
<classpathentry kind="lib" path="lib/miglayout-4.0-swing.jar" sourcepath="reference/miglayout-4.0-sources.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
<?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="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"/>
|
||||
<classpathentry kind="lib" path="lib/jfreechart-1.0.15.jar"/>
|
||||
<classpathentry kind="lib" path="lib/OrangeExtensions-1.2.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jogl/gluegen-rt.jar"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/OpenRocket Core"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/slf4j-api-1.7.5.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/aopalliance.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/guice-3.0.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/guice-multibindings-3.0.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/javax.inject.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/opencsv-2.3.jar"/>
|
||||
<classpathentry kind="lib" path="lib/logback-classic-1.0.12.jar"/>
|
||||
<classpathentry kind="lib" path="lib/logback-core-1.0.12.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/resources"/>
|
||||
<classpathentry kind="lib" path="resources"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/annotation-detector-3.0.2.jar"/>
|
||||
<classpathentry kind="lib" path="lib/miglayout-4.0-swing.jar" sourcepath="reference/miglayout-4.0-sources.jar"/>
|
||||
<classpathentry kind="lib" path="lib/rsyntaxtextarea-2.5.6.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
@ -105,6 +105,7 @@
|
||||
<zipfileset src="${core.dir}/lib/slf4j-api-1.7.5.jar"/>
|
||||
<zipfileset src="${lib.dir}/logback-classic-1.0.12.jar"/>
|
||||
<zipfileset src="${lib.dir}/logback-core-1.0.12.jar"/>
|
||||
<zipfileset src="${lib.dir}/rsyntaxtextarea-2.5.6.jar"/>
|
||||
|
||||
|
||||
<!-- JOGL libraries need to be jar-in-jar -->
|
||||
|
BIN
swing/lib/rsyntaxtextarea-2.5.6.jar
Normal file
BIN
swing/lib/rsyntaxtextarea-2.5.6.jar
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -167,7 +170,7 @@ public class SimulationEditDialog extends JDialog {
|
||||
//// Launch conditions
|
||||
tabbedPane.addTab(trans.get("simedtdlg.tab.Launchcond"), new SimulationConditionsPanel(simulation[0]));
|
||||
//// Simulation options
|
||||
tabbedPane.addTab(trans.get("simedtdlg.tab.Simopt"), new SimulationOptionsPanel(simulation[0]));
|
||||
tabbedPane.addTab(trans.get("simedtdlg.tab.Simopt"), new SimulationOptionsPanel(document, simulation[0]));
|
||||
|
||||
tabbedPane.setSelectedIndex(0);
|
||||
|
||||
|
@ -1,54 +1,70 @@
|
||||
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;
|
||||
import net.sf.openrocket.document.OpenRocketDocument;
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.gui.SpinnerEditor;
|
||||
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.startup.Preferences;
|
||||
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();
|
||||
|
||||
|
||||
private OpenRocketDocument document;
|
||||
final Simulation simulation;
|
||||
|
||||
SimulationOptionsPanel(final Simulation simulation) {
|
||||
|
||||
private JPanel currentExtensions;
|
||||
|
||||
SimulationOptionsPanel(OpenRocketDocument document, final Simulation simulation) {
|
||||
super(new MigLayout("fill"));
|
||||
this.document = document;
|
||||
this.simulation = simulation;
|
||||
|
||||
|
||||
final SimulationOptions conditions = simulation.getOptions();
|
||||
|
||||
|
||||
JPanel sub, subsub;
|
||||
String tip;
|
||||
JLabel label;
|
||||
@ -56,7 +72,7 @@ class SimulationOptionsPanel extends JPanel {
|
||||
JSpinner spin;
|
||||
UnitSelector unit;
|
||||
BasicSlider slider;
|
||||
|
||||
|
||||
// // Simulation options
|
||||
sub = new JPanel(new MigLayout("fill, gap rel unrel",
|
||||
"[grow][65lp!][30lp!][75lp!]", ""));
|
||||
@ -64,38 +80,38 @@ class SimulationOptionsPanel extends JPanel {
|
||||
sub.setBorder(BorderFactory.createTitledBorder(trans
|
||||
.get("simedtdlg.border.Simopt")));
|
||||
this.add(sub, "growx, growy, aligny 0");
|
||||
|
||||
|
||||
// Separate panel for computation methods, as they use a different
|
||||
// layout
|
||||
subsub = new JPanel(new MigLayout("insets 0, fill", "[grow][min!][min!][]"));
|
||||
|
||||
|
||||
// // Calculation method:
|
||||
tip = trans.get("simedtdlg.lbl.ttip.Calcmethod");
|
||||
label = new JLabel(trans.get("simedtdlg.lbl.Calcmethod"));
|
||||
label.setToolTipText(tip);
|
||||
subsub.add(label, "gapright para");
|
||||
|
||||
|
||||
// // Extended Barrowman
|
||||
label = new JLabel(trans.get("simedtdlg.lbl.ExtBarrowman"));
|
||||
label.setToolTipText(tip);
|
||||
subsub.add(label, "growx, span 3, wrap");
|
||||
|
||||
|
||||
// Simulation method
|
||||
tip = trans.get("simedtdlg.lbl.ttip.Simmethod1")
|
||||
+ trans.get("simedtdlg.lbl.ttip.Simmethod2");
|
||||
label = new JLabel(trans.get("simedtdlg.lbl.Simmethod"));
|
||||
label.setToolTipText(tip);
|
||||
subsub.add(label, "gapright para");
|
||||
|
||||
|
||||
label = new JLabel("6-DOF Runge-Kutta 4");
|
||||
label.setToolTipText(tip);
|
||||
subsub.add(label, "growx, span 3, wrap");
|
||||
|
||||
|
||||
// // Geodetic calculation method:
|
||||
label = new JLabel(trans.get("simedtdlg.lbl.GeodeticMethod"));
|
||||
label.setToolTipText(trans.get("simedtdlg.lbl.ttip.GeodeticMethodTip"));
|
||||
subsub.add(label, "gapright para");
|
||||
|
||||
|
||||
EnumModel<GeodeticComputationStrategy> gcsModel = new EnumModel<GeodeticComputationStrategy>(
|
||||
conditions, "GeodeticComputation");
|
||||
final JComboBox gcsCombo = new JComboBox(gcsModel);
|
||||
@ -110,7 +126,7 @@ class SimulationOptionsPanel extends JPanel {
|
||||
gcsCombo.addActionListener(gcsTTipListener);
|
||||
gcsTTipListener.actionPerformed(null);
|
||||
subsub.add(gcsCombo, "span 3, wrap para");
|
||||
|
||||
|
||||
|
||||
// // Time step:
|
||||
label = new JLabel(trans.get("simedtdlg.lbl.Timestep"));
|
||||
@ -121,25 +137,25 @@ class SimulationOptionsPanel extends JPanel {
|
||||
.toStringUnit(RK4SimulationStepper.RECOMMENDED_TIME_STEP)
|
||||
+ ".";
|
||||
label.setToolTipText(tip);
|
||||
subsub.add(label,"gapright para");
|
||||
|
||||
subsub.add(label, "gapright para");
|
||||
|
||||
m = new DoubleModel(conditions, "TimeStep", UnitGroup.UNITS_TIME_STEP,
|
||||
0, 1);
|
||||
|
||||
|
||||
spin = new JSpinner(m.getSpinnerModel());
|
||||
spin.setEditor(new SpinnerEditor(spin));
|
||||
spin.setToolTipText(tip);
|
||||
subsub.add(spin, "");
|
||||
|
||||
|
||||
unit = new UnitSelector(m);
|
||||
unit.setToolTipText(tip);
|
||||
subsub.add(unit, "");
|
||||
slider = new BasicSlider(m.getSliderModel(0, 0.2));
|
||||
slider.setToolTipText(tip);
|
||||
subsub.add(slider, "w 100");
|
||||
|
||||
|
||||
sub.add(subsub, "spanx, wrap para");
|
||||
|
||||
|
||||
// Reset to default button
|
||||
JButton button = new JButton(trans.get("simedtdlg.but.resettodefault"));
|
||||
// Reset the time step to its default value (
|
||||
@ -159,141 +175,227 @@ class SimulationOptionsPanel extends JPanel {
|
||||
GeodeticComputationStrategy.SPHERICAL));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
sub.add(button, "align left");
|
||||
|
||||
// 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");
|
||||
|
||||
// // 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");
|
||||
|
||||
// // Add button
|
||||
button = new JButton(trans.get("simedtdlg.but.add"));
|
||||
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();
|
||||
desc.setText(trans.get("simedtdlg.SimExt.desc"));
|
||||
sub.add(desc, "aligny 0, hmin 100lp, growx, wrap para");
|
||||
|
||||
|
||||
final JButton addExtension = new JButton(trans.get("simedtdlg.SimExt.add"));
|
||||
final JPopupMenu menu = getExtensionMenu();
|
||||
addExtension.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
menu.show(addExtension, 5, addExtension.getBounds().height);
|
||||
}
|
||||
});
|
||||
sub.add(button, "split 2, sizegroup buttons, alignx 50%, gapright para");
|
||||
|
||||
// // Remove button
|
||||
button = new JButton(trans.get("simedtdlg.but.remove"));
|
||||
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]);
|
||||
sub.add(addExtension, "growx, wrap 0");
|
||||
|
||||
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();
|
||||
SwingSimulationExtensionConfigurator configurator = findConfigurator(e);
|
||||
if (configurator != null) {
|
||||
configurator.configure(e, simulation, SwingUtilities.windowForComponent(SimulationOptionsPanel.this));
|
||||
}
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
}
|
||||
listenerModel.fireContentsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
JMenu copyMenu = null;
|
||||
for (Simulation sim : document.getSimulations()) {
|
||||
if (!sim.getSimulationExtensions().isEmpty()) {
|
||||
JMenu menu = new JMenu(sim.getName());
|
||||
for (final SimulationExtension ext : sim.getSimulationExtensions()) {
|
||||
JMenuItem item = new JMenuItem(ext.getName());
|
||||
item.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
SimulationExtension e = ext.clone();
|
||||
simulation.getSimulationExtensions().add(e);
|
||||
updateCurrentExtensions();
|
||||
SwingSimulationExtensionConfigurator configurator = findConfigurator(e);
|
||||
if (configurator != null) {
|
||||
configurator.configure(e, simulation, SwingUtilities.windowForComponent(SimulationOptionsPanel.this));
|
||||
}
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
}
|
||||
|
||||
if (copyMenu == null) {
|
||||
copyMenu = new JMenu(trans.get("simedtdlg.SimExt.copyExtension"));
|
||||
}
|
||||
copyMenu.add(menu);
|
||||
}
|
||||
}
|
||||
if (copyMenu != null) {
|
||||
basemenu.add(copyMenu);
|
||||
}
|
||||
|
||||
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) {
|
||||
findConfigurator(extension).configure(extension, simulation,
|
||||
SwingUtilities.windowForComponent(SimulationOptionsPanel.this));
|
||||
updateCurrentExtensions();
|
||||
}
|
||||
});
|
||||
this.add(button, "right");
|
||||
}
|
||||
|
||||
if (extension.getDescription() != null) {
|
||||
button = new JButton(Icons.HELP);
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
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);
|
||||
}
|
||||
});
|
||||
panel.add(close, "right");
|
||||
dialog.add(panel);
|
||||
GUIUtil.setDisposableDialogOptions(dialog, close);
|
||||
dialog.setLocationRelativeTo(SwingUtilities.windowForComponent(SimulationOptionsPanel.this));
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
});
|
||||
this.add(button, "right");
|
||||
}
|
||||
|
||||
button = new JButton(Icons.DELETE);
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
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");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private SwingSimulationExtensionConfigurator findConfigurator(SimulationExtension extension) {
|
||||
Set<SwingSimulationExtensionConfigurator> configurators = Application.getInjector().getInstance(new Key<Set<SwingSimulationExtensionConfigurator>>() {
|
||||
});
|
||||
sub.add(button, "sizegroup buttons, alignx 50%");
|
||||
|
||||
}
|
||||
|
||||
private class ListenerCellRenderer extends JLabel implements
|
||||
ListCellRenderer {
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList list, Object value,
|
||||
int index, boolean isSelected, boolean cellHasFocus) {
|
||||
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;
|
||||
for (SwingSimulationExtensionConfigurator c : configurators) {
|
||||
if (c.support(extension)) {
|
||||
return c;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
@ -78,6 +78,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");
|
||||
|
||||
|
@ -45,8 +45,8 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
||||
for (String lang : new String[] { "en", "de", "es", "fr", "it", "ru", "cs", "pl", "ja", "pt", "tr" }) {
|
||||
list.add(new Locale(lang));
|
||||
}
|
||||
list.add(new Locale("zh","CN"));
|
||||
list.add(new Locale("uk","UA"));
|
||||
list.add(new Locale("zh", "CN"));
|
||||
list.add(new Locale("uk", "UA"));
|
||||
SUPPORTED_LOCALES = Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
@ -200,6 +200,7 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
||||
* @param nodeName the node name
|
||||
* @return the preferences object for that node
|
||||
*/
|
||||
@Override
|
||||
public Preferences getNode(String nodeName) {
|
||||
return PREFNODE.node(nodeName);
|
||||
}
|
||||
@ -418,11 +419,11 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
||||
public boolean computeFlightInBackground() {
|
||||
return PREFNODE.getBoolean("backgroundFlight", true);
|
||||
}
|
||||
|
||||
|
||||
public void setComputeFlightInBackground(boolean b) {
|
||||
PREFNODE.putBoolean("backgroundFlight", b);
|
||||
}
|
||||
|
||||
|
||||
public Simulation getBackgroundSimulation(Rocket rocket) {
|
||||
Simulation s = new Simulation(rocket);
|
||||
SimulationOptions cond = s.getOptions();
|
||||
|
@ -0,0 +1,88 @@
|
||||
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;
|
||||
|
||||
private JDialog dialog;
|
||||
|
||||
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) {
|
||||
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);
|
||||
close();
|
||||
GUIUtil.setNullModels(dialog);
|
||||
dialog = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a title for the dialog window. By default uses the extension's name.
|
||||
*/
|
||||
protected String getTitle(SimulationExtension extension, Simulation simulation) {
|
||||
return extension.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dialog currently open.
|
||||
*/
|
||||
protected JDialog getDialog() {
|
||||
return dialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the default dialog is closed. By default does nothing.
|
||||
*/
|
||||
protected void close() {
|
||||
|
||||
}
|
||||
|
||||
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,58 @@
|
||||
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");
|
||||
|
||||
|
||||
panel.add(new JLabel("Launch velocity:"));
|
||||
|
||||
m = new DoubleModel(extension, "LaunchVelocity", UnitGroup.UNITS_VELOCITY, 0);
|
||||
|
||||
spin = new JSpinner(m.getSpinnerModel());
|
||||
spin.setEditor(new SpinnerEditor(spin));
|
||||
panel.add(spin, "w 65lp!");
|
||||
|
||||
unit = new UnitSelector(m);
|
||||
panel.add(unit, "w 25");
|
||||
|
||||
slider = new BasicSlider(m.getSliderModel(0, 150));
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
package net.sf.openrocket.simulation.extension.impl;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.gui.adaptors.BooleanModel;
|
||||
import net.sf.openrocket.gui.components.StyledLabel;
|
||||
import net.sf.openrocket.gui.components.StyledLabel.Style;
|
||||
import net.sf.openrocket.plugin.Plugin;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
|
||||
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
|
||||
import org.fife.ui.rtextarea.RTextScrollPane;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
@Plugin
|
||||
public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfigurator<ScriptingExtension> {
|
||||
|
||||
@Inject
|
||||
private ScriptingUtil util;
|
||||
|
||||
private JComboBox languageSelector;
|
||||
private RSyntaxTextArea text;
|
||||
private JCheckBox trusted;
|
||||
|
||||
private ScriptingExtension extension;
|
||||
private Simulation simulation;
|
||||
|
||||
public ScriptingConfigurator() {
|
||||
super(ScriptingExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent getConfigurationComponent(final ScriptingExtension extension, Simulation simulation, JPanel panel) {
|
||||
this.extension = extension;
|
||||
this.simulation = simulation;
|
||||
|
||||
panel.add(new StyledLabel(trans.get("SimulationExtension.scripting.language.label"), Style.BOLD), "spanx, split");
|
||||
|
||||
String[] languages = util.getLanguages().toArray(new String[0]);
|
||||
languageSelector = new JComboBox(languages);
|
||||
languageSelector.setEditable(false);
|
||||
languageSelector.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
setLanguage((String) languageSelector.getSelectedItem());
|
||||
}
|
||||
});
|
||||
panel.add(languageSelector, "wrap para");
|
||||
|
||||
|
||||
text = new RSyntaxTextArea(extension.getScript(), 20, 80);
|
||||
text.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT);
|
||||
text.setCodeFoldingEnabled(true);
|
||||
text.setLineWrap(true);
|
||||
text.setWrapStyleWord(true);
|
||||
text.setEditable(true);
|
||||
text.setCurrentLineHighlightColor(new Color(255, 255, 230));
|
||||
text.addFocusListener(new FocusListener() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusLost(FocusEvent event) {
|
||||
String str = text.getText();
|
||||
if (!extension.getScript().equals(str)) {
|
||||
extension.setScript(str);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
RTextScrollPane scroll = new RTextScrollPane(text);
|
||||
panel.add(scroll, "spanx, grow, wrap para");
|
||||
|
||||
|
||||
BooleanModel enabled = new BooleanModel(extension, "Enabled");
|
||||
JCheckBox check = new JCheckBox(enabled);
|
||||
check.setText(trans.get("SimulationExtension.scripting.text.enabled"));
|
||||
check.setToolTipText(trans.get("SimulationExtension.scripting.text.enabled.ttip"));
|
||||
panel.add(check, "spanx, wrap rel");
|
||||
|
||||
trusted = new JCheckBox(trans.get("SimulationExtension.scripting.text.trusted"));
|
||||
trusted.setSelected(util.isTrustedScript(extension.getLanguage(), extension.getScript()));
|
||||
panel.add(trusted, "spanx, split");
|
||||
|
||||
panel.add(new JPanel(), "growx");
|
||||
|
||||
JButton button = new JButton(trans.get("SimulationExtension.scripting.text.trusted.clear"));
|
||||
button.setToolTipText(trans.get("SimulationExtension.scripting.text.trusted.clear.ttip"));
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
util.clearTrustedScripts();
|
||||
JOptionPane.showMessageDialog(getDialog(), trans.get("SimulationExtension.scripting.text.trusted.cleared"),
|
||||
trans.get("SimulationExtension.scripting.text.trusted.cleared.title"), JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
});
|
||||
panel.add(button, "wrap rel");
|
||||
|
||||
|
||||
StyledLabel label = new StyledLabel(trans.get("SimulationExtension.scripting.text.trusted.msg"), -1, Style.ITALIC);
|
||||
panel.add(label);
|
||||
|
||||
setLanguage(util.getLanguage(extension.getLanguage()));
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void close() {
|
||||
util.setTrustedScript(extension.getLanguage(), extension.getScript(), trusted.isSelected());
|
||||
}
|
||||
|
||||
|
||||
private void setLanguage(String language) {
|
||||
if (language == null) {
|
||||
language = "";
|
||||
}
|
||||
if (!language.equals(languageSelector.getSelectedItem())) {
|
||||
languageSelector.setSelectedItem(language);
|
||||
}
|
||||
extension.setLanguage(language);
|
||||
text.setSyntaxEditingStyle(findSyntaxLanguage(language));
|
||||
getDialog().setTitle(getTitle(extension, simulation));
|
||||
}
|
||||
|
||||
private String findSyntaxLanguage(String language) {
|
||||
ScriptEngineManager manager = new ScriptEngineManager();
|
||||
ScriptEngine engine = manager.getEngineByName(language);
|
||||
|
||||
if (engine != null) {
|
||||
Set<String> supported = TokenMakerFactory.getDefaultInstance().keySet();
|
||||
for (String type : engine.getFactory().getMimeTypes()) {
|
||||
if (supported.contains(type)) {
|
||||
return type;
|
||||
}
|
||||
for (String match : supported) {
|
||||
if (match.contains("/" + language.toLowerCase())) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SyntaxConstants.SYNTAX_STYLE_NONE;
|
||||
}
|
||||
|
||||
}
|
38
swing/src/net/sf/openrocket/utils/Scripting.java
Normal file
38
swing/src/net/sf/openrocket/utils/Scripting.java
Normal file
@ -0,0 +1,38 @@
|
||||
package net.sf.openrocket.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.script.ScriptEngineFactory;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
|
||||
|
||||
public class Scripting {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Scripting APIs:");
|
||||
|
||||
ScriptEngineManager manager = new ScriptEngineManager();
|
||||
for (ScriptEngineFactory factory : manager.getEngineFactories()) {
|
||||
System.out.println(" engineName=" + factory.getEngineName() +
|
||||
" engineVersion=" + factory.getEngineVersion() +
|
||||
" languageName=" + factory.getLanguageName() +
|
||||
" languageVersion=" + factory.getLanguageVersion() +
|
||||
" names=" + factory.getNames() +
|
||||
" mimeTypes=" + factory.getMimeTypes() +
|
||||
" extensions=" + factory.getExtensions());
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
System.out.println("RSyntaxTextArea supported syntax languages:");
|
||||
TokenMakerFactory f = TokenMakerFactory.getDefaultInstance();
|
||||
List<String> list = new ArrayList<String>(f.keySet());
|
||||
Collections.sort(list);
|
||||
for (String type : list) {
|
||||
System.out.println(" " + type);
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user