//----------------------------------------------------------------------------// // // // P l u g i n // // // //----------------------------------------------------------------------------// // <editor-fold defaultstate="collapsed" desc="hdr"> // // Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. // // This software is released under the GNU General Public License. // // Goto http://kenai.com/projects/audiveris to report bugs or suggestions. // //----------------------------------------------------------------------------// // </editor-fold> package omr.plugin; import omr.score.Score; import omr.step.Stepping; import omr.step.Steps; import omr.util.BasicTask; import omr.util.FileUtil; import org.jdesktop.application.Task; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.util.List; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import omr.WellKnowns; /** * Class {@code Plugin} describes a plugin instance, encapsulating the * relationship with the underlying javascript file. * * <p>A plugin is meant to describe the connection between Audiveris and an * external program, which will consume the MusicXML file exported by * Audiveris.</p> * * <p>A plugin is a javascript file, meant to export: * <dl> * <dt>pluginTitle</dt> * <dd>(string) The title to appear in Plugins pull-down menu</dd> * <dt>pluginTip</dt> * <dd>(string) A description text to appear as a user tip in Plugins menu</dd> * <dt>pluginCli</dt> * <dd>(function) A javascript function which returns the precise list of * arguments used when calling the external program. Note that the actual call * is not made by the javascript code, but by Audiveris itself for an easier * handling of input and output streams.</dd> * </dl> * * @author Hervé Bitteur */ public class Plugin { //~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger(Plugin.class); //~ Instance fields -------------------------------------------------------- // /** Related javascript file. */ private final File file; /** Related engine. */ private ScriptEngine engine; /** Plugin title. */ private String title; /** Description used for tool tip. */ private String tip; //~ Constructors ----------------------------------------------------------- //--------// // Plugin // //--------// /** * Creates a new Plugin object. * * @param file related javascript file */ public Plugin (File file) throws JavascriptUnavailableException { this.file = file; evaluateScript(); logger.debug("Created {}", this); } //~ Methods ---------------------------------------------------------------- //----------------// // getDescription // //----------------// /** * Report a descriptive sentence for this plugin. * * @return a sentence meant for tool tip */ public String getDescription () { if (tip != null) { return tip; } else { // Default value return getId(); } } //-------// // getId // //-------// /** * Report a unique ID for this plugin. * * @return plugin unique ID */ public String getId () { return FileUtil.getNameSansExtension(file); } //---------// // getTask // //---------// /** * Report the asynchronous plugin task on provided score. * * @param score the score to process through this plugin */ public Task<Void, Void> getTask (Score score) { return new PluginTask(score); } //----------// // getTitle // //----------// /** * Report a title meant for user interface. * * @return a title for this plugin */ public String getTitle () { if (title != null) { return title; } else { return getId(); } } //-----------// // runPlugin // //-----------// public Void runPlugin (Score score) { // Make sure we have the export file Stepping.ensureScoreStep(Steps.valueOf(Steps.EXPORT), score); final File exportFile = score.getExportFile(); if (exportFile == null) { logger.warn("Could not get export file"); return null; } // Retrieve proper sequence of command items List<String> args; try { logger.debug("{} doInBackground on {}", Plugin.this, exportFile); Invocable inv = (Invocable) engine; Object obj = inv.invokeFunction( "pluginCli", exportFile.getAbsolutePath()); if (obj instanceof List) { args = (List<String>) obj; // Unchecked by compiler logger.debug("{} command args: {}", this, args); } else { return null; } } catch (ScriptException | NoSuchMethodException ex) { logger.warn(this + " error invoking javascript", ex); return null; } // Spawn the command logger.info("Launching {} on {}", getTitle(), score.getRadix()); ProcessBuilder pb = new ProcessBuilder(args); pb = pb.redirectErrorStream(true); try { Process process = pb.start(); InputStream is = process.getInputStream(); InputStreamReader isr = new InputStreamReader(is, WellKnowns.FILE_ENCODING); BufferedReader br = new BufferedReader(isr); // Consume process output String line; while ((line = br.readLine()) != null) { logger.debug(line); } // Wait to get exit value try { int exitValue = process.waitFor(); if (exitValue != 0) { logger.warn("{} exited with value {}", Plugin.this, exitValue); } else { logger.debug("{} exit value is {}", Plugin.this, exitValue); } } catch (InterruptedException ex) { ex.printStackTrace(); } } catch (IOException ex) { logger.warn(Plugin.this + " error launching editor", ex); } return null; } //----------// // toString // //----------// @Override public String toString () { StringBuilder sb = new StringBuilder("{"); sb.append(getClass().getSimpleName()); sb.append(" ").append(getId()); sb.append("}"); return sb.toString(); } //----------------// // evaluateScript // //----------------// /** * Evaluate the plugin script to get precise information built. */ private void evaluateScript () throws JavascriptUnavailableException { ScriptEngineManager mgr = new ScriptEngineManager(); engine = mgr.getEngineByName("JavaScript"); if (engine != null) { try { InputStream is = new FileInputStream(file); Reader reader = new InputStreamReader(is, WellKnowns.FILE_ENCODING); engine.eval(reader); // Retrieve information from script title = (String) engine.get("pluginTitle"); tip = (String) engine.get("pluginTip"); } catch (FileNotFoundException | UnsupportedEncodingException | ScriptException ex) { logger.warn(this + " error", ex); } } else { throw new JavascriptUnavailableException(); } } //~ Inner Classes ---------------------------------------------------------- //------------// // PluginTask // //------------// /** * Handles the processing defined by the underlying javascript. * The lifecycle of this instance is limited to the duration of the task. */ private class PluginTask extends BasicTask { //~ Instance fields ---------------------------------------------------- private final Score score; //~ Constructors ------------------------------------------------------- public PluginTask (Score score) { this.score = score; } //~ Methods ------------------------------------------------------------ @Override @SuppressWarnings("unchecked") protected Void doInBackground () throws InterruptedException { return Plugin.this.runPlugin(score); } } }