Add support for multiple scripting languages

This commit is contained in:
Sampo Niskanen 2014-12-27 23:50:54 +02:00
parent eb5321e82c
commit a39a3fce15
7 changed files with 210 additions and 11 deletions

View File

@ -407,7 +407,7 @@ SimulationExtension.javacode.className = Fully-qualified Java class name:
SimulationExtension.scripting.name = {language} script
SimulationExtension.scripting.desc = Extend OpenRocket simulations by custom scripts.
SimulationExtension.scripting.script.label = JavaScript code:
SimulationExtension.scripting.language.label = Language:
SimulationEditDialog.btn.plot = Plot
SimulationEditDialog.btn.export = Export

View File

@ -13,10 +13,10 @@ import net.sf.openrocket.simulation.listeners.SimulationListener;
public class ScriptingExtension extends AbstractSimulationExtension {
private static final String JS = "JavaScript";
private static final String DEFAULT_LANGUAGE = "JavaScript";
public ScriptingExtension() {
setLanguage(JS);
setLanguage(DEFAULT_LANGUAGE);
}
@Override
@ -46,8 +46,7 @@ public class ScriptingExtension extends AbstractSimulationExtension {
}
public String getLanguage() {
// TODO: Support other languages
return JS;
return config.getString("language", DEFAULT_LANGUAGE);
}
public void setLanguage(String language) {
@ -57,7 +56,10 @@ public class ScriptingExtension extends AbstractSimulationExtension {
SimulationListener getListener() throws SimulationException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
ScriptEngine engine = manager.getEngineByName(getLanguage());
if (engine == null) {
throw new SimulationException("Your JRE does not support the scripting language '" + getLanguage() + "'");
}
try {
engine.eval(getScript());

View File

@ -0,0 +1,53 @@
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

@ -0,0 +1,32 @@
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

@ -24,6 +24,8 @@ public abstract class AbstractSwingSimulationExtensionConfigurator<E extends Sim
private final Class<E> extensionClass;
private JDialog dialog;
protected AbstractSwingSimulationExtensionConfigurator(Class<E> extensionClass) {
this.extensionClass = extensionClass;
}
@ -37,7 +39,7 @@ public abstract class AbstractSwingSimulationExtensionConfigurator<E extends Sim
@SuppressWarnings("unchecked")
@Override
public void configure(SimulationExtension extension, Simulation simulation, Window parent) {
final JDialog dialog = new JDialog(parent, getTitle(extension, simulation), ModalityType.APPLICATION_MODAL);
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"));
@ -55,6 +57,8 @@ public abstract class AbstractSwingSimulationExtensionConfigurator<E extends Sim
dialog.add(panel);
GUIUtil.setDisposableDialogOptions(dialog, close);
dialog.setVisible(true);
GUIUtil.setNullModels(dialog);
dialog = null;
}
/**
@ -64,6 +68,13 @@ public abstract class AbstractSwingSimulationExtensionConfigurator<E extends Sim
return extension.getName();
}
/**
* Return the dialog currently open.
*/
protected JDialog getDialog() {
return dialog;
}
protected abstract JComponent getConfigurationComponent(E extension, Simulation simulation, JPanel panel);
}

View File

@ -1,9 +1,15 @@
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.JComboBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
@ -12,24 +18,46 @@ 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;
@Plugin
public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfigurator<ScriptingExtension> {
protected ScriptingConfigurator() {
private JComboBox languageSelector;
private RSyntaxTextArea text;
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.script.label"), Style.BOLD), "wrap");
panel.add(new StyledLabel(trans.get("SimulationExtension.scripting.language.label"), Style.BOLD), "");
final RSyntaxTextArea text = new RSyntaxTextArea(extension.getScript(), 15, 60);
String[] languages = ScriptingUtil.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(), 15, 60);
text.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT);
text.setCodeFoldingEnabled(true);
text.setLineWrap(true);
@ -51,9 +79,44 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi
});
RTextScrollPane scroll = new RTextScrollPane(text);
panel.add(scroll, "grow");
panel.add(scroll, "spanx, grow");
setLanguage(ScriptingUtil.getLanguage(extension.getLanguage()));
return panel;
}
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;
}
}

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