001package jmri.jmrit.jython;
002
003import java.awt.event.ActionEvent;
004import java.io.File;
005import javax.script.ScriptException;
006import javax.swing.Icon;
007import javax.swing.JFileChooser;
008import jmri.script.JmriScriptEngineManager;
009import jmri.script.swing.ScriptFileChooser;
010import jmri.util.FileUtil;
011import jmri.util.swing.JmriAbstractAction;
012import jmri.util.swing.WindowInterface;
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015
016/**
017 * This Action runs a script using an available script engine.
018 * <p>
019 * The script engine to use is determined by the script's extension.
020 * <p>
021 * There are two constructors. One, without a script file name, will open a
022 * FileDialog to prompt for the file to use. The other, with a File object, will
023 * directly invoke that file.
024 *
025 * @author Bob Jacobsen Copyright (C) 2004, 2007
026 */
027public class RunJythonScript extends JmriAbstractAction {
028
029    public RunJythonScript(String s, WindowInterface wi) {
030        super(s, wi);
031    }
032
033    public RunJythonScript(String s, Icon i, WindowInterface wi) {
034        super(s, i, wi);
035    }
036
037    /**
038     * Constructor that, when action is invoked, opens a JFileChooser to select
039     * file to invoke.
040     *
041     * @param name Action name
042     */
043    public RunJythonScript(String name) {
044        super(name);
045        configuredFile = null;
046    }
047
048    /**
049     * Constructor that, when action is invoked, directly invokes the provided
050     * File.
051     *
052     * @param name Action name
053     * @param file the script file to invoke
054     */
055    public RunJythonScript(String name, File file) {
056        super(name);
057        this.configuredFile = file;
058    }
059
060    File configuredFile;
061
062    /**
063     * We always use the same file chooser in this class, so that the user's
064     * last-accessed directory remains available.
065     */
066    static JFileChooser fci = null;
067
068    private static synchronized void setFileChooser(JFileChooser chooser) {
069        fci = chooser;
070    }
071
072    /**
073     * Invoking this action via an event triggers display of a file dialog. If a
074     * file is selected, it's then invoked as a script.
075     *
076     */
077    @Override
078    public void actionPerformed(ActionEvent e) {
079        File thisFile;
080        if (configuredFile != null) {
081            thisFile = configuredFile;
082        } else {
083            thisFile = selectFile();
084        }
085
086        // and invoke that file
087        if (thisFile != null) {
088            invoke(thisFile);
089        } else {
090            log.info("No file selected");
091        }
092    }
093
094    File selectFile() {
095        if (fci == null) {
096            setFileChooser( new ScriptFileChooser(FileUtil.getScriptsPath()) );
097            RunJythonScript.fci.setDialogTitle(Bundle.getMessage("FindDesiredScriptFile"));
098        } else {
099            // when reusing the chooser, make sure new files are included
100            fci.rescanCurrentDirectory();
101        }
102
103        int retVal = fci.showOpenDialog(null);
104        // handle selection or cancel
105        if (retVal == JFileChooser.APPROVE_OPTION) {
106            File file = fci.getSelectedFile();
107            // Run the script from its filename
108            return file;
109        }
110        return null;
111    }
112
113    void invoke(File file) {
114        try {
115            JmriScriptEngineManager.getDefault().eval(file);
116        } catch (ScriptException | java.io.IOException ex) {
117            log.error("Unable to execute script.", ex);
118        }
119    }
120
121    // never invoked, because we overrode actionPerformed above
122    @Override
123    public jmri.util.swing.JmriPanel makePanel() {
124        throw new IllegalArgumentException("Should not be invoked");
125    }
126
127    // initialize logging
128    private final static Logger log = LoggerFactory.getLogger(RunJythonScript.class);
129
130}