//----------------------------------------------------------------------------// // // // S c r i p t // // // //----------------------------------------------------------------------------// // <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.script; import omr.score.Score; import omr.score.entity.Page; import omr.sheet.Sheet; import omr.step.ProcessingCancellationException; import omr.util.TreeNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElements; import javax.xml.bind.annotation.XmlList; import javax.xml.bind.annotation.XmlRootElement; /** * Class {@code Script} handles a complete script applied to a score. * * <p>A script is a sequence of {@link ScriptTask} instances tasks that are * recorded as the user interacts with the score data. * * <p>A script can be stored and reloaded/replayed. * * @author Hervé Bitteur */ @XmlAccessorType(XmlAccessType.NONE) @XmlRootElement(name = "script") public class Script { //~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger(Script.class); //~ Instance fields -------------------------------------------------------- /** Score to which the script is applied. */ private Score score; /** Full path to the Score image file. */ @XmlAttribute(name = "file") private final String scorePath; /** Collection of 1-based page ids explicitly included, if any. */ // To get all page numbers, space-separated, in a single element: @XmlList @XmlElement(name = "pages") private SortedSet<Integer> pages; // = new TreeSet<>(); /** Sequence of tasks that compose the script. */ @XmlElements({ @XmlElement(name = "assign", type = AssignTask.class), @XmlElement(name = "barline", type = BarlineTask.class), @XmlElement(name = "boundary", type = BoundaryTask.class), @XmlElement(name = "closescore", type = CloseScoreTask.class), @XmlElement(name = "delete", type = DeleteTask.class), @XmlElement(name = "export", type = ExportTask.class), @XmlElement(name = "exportc", type = ExportCTask.class), @XmlElement(name = "insert", type = InsertTask.class), @XmlElement(name = "parameters", type = ParametersTask.class), @XmlElement(name = "print", type = PrintTask.class), @XmlElement(name = "rational", type = RationalTask.class), @XmlElement(name = "remove", type = RemoveTask.class), @XmlElement(name = "segment", type = SegmentTask.class), @XmlElement(name = "slur", type = SlurTask.class), @XmlElement(name = "step", type = StepTask.class), @XmlElement(name = "text", type = TextTask.class) }) private final List<ScriptTask> tasks = new ArrayList<>(); /** Flag a script that needs to be stored. */ private boolean modified; //~ Constructors ----------------------------------------------------------- //--------// // Script // //--------// /** * Create a script. * * @param score the related score */ public Script (Score score) { this.score = score; scorePath = score.getImagePath(); // Store page ids pages = new TreeSet<Integer>(); for (TreeNode pn : score.getPages()) { Page page = (Page) pn; pages.add(page.getIndex()); } } //--------// // Script // //--------// /** No-arg constructor for JAXB */ private Script () { scorePath = null; } //~ Methods ---------------------------------------------------------------- //---------// // addTask // //---------// /** * Add a task to the script. * * @param task the task to add at the end of the current sequence */ public synchronized void addTask (ScriptTask task) { tasks.add(task); setModified(true); logger.debug("Script: added {}", task); } //------// // dump // //------// /** * Meant for debug. */ public void dump () { logger.info(toString()); if (pages != null && !pages.isEmpty()) { logger.info("Included pages: {}", pages); } for (ScriptTask task : tasks) { logger.info(task.toString()); } } //----------// // getScore // //----------// /** * Report the score this script is linked to. * * @return the score concerned */ public Score getScore () { return score; } //------------// // isModified // //------------// /** * Has the script been modified (wrt its backup on disk)? * * @return the modified */ public boolean isModified () { return modified; } //-----// // run // //-----// /** * This methods runs sequentially and synchronously the various * tasks of the script. * It is up to the caller to run this method in a separate thread if so * desired. */ public void run () { logger.debug("Running {}{}", this, (score != null) ? (" on score " + score.getRadix()) : ""); // Make score concrete (with its pages/sheets) if (score == null) { if (scorePath == null) { logger.warn("No score defined in script"); return; } score = new Score(new File(scorePath)); score.createPages(pages); } // Run the tasks in sequence try { for (ScriptTask task : tasks) { Page page; if (task instanceof SheetTask) { Integer pageIndex = ((SheetTask) task).getPageIndex(); page = score.getPage(pageIndex); if (page == null) { logger.warn("Script error. No page for index {}", pageIndex); continue; } } else { page = score.getFirstPage(); } Sheet sheet = page.getSheet(); logger.debug("Running {} on {}", task, sheet); try { // Run the task synchronously (prolog/core/epilog) task.run(sheet); } catch (ProcessingCancellationException pce) { throw pce; } catch (Exception ex) { logger.warn("Error running " + task, ex); throw new RuntimeException(task.toString()); } } logger.debug("All tasks run on {}", score); } catch (ProcessingCancellationException pce) { throw pce; } catch (Exception ex) { logger.warn("Script aborted", ex); } finally { // Flag the (active) script as up-to-date score.getScript().setModified(false); } } //----------// // toString // //----------// @Override public String toString () { StringBuilder sb = new StringBuilder(); sb.append("{Script"); if (modified) { sb.append(" modified"); } if (scorePath != null) { sb.append(" ").append(scorePath); } else if (score != null) { sb.append(" ").append(score.getRadix()); } if (pages != null && !pages.isEmpty()) { sb.append(" pages:").append(pages); } if (tasks != null) { sb.append(" tasks:").append(tasks.size()); } sb.append("}"); return sb.toString(); } //-------------// // setModified // //-------------// /** * Flag the script as modified (wrt disk) * * @param modified the modified to set */ void setModified (boolean modified) { this.modified = modified; } }