Disable untrusted scripts on loading files

This commit is contained in:
Sampo Niskanen 2015-01-06 19:25:13 +02:00
parent a39a3fce15
commit a1f6782195
16 changed files with 554 additions and 206 deletions

View File

@ -386,6 +386,7 @@ simedtdlg.but.ttip.resettodefault = Reset the time step to its default value (
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.lbl.Noflightdata = No flight data available.
simedtdlg.lbl.runsimfirst = Please run the simulation first.
simedtdlg.chart.Simflight = Simulated flight
@ -408,6 +409,15 @@ 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.
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

View File

@ -3,6 +3,9 @@ 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;
@ -42,7 +45,9 @@ public abstract class AbstractSimulationExtension extends AbstractChangeSource i
}
/**
* By default, returns the canonical name of this class.
* {@inheritDoc}
* <p>
* By default, this method returns the canonical name of this class.
*/
@Override
public String getId() {
@ -50,7 +55,9 @@ public abstract class AbstractSimulationExtension extends AbstractChangeSource i
}
/**
* By default, returns the name provided to the constructor.
* {@inheritDoc}
* <p>
* By default, this method returns the name provided to the constructor.
*/
@Override
public String getName() {
@ -58,7 +65,9 @@ public abstract class AbstractSimulationExtension extends AbstractChangeSource i
}
/**
* By default, returns null.
* {@inheritDoc}
* <p>
* By default, this method returns null.
*/
@Override
public String getDescription() {
@ -66,7 +75,9 @@ public abstract class AbstractSimulationExtension extends AbstractChangeSource i
}
/**
* By default, returns an empty list.
* {@inheritDoc}
* <p>
* By default, this method returns an empty list.
*/
@Override
public List<FlightDataType> getFlightDataTypes() {
@ -74,7 +85,18 @@ public abstract class AbstractSimulationExtension extends AbstractChangeSource i
}
/**
* By default, returns a new object obtained by calling Object.clone().
* {@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() {

View File

@ -2,6 +2,9 @@ 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;
@ -35,6 +38,16 @@ public interface SimulationExtension {
*/
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

View File

