source: src/main/java/weka/gui/scripting/Script.java @ 4

Last change on this file since 4 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 16.5 KB
RevLine 
[4]1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 * Script.java
19 * Copyright (C) 2009 University of Waikato, Hamilton, New Zealand
20 */
21
22package weka.gui.scripting;
23
24import weka.core.Option;
25import weka.core.OptionHandler;
26import weka.core.SerializedObject;
27import weka.core.Utils;
28import weka.core.WekaException;
29import weka.gui.ExtensionFileFilter;
30import weka.gui.scripting.event.ScriptExecutionEvent;
31import weka.gui.scripting.event.ScriptExecutionListener;
32import weka.gui.scripting.event.ScriptExecutionEvent.Type;
33
34import java.io.File;
35import java.io.Serializable;
36import java.util.Enumeration;
37import java.util.HashSet;
38import java.util.Iterator;
39import java.util.Vector;
40
41import javax.swing.event.DocumentEvent;
42import javax.swing.event.DocumentListener;
43import javax.swing.text.Document;
44
45/**
46 * A simple helper class for loading, saving scripts.
47 *
48 * @author  fracpete (fracpete at waikato dot ac dot nz)
49 * @version $Revision: 5142 $
50 */
51public abstract class Script
52  implements OptionHandler, Serializable {
53
54  /** for serialization. */
55  private static final long serialVersionUID = 5053328052680586401L;
56
57  /**
58   * The Thread for running a script.
59   *
60   * @author  fracpete (fracpete at waikato dot ac dot nz)
61   * @version $Revision: 5142 $
62   */
63  public abstract static class ScriptThread
64    extends Thread {
65   
66    /** the owning script. */
67    protected Script m_Owner;
68   
69    /** commandline arguments. */
70    protected String[] m_Args;
71   
72    /** whether the thread was stopped. */
73    protected boolean m_Stopped;
74   
75    /**
76     * Initializes the thread.
77     *
78     * @param owner     the owning script
79     * @param args      the commandline arguments
80     */
81    public ScriptThread(Script owner, String[] args) {
82      super();
83     
84      m_Owner = owner;
85      m_Args  = args.clone();
86    }
87
88    /**
89     * Returns the owner.
90     *
91     * @return          the owning script
92     */
93    public Script getOwner() {
94      return m_Owner;
95    }
96   
97    /**
98     * Returns the commandline args.
99     *
100     * @return          the arguments
101     */
102    public String[] getArgs() {
103      return m_Args;
104    }
105   
106    /**
107     * Performs the actual run.
108     */
109    protected abstract void doRun();
110   
111    /**
112     * Executes the script.
113     */
114    public void run() {
115      m_Stopped = false;
116     
117      getOwner().notifyScriptFinishedListeners(new ScriptExecutionEvent(m_Owner, Type.STARTED));
118      try {
119        doRun();
120        if (!m_Stopped)
121          getOwner().notifyScriptFinishedListeners(new ScriptExecutionEvent(m_Owner, Type.FINISHED));
122      }
123      catch (Exception e) {
124        e.printStackTrace();
125        getOwner().notifyScriptFinishedListeners(new ScriptExecutionEvent(m_Owner, Type.ERROR, e));
126      }
127      getOwner().m_ScriptThread = null;
128    }
129   
130    /**
131     * Stops the script execution.
132     */
133    public void stopScript() {
134      if (isAlive()) {
135        m_Stopped = true;
136        try {
137          stop();
138        }
139        catch (Exception e) {
140          // ignored
141        }
142      }
143    }
144  }
145
146  /** the backup extension. */
147  public final static String BACKUP_EXTENSION = ".bak";
148 
149  /** the document this script is a wrapper around. */
150  protected Document m_Document;
151 
152  /** the filename of the script. */
153  protected File m_Filename;
154 
155  /** the newline used on this platform. */
156  protected String m_NewLine;
157 
158  /** whether the script is modified. */
159  protected boolean m_Modified;
160 
161  /** the current script thread. */
162  protected transient ScriptThread m_ScriptThread;
163 
164  /** optional listeners when the script finishes. */
165  protected HashSet<ScriptExecutionListener> m_FinishedListeners;
166 
167  /**
168   * Initializes the script.
169   */
170  public Script() {
171    this(null);
172  }
173 
174  /**
175   * Initializes the script.
176   *
177   * @param doc         the document to use as basis
178   */
179  public Script(Document doc) {
180    this(doc, null);
181  }
182 
183  /**
184   * Initializes the script. Automatically loads the specified file, if not
185   * null.
186   *
187   * @param doc         the document to use as basis
188   * @param file        the file to load (if not null)
189   */
190  public Script(Document doc, File file) {
191    initialize();
192   
193    m_Document = doc;
194   
195    if (m_Document != null) {
196      m_Document.addDocumentListener(new DocumentListener() {
197        public void changedUpdate(DocumentEvent e) {
198          m_Modified = true;
199        }
200        public void insertUpdate(DocumentEvent e) {
201          m_Modified = true;
202        }
203        public void removeUpdate(DocumentEvent e) {
204          m_Modified = true;
205        }
206      });
207    }
208   
209    if (file != null)
210      open(file);
211  }
212 
213  /**
214   * Initializes the script.
215   */
216  protected void initialize() {
217    m_Filename          = null;
218    m_NewLine           = System.getProperty("line.separator");
219    m_Modified          = false;
220    m_ScriptThread      = null;
221    m_FinishedListeners = new HashSet<ScriptExecutionListener>();
222  }
223
224  /**
225   * Returns an enumeration describing the available options.
226   *
227   * @return an enumeration of all the available options
228   */
229  public Enumeration listOptions() {
230    return new Vector().elements();
231  }
232
233  /**
234   * Parses a given list of options.
235   *
236   * @param options     the list of options as an array of strings
237   * @throws Exception  if an option is not supported
238   */
239  public void setOptions(String[] options) throws Exception {
240  }
241
242  /**
243   * Gets the current settings of the script.
244   *
245   * @return            an array of strings suitable for passing to setOptions
246   */
247  public String[] getOptions() {
248    return new String[0];
249  }
250 
251  /**
252   * Returns the extension filters for this type of script.
253   *
254   * @return            the filters
255   */
256  public abstract ExtensionFileFilter[] getFilters();
257 
258  /**
259   * Returns the default extension. Gets automatically added to files if
260   * their name doesn't end with this.
261   *
262   * @return            the default extension (incl. the dot)
263   * @see               #saveAs(File)
264   */
265  public abstract String getDefaultExtension();
266 
267  /**
268   * Returns the current filename.
269   *
270   * @return            the filename, null if no file loaded/saved
271   */
272  public File getFilename() {
273    return m_Filename;
274  }
275 
276  /**
277   * Returns the new line string in use.
278   *
279   * @return            the new line string
280   */
281  public String getNewLine() {
282    return m_NewLine;
283  }
284 
285  /**
286   * Returns whether the script is modified.
287   *
288   * @return            true if the script is modified
289   */
290  public boolean isModified() {
291    return m_Modified;
292  }
293 
294  /**
295   * Returns the content.
296   *
297   * @return            the content or null in case of an error
298   */
299  public String getContent() {
300    String      result;
301   
302    if (m_Document == null)
303      return "";
304   
305    try {
306      synchronized(m_Document) {
307        result = m_Document.getText(0, m_Document.getLength());
308      }
309    }
310    catch (Exception e) {
311      e.printStackTrace();
312      result = null;
313    }
314   
315    return result;
316  }
317 
318  /**
319   * Sets the content.
320   *
321   * @param value       the new content
322   */
323  public void setContent(String value) {
324    if (m_Document == null)
325      return;
326   
327    try {
328      m_Document.insertString(0, value, null);
329    }
330    catch (Exception e) {
331      e.printStackTrace();
332    }
333  }
334
335  /**
336   * Checks whether the extension of the file is a known one.
337   *
338   * @param file        the file to check
339   * @return            true if the exetnsion is known
340   */
341  protected boolean checkExtension(File file) {
342    boolean                     result;
343    int                         i;
344    int                         n;
345    ExtensionFileFilter[]       filters;
346    String[]                    exts;
347
348    result = false;
349    filters  = getFilters();
350    for (i = 0; i < filters.length; i++) {
351      exts = filters[i].getExtensions();
352      for (n = 0; n < exts.length; n++) {
353        if (file.getName().endsWith(exts[n])) {
354          result = true;
355          break;
356        }
357      }
358      if (result)
359        break;
360    }
361   
362    return result;
363  }
364 
365  /**
366   * Empties the document.
367   */
368  public void empty() {
369    if (m_Document != null) {
370      try {
371        m_Document.remove(0, m_Document.getLength());
372      }
373      catch (Exception e) {
374        // ignored
375      }
376    }
377   
378    m_Modified = false;
379    m_Filename = null;
380  }
381 
382  /**
383   * Tries to open the file.
384   *
385   * @param file        the file to open
386   * @return            true if successfully read
387   */
388  public boolean open(File file) {
389    boolean     result;
390    String      content;
391
392    if (m_Document == null)
393      return true;
394   
395    // Warn if extension unwknown
396    if (!checkExtension(file))
397      System.err.println("Extension of file '" + file + "' is unknown!");
398   
399    try {
400      // clear old content
401      m_Document.remove(0, m_Document.getLength());
402     
403      // add new content
404      content = ScriptUtils.load(file);
405      if (content == null)
406        throw new WekaException("Error reading content of file '" + file + "'!");
407      m_Document.insertString(0, content, null);
408     
409      m_Modified = false;
410      m_Filename = file;
411      result     = true;
412    }
413    catch (Exception e) {
414      e.printStackTrace();
415      try {
416        m_Document.remove(0, m_Document.getLength());
417      }
418      catch (Exception ex) {
419        // ignored
420      }
421      result     = false;
422      m_Filename = null;
423    }
424   
425    return result;
426  }
427 
428  /**
429   * Saves the file under with the current filename.
430   *
431   * @return            true if successfully written
432   */
433  public boolean save() {
434    if (m_Filename == null)
435      return false;
436    else
437      return saveAs(m_Filename);
438  }
439 
440  /**
441   * Saves the file under with the given filename (and updates the internal
442   * filename).
443   *
444   * @param file        the filename to write the content to
445   * @return            true if successfully written
446   */
447  public boolean saveAs(File file) {
448    boolean     result;
449    File        backupFile;
450   
451    if (m_Document == null)
452      return true;
453   
454    // correct extension?
455    if (!checkExtension(file))
456      file = new File(file.getPath() + getDefaultExtension());
457
458    // backup previous file
459    if (file.exists()) {
460      backupFile = new File(file.getPath() + BACKUP_EXTENSION);
461      try {
462        ScriptUtils.copy(file, backupFile);
463      }
464      catch (Exception e) {
465        e.printStackTrace();
466      }
467    }
468   
469    // save current content
470    try {
471      result     = ScriptUtils.save(file, m_Document.getText(0, m_Document.getLength()));
472      m_Filename = file;
473      m_Modified = false;
474    }
475    catch (Exception e) {
476      e.printStackTrace();
477      result = false;
478    }
479   
480    return result;
481  }
482
483  /**
484   * Returns whether scripts can be executed.
485   *
486   * @return            true if scripts can be executed
487   */
488  protected abstract boolean canExecuteScripts();
489
490  /**
491   * Returns a new thread to execute.
492   *
493   * @param args        optional commandline arguments
494   * @return            the new thread object
495   */
496  public abstract ScriptThread newThread(String[] args);
497 
498  /**
499   * Performs pre-execution checks:
500   * <ul>
501   *    <li>whether a script is currently running.</li>
502   *    <li>whether script has changed and needs saving</li>
503   *    <li>whether a filename is set (= empty content)</li>
504   * </ul>
505   * Throws exceptions if checks not met.
506   *
507   * @param args        optional commandline arguments
508   * @throws Exception  if checks fail
509   */
510  protected void preCheck(String[] args) throws Exception {
511    if (m_ScriptThread != null)
512      throw new Exception("A script is currently running!");
513    if (m_Modified)
514      throw new Exception("The Script has been modified!");
515    if (m_Filename == null)
516      throw new Exception("The Script contains no content?");
517  }
518 
519  /**
520   * Executes the script.
521   *
522   * @param args        optional commandline arguments
523   */
524  protected void execute(String[] args) {
525    m_ScriptThread = newThread(args);
526    try {
527      m_ScriptThread.start();
528    }
529    catch (Exception e) {
530      e.printStackTrace();
531    }
532  }
533 
534  /**
535   * Executes the script.
536   *
537   * @param args        optional commandline arguments, can be null
538   * @throws Exception  if checks or execution fail
539   */
540  public void start(String[] args) throws Exception {
541    if (args == null)
542      args = new String[0];
543   
544    preCheck(args);
545   
546    execute(args);
547  }
548 
549  /**
550   * Stops the execution of the script.
551   */
552  public void stop() {
553    if (isRunning()) {
554      m_ScriptThread.stopScript();
555      m_ScriptThread = null;
556      notifyScriptFinishedListeners(new ScriptExecutionEvent(this, Type.STOPPED));
557    }
558  }
559 
560  /**
561   * Executes the script without loading it first.
562   *
563   * @param file        the script to execute
564   * @param args        the commandline parameters for the script
565   */
566  public void run(File file, String[] args) {
567    Script      script;
568   
569    try {
570      script = (Script) new SerializedObject(this).getObject();
571      script.m_Filename = file;
572      script.m_Modified = false;
573      script.start(args);
574    }
575    catch (Exception e) {
576      e.printStackTrace();
577    }
578  }
579 
580  /**
581   * Returns whether the script is still running.
582   *
583   * @return            true if the script is still running
584   */
585  public boolean isRunning() {
586    return (m_ScriptThread != null);
587  }
588 
589  /**
590   * Adds the given listener to its internal list.
591   *
592   * @param l           the listener to add
593   */
594  public void addScriptFinishedListener(ScriptExecutionListener l) {
595    m_FinishedListeners.add(l);
596  }
597 
598  /**
599   * Removes the given listener from its internal list.
600   *
601   * @param l           the listener to remove
602   */
603  public void removeScriptFinishedListener(ScriptExecutionListener l) {
604    m_FinishedListeners.remove(l);
605  }
606 
607  /**
608   * Notifies all listeners.
609   *
610   * @param e           the event to send to all listeners
611   */
612  protected void notifyScriptFinishedListeners(ScriptExecutionEvent e) {
613    Iterator<ScriptExecutionListener>   iter;
614   
615    iter = m_FinishedListeners.iterator();
616    while (iter.hasNext())
617      iter.next().scriptFinished(e);
618  }
619 
620  /**
621   * Returns the content as string.
622   *
623   * @return            the current content
624   */
625  public String toString() {
626    String      result;
627   
628    try {
629      if (m_Document == null)
630        result = "";
631      else
632        result = m_Document.getText(0, m_Document.getLength());
633    }
634    catch (Exception e) {
635      result = "";
636    }
637   
638    return result.toString();
639  }
640
641  /**
642   * Make up the help string giving all the command line options.
643   *
644   * @param script      the script to include options for
645   * @return            a string detailing the valid command line options
646   */
647  protected static String makeOptionString(Script script) {
648    StringBuffer        result;
649    Enumeration         enm;
650    Option              option;
651   
652    result = new StringBuffer("");
653
654    result.append("\nHelp requested:\n\n");
655    result.append("-h or -help\n");
656    result.append("\tDisplays this help screen.\n");
657    result.append("-s <file>\n");
658    result.append("\tThe script to execute.\n");
659
660    enm = script.listOptions();
661    while (enm.hasMoreElements()) {
662      option = (Option) enm.nextElement();
663      result.append(option.synopsis() + '\n');
664      result.append(option.description() + "\n");
665    }
666
667    result.append("\n");
668    result.append("Any additional options are passed on to the script as\n");
669    result.append("command-line parameters.\n");
670    result.append("\n");
671   
672    return result.toString();
673  }
674 
675  /**
676   * Runs the specified script. All options that weren't "consumed" (like
677   * "-s" for the script filename), will be used as commandline arguments for
678   * the actual script.
679   *
680   * @param script      the script object to use
681   * @param args        the commandline arguments
682   * @throws Exception  if execution fails
683   */
684  public static void runScript(Script script, String[] args) throws Exception {
685    String              tmpStr;
686    File                scriptFile;
687    Vector<String>      options;
688    int                 i;
689   
690    if (Utils.getFlag('h', args) || Utils.getFlag("help", args)) {
691      System.out.println(makeOptionString(script));
692    }
693    else {
694      // process options
695      tmpStr = Utils.getOption('s', args);
696      if (tmpStr.length() == 0)
697        throw new WekaException("No script supplied!");
698      else
699        scriptFile = new File(tmpStr);
700      script.setOptions(args);
701     
702      // remove empty elements from array
703      options = new Vector<String>();
704      for (i = 0; i < args.length; i++) {
705        if (args[i].length() > 0)
706          options.add(args[i]);
707      }
708     
709      // run script
710      script.run(scriptFile, options.toArray(new String[options.size()]));
711    }
712  }
713}
Note: See TracBrowser for help on using the repository browser.