@ -5,18 +5,30 @@ 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
@ -31,9 +43,25 @@ public class ScriptingExtension extends AbstractSimulationExtension {
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 {
conditions.getSimulationListenerList().add(getListener());
if (isEnabled()) {
conditions.getSimulationListenerList().add(getListener());
}
}
@ -53,6 +81,14 @@ public class ScriptingExtension extends AbstractSimulationExtension {
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();

View File

@ -18,6 +18,7 @@ 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;
@ -68,25 +69,25 @@ public class ScriptingSimulationListener implements SimulationListener, Simulati
@Override
public void startSimulation(SimulationStatus status) throws SimulationException {
invoke(null, "startSimulation", status);
invoke(Void.class, null, "startSimulation", status);
}
@Override
public void endSimulation(SimulationStatus status, SimulationException exception) {
try {
invoke(null, "endSimulation", status, exception);
invoke(Void.class, null, "endSimulation", status, exception);
} catch (SimulationException e) {
}
}
@Override
public boolean preStep(SimulationStatus status) throws SimulationException {
return invoke(true, "preStep", status);
return invoke(Boolean.class, true, "preStep", status);
}
@Override
public void postStep(SimulationStatus status) throws SimulationException {
invoke(null, "postStep", status);
invoke(Void.class, null, "postStep", status);
}
@ -95,22 +96,22 @@ public class ScriptingSimulationListener implements SimulationListener, Simulati
@Override
public boolean addFlightEvent(SimulationStatus status, FlightEvent event) throws SimulationException {
return invoke(true, "addFlightEvent", status, event);
return invoke(Boolean.class, true, "addFlightEvent", status, event);
}
@Override
public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) throws SimulationException {
return invoke(true, "handleFlightEvent", status, event);
return invoke(Boolean.class, true, "handleFlightEvent", status, event);
}
@Override
public boolean motorIgnition(SimulationStatus status, MotorId motorId, MotorMount mount, MotorInstance instance) throws SimulationException {
return invoke(true, "motorIgnition", status, motorId, mount, instance);
return invoke(Boolean.class, true, "motorIgnition", status, motorId, mount, instance);
}
@Override
public boolean recoveryDeviceDeployment(SimulationStatus status, RecoveryDevice recoveryDevice) throws SimulationException {
return invoke(true, "recoveryDeviceDeployment", status, recoveryDevice);
return invoke(Boolean.class, true, "recoveryDeviceDeployment", status, recoveryDevice);
}
@ -119,90 +120,99 @@ public class ScriptingSimulationListener implements SimulationListener, Simulati
@Override
public AccelerationData preAccelerationCalculation(SimulationStatus status) throws SimulationException {
return invoke(null, "preAccelerationCalculation", status);
return invoke(AccelerationData.class, null, "preAccelerationCalculation", status);
}
@Override
public AerodynamicForces preAerodynamicCalculation(SimulationStatus status) throws SimulationException {
return invoke(null, "preAerodynamicCalculation", status);
return invoke(AerodynamicForces.class, null, "preAerodynamicCalculation", status);
}
@Override
public AtmosphericConditions preAtmosphericModel(SimulationStatus status) throws SimulationException {
return invoke(null, "preAtmosphericModel", status);
return invoke(AtmosphericConditions.class, null, "preAtmosphericModel", status);
}
@Override
public FlightConditions preFlightConditions(SimulationStatus status) throws SimulationException {
return invoke(null, "preFlightConditions", status);
return invoke(FlightConditions.class, null, "preFlightConditions", status);
}
@Override
public double preGravityModel(SimulationStatus status) throws SimulationException {
return invoke(Double.NaN, "preGravityModel", status);
return invoke(Double.class, Double.NaN, "preGravityModel", status);
}
@Override
public MassData preMassCalculation(SimulationStatus status) throws SimulationException {
return invoke(null, "preMassCalculation", status);
return invoke(MassData.class, null, "preMassCalculation", status);
}
@Override
public double preSimpleThrustCalculation(SimulationStatus status) throws SimulationException {
return invoke(Double.NaN, "preSimpleThrustCalculation", status);
return invoke(Double.class, Double.NaN, "preSimpleThrustCalculation", status);
}
@Override
public Coordinate preWindModel(SimulationStatus status) throws SimulationException {
return invoke(null, "preWindModel", status);
return invoke(Coordinate.class, null, "preWindModel", status);
}
@Override
public AccelerationData postAccelerationCalculation(SimulationStatus status, AccelerationData acceleration) throws SimulationException {
return invoke(null, "postAccelerationCalculation", status, acceleration);
return invoke(AccelerationData.class, null, "postAccelerationCalculation", status, acceleration);
}
@Override
public AerodynamicForces postAerodynamicCalculation(SimulationStatus status, AerodynamicForces forces) throws SimulationException {
return invoke(null, "postAerodynamicCalculation", status, forces);
return invoke(AerodynamicForces.class, null, "postAerodynamicCalculation", status, forces);
}
@Override
public AtmosphericConditions postAtmosphericModel(SimulationStatus status, AtmosphericConditions atmosphericConditions) throws SimulationException {
return invoke(null, "postAtmosphericModel", status, atmosphericConditions);
return invoke(AtmosphericConditions.class, null, "postAtmosphericModel", status, atmosphericConditions);
}
@Override
public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) throws SimulationException {
return invoke(null, "postFlightConditions", status, flightConditions);
return invoke(FlightConditions.class, null, "postFlightConditions", status, flightConditions);
}
@Override
public double postGravityModel(SimulationStatus status, double gravity) throws SimulationException {
return invoke(Double.NaN, "postGravityModel", status, gravity);
return invoke(Double.class, Double.NaN, "postGravityModel", status, gravity);
}
@Override
public MassData postMassCalculation(SimulationStatus status, MassData massData) throws SimulationException {
return invoke(null, "postMassCalculation", status, massData);
return invoke(MassData.class, null, "postMassCalculation", status, massData);
}
@Override
public double postSimpleThrustCalculation(SimulationStatus status, double thrust) throws SimulationException {
return invoke(Double.NaN, "postSimpleThrustCalculation", status, thrust);
return invoke(Double.class, Double.NaN, "postSimpleThrustCalculation", status, thrust);
}
@Override
public Coordinate postWindModel(SimulationStatus status, Coordinate wind) throws SimulationException {
return invoke(null, "postWindModel", status, wind);
return invoke(Coordinate.class, null, "postWindModel", status, wind);
}
@SuppressWarnings("unchecked")
private <T> T invoke(T def, String method, Object... args) throws SimulationException {
private <T> T invoke(Class<T> retType, T def, String method, Object... args) throws SimulationException {
try {
if (!missing.contains(method)) {
return (T) invocable.invokeFunction(method, args);
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);

View File

@ -0,0 +1,150 @@
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();
/** 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);
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;
}
}

View File

@ -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"));
}
}

View File

@ -92,6 +92,8 @@ public abstract class Preferences {
public abstract void putString(String directory, String key, String value);
public abstract java.util.prefs.Preferences getNode(String nodeName);
/*
* ******************************************************************************************
*/

View File

@ -1,53 +0,0 @@
package net.sf.openrocket.util;
import java.util.Arrays;
import java.util.List;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
public class ScriptingUtil {
/** 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");
/**
* 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 static 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 static 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 static String getLanguage(ScriptEngineFactory factory) {
for (String name : factory.getNames()) {
if (PREFERRED_LANGUAGE_NAMES.contains(name)) {
return name;
}
}
return factory.getLanguageName();
}
}

View File

@ -151,5 +151,11 @@ public class ServicesForTesting extends AbstractModule {
return null;
}
@Override
public java.util.prefs.Preferences getNode(String nodeName) {
// TODO Auto-generated method stub
return null;
}
}
}

View 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");
}
}

View File

@ -1,32 +0,0 @@
package net.sf.openrocket.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class TestScriptingUtil {
/*
* Note: This class assumes that the JRE supports JavaScript scripting.
*/
@Test
public void testGetLanguage() {
assertEquals(null, ScriptingUtil.getLanguage(null));
assertEquals(null, ScriptingUtil.getLanguage(""));
assertEquals(null, ScriptingUtil.getLanguage("foobar"));
assertEquals("JavaScript", ScriptingUtil.getLanguage("JavaScript"));
assertEquals("JavaScript", ScriptingUtil.getLanguage("javascript"));
assertEquals("JavaScript", ScriptingUtil.getLanguage("ECMAScript"));
assertEquals("JavaScript", ScriptingUtil.getLanguage("js"));
}
@Test
public void testGetLanguages() {
assertTrue(ScriptingUtil.getLanguages().size() >= 1);
assertTrue(ScriptingUtil.getLanguages().contains("JavaScript"));
}
}

View File

@ -186,7 +186,7 @@ class SimulationOptionsPanel extends JPanel {
sub.add(desc, "aligny 0, hmin 100lp, growx, wrap para");
final JButton addExtension = new JButton("Add extension");
final JButton addExtension = new JButton(trans.get("simedtdlg.SimExt.add"));
final JPopupMenu menu = getExtensionMenu();
addExtension.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
@ -223,6 +223,10 @@ class SimulationOptionsPanel extends JPanel {
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);
@ -344,86 +348,17 @@ class SimulationOptionsPanel extends JPanel {
this.add(button, "right");
}
private SwingSimulationExtensionConfigurator findConfigurator(SimulationExtension extension) {
Set<SwingSimulationExtensionConfigurator> configurators = Application.getInjector().getInstance(new Key<Set<SwingSimulationExtensionConfigurator>>() {
});
for (SwingSimulationExtensionConfigurator c : configurators) {
if (c.support(extension)) {
return c;
}
}
return null;
}
}
//
//
// private class ExtensionListModel extends AbstractListModel {
// @Override
// public SimulationExtensionConfiguration getElementAt(int index) {
// if (index < 0 || index >= getSize())
// return null;
// return simulation.getSimulationExtensions().get(index);
// }
//
// @Override
// public int getSize() {
// return simulation.getSimulationExtensions().size();
// }
// }
//
//
// private class ExtensionCellRenderer extends JPanel implements ListCellRenderer {
// private JLabel label;
//
// public ExtensionCellRenderer() {
// super(new MigLayout("fill"));
// label = new JLabel();
//
// }
//
// @Override
// public Component getListCellRendererComponent(JList list, Object value,
// int index, boolean isSelected, boolean cellHasFocus) {
// SimulationExtensionConfiguration config = (SimulationExtensionConfiguration) value;
//
//
//
// String s = value.toString();
// setText(s);
//
// // Attempt instantiating, catch any exceptions
// Exception ex = null;
// try {
// Class<?> c = Class.forName(s);
// @SuppressWarnings("unused")
// SimulationListener l = (SimulationListener) c.newInstance();
// } catch (Exception e) {
// ex = e;
// }
//
// if (ex == null) {
// setIcon(Icons.SIMULATION_LISTENER_OK);
// //// Listener instantiated successfully.
// setToolTipText("Listener instantiated successfully.");
// } else {
// setIcon(Icons.SIMULATION_LISTENER_ERROR);
// //// <html>Unable to instantiate listener due to exception:<br>
// setToolTipText("<html>Unable to instantiate listener due to exception:<br>" +
// ex.toString());
// }
//
// if (isSelected) {
// setBackground(list.getSelectionBackground());
// setForeground(list.getSelectionForeground());
// } else {
// setBackground(list.getBackground());
// setForeground(list.getForeground());
// }
// setOpaque(true);
// return this;
// }
// }
private SwingSimulationExtensionConfigurator findConfigurator(SimulationExtension extension) {
Set<SwingSimulationExtensionConfigurator> configurators = Application.getInjector().getInstance(new Key<Set<SwingSimulationExtensionConfigurator>>() {
});
for (SwingSimulationExtensionConfigurator c : configurators) {
if (c.support(extension)) {
return c;
}
}
return null;
}
}

View File

@ -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();

View File

@ -57,6 +57,7 @@ public abstract class AbstractSwingSimulationExtensionConfigurator<E extends Sim
dialog.add(panel);
GUIUtil.setDisposableDialogOptions(dialog, close);
dialog.setVisible(true);
close();
GUIUtil.setNullModels(dialog);
dialog = null;
}
@ -75,6 +76,13 @@ public abstract class AbstractSwingSimulationExtensionConfigurator<E extends Sim
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);
}

View File

@ -9,27 +9,36 @@ 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 net.sf.openrocket.util.ScriptingUtil;
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;
@ -43,9 +52,9 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi
this.extension = extension;
this.simulation = simulation;
panel.add(new StyledLabel(trans.get("SimulationExtension.scripting.language.label"), Style.BOLD), "");
panel.add(new StyledLabel(trans.get("SimulationExtension.scripting.language.label"), Style.BOLD), "spanx, split");
String[] languages = ScriptingUtil.getLanguages().toArray(new String[0]);
String[] languages = util.getLanguages().toArray(new String[0]);
languageSelector = new JComboBox(languages);
languageSelector.setEditable(false);
languageSelector.addActionListener(new ActionListener() {
@ -79,13 +88,48 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi
});
RTextScrollPane scroll = new RTextScrollPane(text);
panel.add(scroll, "spanx, grow");
panel.add(scroll, "spanx, grow, wrap para");
setLanguage(ScriptingUtil.getLanguage(extension.getLanguage()));
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 = "";