7a8c7868f78619f257f739f540b5e2d557fd89ab
[jalview.git] / src / jalview / bin / Jalview.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.bin;
22
23 import java.util.Locale;
24
25 import java.awt.GraphicsEnvironment;
26 import java.awt.Color;
27
28 import java.io.BufferedReader;
29 import java.io.File;
30 import java.io.FileOutputStream;
31 import java.io.IOException;
32 import java.io.InputStreamReader;
33 import java.io.OutputStreamWriter;
34 import java.io.PrintWriter;
35 import java.net.MalformedURLException;
36 import java.net.URI;
37 import java.net.URISyntaxException;
38 import java.net.URL;
39 import java.security.AllPermission;
40 import java.security.CodeSource;
41 import java.security.PermissionCollection;
42 import java.security.Permissions;
43 import java.security.Policy;
44 import java.util.HashMap;
45 import java.util.Locale;
46 import java.util.Map;
47 import java.util.Vector;
48 import java.util.logging.ConsoleHandler;
49 import java.util.logging.Level;
50 import java.util.logging.Logger;
51
52 import javax.swing.JOptionPane;
53 import javax.swing.SwingUtilities;
54 import javax.swing.UIManager;
55 import javax.swing.UIManager.LookAndFeelInfo;
56
57 import com.formdev.flatlaf.FlatLightLaf;
58 import com.formdev.flatlaf.util.SystemInfo;
59 import com.threerings.getdown.util.LaunchUtil;
60
61 //import edu.stanford.ejalbert.launching.IBrowserLaunching;
62 import groovy.lang.Binding;
63 import groovy.util.GroovyScriptEngine;
64 import jalview.api.AlignCalcWorkerI;
65 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
66 import jalview.ext.so.SequenceOntology;
67 import jalview.gui.AlignFrame;
68 import jalview.gui.AlignViewport;
69 import jalview.gui.Desktop;
70 import jalview.gui.Preferences;
71 import jalview.gui.PromptUserConfig;
72 import jalview.io.AppletFormatAdapter;
73 import jalview.io.BioJsHTMLOutput;
74 import jalview.io.DataSourceType;
75 import jalview.io.FileFormat;
76 import jalview.io.FileFormatException;
77 import jalview.io.FileFormatI;
78 import jalview.io.FileFormats;
79 import jalview.io.FileLoader;
80 import jalview.io.HtmlSvgOutput;
81 import jalview.io.IdentifyFile;
82 import jalview.io.NewickFile;
83 import jalview.io.gff.SequenceOntologyFactory;
84 import jalview.schemes.ColourSchemeI;
85 import jalview.schemes.ColourSchemeProperty;
86 import jalview.util.ChannelProperties;
87 import jalview.util.HttpUtils;
88 import jalview.util.LaunchUtils;
89 import jalview.util.MessageManager;
90 import jalview.util.Platform;
91 import jalview.ws.jws2.Jws2Discoverer;
92
93 /**
94  * Main class for Jalview Application <br>
95  * <br>
96  * start with: java -classpath "$PATH_TO_LIB$/*:$PATH_TO_CLASSES$" \
97  * jalview.bin.Jalview
98  * 
99  * or on Windows: java -classpath "$PATH_TO_LIB$/*;$PATH_TO_CLASSES$" \
100  * jalview.bin.Jalview jalview.bin.Jalview
101  * 
102  * (ensure -classpath arg is quoted to avoid shell expansion of '*' and do not
103  * embellish '*' to e.g. '*.jar')
104  * 
105  * @author $author$
106  * @version $Revision$
107  */
108 public class Jalview implements ApplicationSingletonI
109 {
110   // for testing those nasty messages you cannot ever find.
111   // static
112   // {
113   // System.setOut(new PrintStream(new ByteArrayOutputStream())
114   // {
115   // @Override
116   // public void println(Object o)
117   // {
118   // if (o != null)
119   // {
120   // System.err.println(o);
121   // }
122   // }
123   //
124   // });
125   // }
126   public static Jalview getInstance()
127   {
128     return (Jalview) ApplicationSingletonProvider
129             .getInstance(Jalview.class);
130   }
131
132   private Jalview()
133   {
134     Platform.getURLCommandArguments();
135     Platform.addJ2SDirectDatabaseCall("https://www.jalview.org");
136     Platform.addJ2SDirectDatabaseCall("http://www.jalview.org");
137     Platform.addJ2SDirectDatabaseCall("http://www.compbio.dundee.ac.uk");
138     Platform.addJ2SDirectDatabaseCall("https://www.compbio.dundee.ac.uk");
139   }
140
141
142   private boolean headless;
143
144   private Desktop desktop;
145
146   public AlignFrame currentAlignFrame;
147
148   public String appletResourcePath;
149
150   public String j2sAppletID;
151
152   private boolean noCalculation, noMenuBar, noStatus;
153
154   private boolean noAnnotation;
155
156   public boolean getStartCalculations()
157   {
158     return !noCalculation;
159   }
160
161   public boolean getAllowMenuBar()
162   {
163     return !noMenuBar;
164   }
165
166   public boolean getShowStatus()
167   {
168     return !noStatus;
169   }
170
171   public boolean getShowAnnotation()
172   {
173     return !noAnnotation;
174   }
175
176   static
177   {
178     if (Platform.isJS())
179     {
180        Platform.getURLCommandArguments();
181     } else
182     /**
183      * Java only
184      * 
185      * @j2sIgnore
186      */
187     {
188       // grab all the rights we can for the JVM
189       Policy.setPolicy(new Policy()
190       {
191         @Override
192         public PermissionCollection getPermissions(CodeSource codesource)
193         {
194           Permissions perms = new Permissions();
195           perms.add(new AllPermission());
196           return (perms);
197         }
198
199         @Override
200         public void refresh()
201         {
202         }
203       });
204     }
205   }
206
207   /**
208    * keep track of feature fetching tasks.
209    * 
210    * @author JimP
211    * 
212    */
213   class FeatureFetcher
214   {
215     /*
216      * TODO: generalise to track all jalview events to orchestrate batch processing
217      * events.
218      */
219
220     private int queued = 0;
221
222     private int running = 0;
223
224     public FeatureFetcher()
225     {
226
227     }
228
229     public void addFetcher(final AlignFrame af,
230             final Vector<String> dasSources)
231     {
232       final long id = System.currentTimeMillis();
233       queued++;
234       final FeatureFetcher us = this;
235       new Thread(new Runnable()
236       {
237
238         @Override
239         public void run()
240         {
241           synchronized (us)
242           {
243             queued--;
244             running++;
245           }
246
247           af.setProgressBar(MessageManager
248                   .getString("status.das_features_being_retrived"), id);
249           af.featureSettings_actionPerformed(null);
250           af.setProgressBar(null, id);
251           synchronized (us)
252           {
253             running--;
254           }
255         }
256       }).start();
257     }
258
259     public synchronized boolean allFinished()
260     {
261       return queued == 0 && running == 0;
262     }
263
264   }
265
266   private final static boolean doPlatformLogging = false;
267
268   /**
269    * main class for Jalview application
270    * 
271    * @param args
272    *          open <em>filename</em>
273    */
274   public static void main(String[] args)
275   {
276     if (doPlatformLogging)
277     {
278       Platform.startJavaLogging();
279     }
280
281     getInstance().doMain(args);
282
283   }
284   
285
286   
287
288   /**
289    * @param args
290    */
291   void doMain(String[] args)
292   {
293
294     boolean isJS = Platform.isJS();
295     if (!isJS)
296     {
297       System.setSecurityManager(null);
298     }
299
300     /*
301      * @j2sNative J2S.db._DirectDatabaseCalls["compbio.dundee.ac.uk"]=null;
302      * @j2sNative J2S.db._DirectDatabaseCalls["jalview.org"]=null;
303      * 
304      */
305     System.out
306             .println("Java version: " + System.getProperty("java.version"));
307     System.out.println("Java Home: " + System.getProperty("java.home"));
308     System.out.println(System.getProperty("os.arch") + " "
309             + System.getProperty("os.name") + " "
310             + System.getProperty("os.version"));
311     String val = System.getProperty("sys.install4jVersion");
312     if (val != null)
313     {
314       System.out.println("Install4j version: " + val);
315     }
316     val = System.getProperty("installer_template_version");
317     if (val != null)
318     {
319       System.out.println("Install4j template version: " + val);
320     }
321     val = System.getProperty("launcher_version");
322     if (val != null)
323     {
324       System.out.println("Launcher version: " + val);
325     }
326
327     // report Jalview version
328     Cache.getInstance().loadBuildProperties(true);
329
330     ArgsParser aparser = new ArgsParser(args);
331     headless = false;
332
333     String usrPropsFile = aparser.getValue("props");
334     Cache.loadProperties(usrPropsFile); // must do this before
335     boolean allowServices = true;
336     
337     if (isJS)
338     {
339       j2sAppletID = Platform.getAppID(null);
340       Preferences.setAppletDefaults();
341       Cache.loadProperties(usrPropsFile); // again, because we
342       // might be changing defaults here?
343       appletResourcePath = (String) aparser.getAppletValue("resourcepath",
344               null, true);
345     }
346
347     else
348     /**
349      * Java only
350      * 
351      * @j2sIgnore
352      */
353     {
354       if (usrPropsFile != null)
355       {
356         System.out.println(
357                 "CMD [-props " + usrPropsFile + "] executed successfully!");
358       }
359       if (aparser.contains("help") || aparser.contains("h"))
360       {
361         showUsage();
362         System.exit(0);
363       }
364       // BH note: Only -nodisplay is official; others are deprecated?
365       if (aparser.contains("nodisplay") || aparser.contains("nogui")
366               || aparser.contains("headless")
367               || GraphicsEnvironment.isHeadless())
368       {
369         if (!isJS) {
370           // BH Definitely not a good idea in JavaScript; 
371           // probably should not be here for Java, either.  
372           System.setProperty("java.awt.headless", "true");
373         }
374         headless = true;
375       }
376       // anything else!
377
378       // allow https handshakes to download intermediate certs if necessary
379       System.setProperty("com.sun.security.enableAIAcaIssuers", "true");
380       final String jabawsUrl = aparser.getValue(ArgsParser.JABAWS);
381       allowServices = !("none".equals(jabawsUrl));
382       if (allowServices && jabawsUrl != null)
383       {
384         try
385         {
386           Jws2Discoverer.getInstance().setPreferredUrl(jabawsUrl);
387           System.out.println(
388                   "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
389         } catch (MalformedURLException e)
390         {
391           System.err.println(
392                   "Invalid jabaws parameter: " + jabawsUrl + " ignored");
393         }
394       }
395     }
396
397     String defs = aparser.getValue(ArgsParser.SETPROP);
398     while (defs != null)
399     {
400       int p = defs.indexOf('=');
401       if (p == -1)
402       {
403         System.err.println("Ignoring invalid setprop argument : " + defs);
404       }
405       else
406       {
407         System.out.println("Executing setprop argument: " + defs);
408         if (isJS)
409         {
410           Cache.setProperty(defs.substring(0, p), defs.substring(p + 1));
411         }
412       }
413       defs = aparser.getValue("setprop");
414     }
415     if (System.getProperty("java.awt.headless") != null
416             && System.getProperty("java.awt.headless").equals("true"))
417     {
418       headless = true;
419     }
420     System.setProperty("http.agent",
421             "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown"));
422     try
423     {
424       Console.initLogger();
425     } catch (NoClassDefFoundError error)
426     {
427       error.printStackTrace();
428       System.out.println("\nEssential logging libraries not found."
429               + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview");
430       System.exit(0);
431     }
432
433     desktop = null;
434
435     setLookAndFeel();
436
437     /*
438      * configure 'full' SO model if preferences say to, else use the default (full SO)
439      * - as JS currently doesn't have OBO parsing, it must use 'Lite' version
440      */
441     boolean soDefault = !isJS;
442     if (Cache.getDefault("USE_FULL_SO", soDefault))
443     {
444       SequenceOntologyFactory.setSequenceOntology(new SequenceOntology());
445     }
446
447     if (!headless)
448     {
449       Desktop.nosplash = aparser.contains("nosplash");
450       desktop = Desktop.getInstance();
451       desktop.setInBatchMode(true); // indicate we are starting up
452
453       try
454       {
455         JalviewTaskbar.setTaskbar(this);
456       } catch (Exception e)
457       {
458         Console.info("Cannot set Taskbar");
459         Console.error(e.getMessage());
460         // e.printStackTrace();
461       } catch (Throwable t)
462       {
463         Console.info("Cannot set Taskbar");
464         Console.error(t.getMessage());
465         // t.printStackTrace();
466       }
467
468       // set Proxy settings before all the internet calls
469       Cache.setProxyPropertiesFromPreferences();
470
471       desktop.setVisible(true);
472
473       if (isJS)
474       {
475         Cache.setProperty("SHOW_JWS2_SERVICES", "false");
476       }
477       if (allowServices && !aparser.contains("nowebservicediscovery"))
478       {
479         desktop.startServiceDiscovery();
480       }
481
482       if (!isJS)
483       /**
484        * Java only
485        * 
486        * @j2sIgnore
487        */
488       {
489         /**
490          * Check to see that the JVM version being run is suitable for the Java
491          * version this Jalview was compiled for. Popup a warning if not.
492          */
493         if (!LaunchUtils.checkJavaVersion())
494         {
495           Console.warn("The Java version being used (Java "
496                   + LaunchUtils.getJavaVersion()
497                   + ") may lead to problems. This installation of Jalview should be used with Java "
498                   + LaunchUtils.getJavaCompileVersion() + ".");
499
500           if (!LaunchUtils
501                   .getBooleanUserPreference("IGNORE_JVM_WARNING_POPUP"))
502           {
503             Object[] options = {
504                 MessageManager.getString("label.continue") };
505             JOptionPane.showOptionDialog(null,
506                     MessageManager.formatMessage(
507                             "warning.wrong_jvm_version_message",
508                             LaunchUtils.getJavaVersion(),
509                             LaunchUtils.getJavaCompileVersion()),
510                     MessageManager
511                             .getString("warning.wrong_jvm_version_title"),
512                     JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
513                     null, options, options[0]);
514           }
515         }
516         if (!aparser.contains("nousagestats"))
517         {
518           startUsageStats(desktop);
519         }
520         else
521         {
522           System.err.println("CMD [-nousagestats] executed successfully!");
523         }
524
525         if (!aparser.contains("noquestionnaire"))
526         {
527           String url = aparser.getValue("questionnaire");
528           if (url != null)
529           {
530             // Start the desktop questionnaire prompter with the specified
531             // questionnaire
532             Console.debug("Starting questionnaire url at " + url);
533             desktop.checkForQuestionnaire(url);
534             System.out.println("CMD questionnaire[-" + url
535                     + "] executed successfully!");
536           }
537           else
538           {
539             if (Cache.getProperty("NOQUESTIONNAIRES") == null)
540             {
541               // Start the desktop questionnaire prompter with the specified
542               // questionnaire
543               // String defurl =
544               // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
545               // //
546               String defurl = "https://www.jalview.org/cgi-bin/questionnaire.pl";
547               Console.debug(
548                       "Starting questionnaire with default url: " + defurl);
549               desktop.checkForQuestionnaire(defurl);
550             }
551           }
552         }
553         else
554         {
555           System.err
556                   .println("CMD [-noquestionnaire] executed successfully!");
557         }
558
559         if (!aparser.contains("nonews")
560                 || Cache.getProperty("NONEWS") == null)
561         {
562           desktop.checkForNews();
563         }
564
565         if (!aparser.contains("nohtmltemplates")
566                 || Cache.getProperty("NOHTMLTEMPLATES") == null)
567         {
568           BioJsHTMLOutput.updateBioJS();
569         }
570       }
571     }
572
573     // Check if JVM and compile version might cause problems and log if it
574     // might.
575     if (headless && !Platform.isJS() && !LaunchUtils.checkJavaVersion())
576     {
577       Console.warn("The Java version being used (Java "
578               + LaunchUtils.getJavaVersion()
579               + ") may lead to problems. This installation of Jalview should be used with Java "
580               + LaunchUtils.getJavaCompileVersion() + ".");
581     }
582     parseArguments(aparser, true);
583   }
584
585   /**
586    * Parse all command-line String[] arguments as well as all JavaScript-derived
587    * parameters from Info.
588    * 
589    * We allow for this method to be run from JavaScript. Basically allowing
590    * simple scripting.
591    * 
592    * @param aparser
593    * @param isStartup
594    */
595   public void parseArguments(ArgsParser aparser, boolean isStartup)
596   {
597
598     String groovyscript = null; // script to execute after all loading is
599     boolean isJS = Platform.isJS();
600     if (!isJS)
601     /** @j2sIgnore */
602     {
603       // Move any new getdown-launcher-new.jar into place over old
604       // getdown-launcher.jar
605       String appdirString = System.getProperty("getdownappdir");
606       if (appdirString != null && appdirString.length() > 0)
607       {
608         final File appdir = new File(appdirString);
609         new Thread()
610         {
611           @Override
612           public void run()
613           {
614             LaunchUtil.upgradeGetdown(
615                     new File(appdir, "getdown-launcher-old.jar"),
616                     new File(appdir, "getdown-launcher.jar"),
617                     new File(appdir, "getdown-launcher-new.jar"));
618           }
619         }.start();
620       }
621
622       // completed one way or another
623       // extract groovy argument and execute if necessary
624       groovyscript = aparser.getValue("groovy", true);
625     }
626
627     String file = aparser.getValue("open", true);
628
629     if (!isJS && file == null && desktop == null)
630     {
631       System.out.println("No files to open!");
632       System.exit(1);
633     }
634     setDisplayParameters(aparser);
635     
636     // time to open a file.
637     long progress = -1;
638     DataSourceType protocol = null;
639     FileLoader fileLoader = new FileLoader(!headless);
640     FileFormatI format = null;
641     // Finally, deal with the remaining input data.
642     AlignFrame af = null;
643
644     JalviewJSApp jsApp = (isJS ? new JalviewJSApp(this, aparser) : null);
645
646     if (file == null)
647     {
648       if (isJS)
649       {
650         // JalviewJS allows sequence1 sequence2 ....
651         
652       }
653       else if (!headless && Cache.getDefault("SHOW_STARTUP_FILE", true))
654     /**
655      * Java only
656      * 
657      * @j2sIgnore
658      */
659     {
660       file = Cache.getDefault("STARTUP_FILE",
661               Cache.getDefault("www.jalview.org", "https://www.jalview.org")
662                       + "/examples/exampleFile_2_7.jvp");
663       if (file.equals("http://www.jalview.org/examples/exampleFile_2_3.jar")
664               || file.equals(
665                       "http://www.jalview.org/examples/exampleFile_2_7.jar"))
666       {
667         file.replace("http:", "https:");
668         // hardwire upgrade of the startup file
669         file.replace("_2_3", "_2_7");
670         file.replace("2_7.jar", "2_7.jvp");
671         // and remove the stale setting
672         Cache.removeProperty("STARTUP_FILE");
673       }
674
675       protocol = AppletFormatAdapter.checkProtocol(file);
676
677       if (file.endsWith(".jar"))
678       {
679         format = FileFormat.Jalview;
680       }
681       else
682       {
683         try
684         {
685           format = new IdentifyFile().identify(file, protocol);
686         } catch (FileFormatException e)
687         {
688           // TODO what?
689         }
690       }
691
692       af = fileLoader.LoadFileWaitTillLoaded(file, protocol, format);
693        }
694     }
695     else
696     {
697       if (!headless)
698       {
699         desktop.setProgressBar(
700                 MessageManager
701                         .getString("status.processing_commandline_args"),
702                 progress = System.currentTimeMillis());
703       }
704       System.out.println("CMD [-open " + file + "] executed successfully!");
705
706       if (!Platform.isJS())
707       /**
708        * ignore in JavaScript -- can't just file existence - could load it?
709        * 
710        * @j2sIgnore
711        */
712       {
713         if (!HttpUtils.startsWithHttpOrHttps(file))
714         {
715           if (!(new File(file)).exists())
716           {
717             System.out.println("Can't find " + file);
718             if (headless)
719             {
720               System.exit(1);
721             }
722           }
723         }
724       }
725
726       // JS Only argument to provide a format parameter to specify what format to use
727       String fileFormat = (isJS
728               ? (String) aparser.getAppletValue("format", null, true)
729               : null);
730       protocol = AppletFormatAdapter.checkProtocol(file);
731
732       try
733       {
734         format = (fileFormat != null
735                 ? FileFormats.getInstance().forName(fileFormat)
736                 : null);
737         if (format == null)
738         {
739           format = new IdentifyFile().identify(file, protocol);
740         }
741       } catch (FileFormatException e1)
742       {
743         // TODO ?
744       }
745
746       af = new FileLoader(!headless).LoadFileWaitTillLoaded(file, protocol,
747               format);
748       if (af == null)
749       {
750         System.out.println("jalview error - AlignFrame was not created");
751       }
752       else
753       {
754         
755         // JalviewLite interface for JavaScript allows second file open
756         String file2 = aparser.getValue(ArgsParser.OPEN2, true);
757         if (file2 != null)
758         {
759           protocol = AppletFormatAdapter.checkProtocol(file2);
760           try
761           {
762             format = new IdentifyFile().identify(file2, protocol);
763           } catch (FileFormatException e1)
764           {
765             // TODO ?
766           }
767           AlignFrame af2 = new FileLoader(!headless)
768                   .LoadFileWaitTillLoaded(file2, protocol, format);
769           if (af2 == null)
770           {
771             System.out.println("error");
772           }
773           else
774           {
775             AlignViewport.openLinkedAlignmentAs(af,
776                     af.getViewport().getAlignment(),
777                     af2.getViewport().getAlignment(), "",
778                     AlignViewport.SPLIT_FRAME);
779             System.out.println(
780                     "CMD [-open2 " + file2 + "] executed successfully!");
781           }
782         }
783         // af is loaded - so set it as current frame
784         setCurrentAlignFrame(af);
785
786         setFrameDependentProperties(aparser, af);
787         
788         if (isJS)
789         {
790           jsApp.initFromParams(af);
791         }
792         else
793         /**
794          * Java only
795          * 
796          * @j2sIgnore
797          */
798         {
799           if (groovyscript != null)
800           {
801             // Execute the groovy script after we've done all the rendering
802             // stuff
803             // and before any images or figures are generated.
804             System.out.println("Executing script " + groovyscript);
805             executeGroovyScript(groovyscript, af);
806             System.out.println("CMD groovy[" + groovyscript
807                     + "] executed successfully!");
808             groovyscript = null;
809           }
810         }
811         if (!isJS || !isStartup) {
812           createOutputFiles(aparser, format);
813         }
814       }
815       if (headless)
816       {
817         af.getViewport().getCalcManager().shutdown();
818       }
819     }
820     // extract groovy arguments before anything else.
821     // Once all other stuff is done, execute any groovy scripts (in order)
822     if (!isJS && groovyscript != null)
823     {
824       if (Cache.groovyJarsPresent())
825       {
826         // TODO: DECIDE IF THIS SECOND PASS AT GROOVY EXECUTION IS STILL REQUIRED !!
827         System.out.println("Executing script " + groovyscript);
828         executeGroovyScript(groovyscript, af);
829         System.out.println("CMD groovy[" + groovyscript
830                     + "] executed successfully!");
831
832       }
833       else
834       {
835         System.err.println(
836                 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
837                         + groovyscript);
838       }
839     }
840
841     // and finally, turn off batch mode indicator - if the desktop still exists
842     if (desktop != null)
843     {
844       if (progress != -1)
845       {
846         desktop.setProgressBar(null, progress);
847       }
848       desktop.setInBatchMode(false);
849     }
850     
851     if (jsApp != null) {
852       jsApp.callInitCallback();
853     }
854   }
855   
856   /**
857    * Set general display parameters irrespective of file loading or headlessness.
858    * 
859    * @param aparser
860    */
861   private void setDisplayParameters(ArgsParser aparser)
862   {
863     if (aparser.contains(ArgsParser.NOMENUBAR))
864     {
865       noMenuBar = true;
866       System.out.println("CMD [nomenu] executed successfully!");
867     }
868
869     if (aparser.contains(ArgsParser.NOSTATUS))
870     {
871       noStatus = true;
872       System.out.println("CMD [nostatus] executed successfully!");
873     }
874
875     if (aparser.contains(ArgsParser.NOANNOTATION)
876             || aparser.contains(ArgsParser.NOANNOTATION2))
877     {
878       noAnnotation = true;
879       System.out.println("CMD no-annotation executed successfully!");
880     }
881     if (aparser.contains(ArgsParser.NOCALCULATION))
882     {
883       noCalculation = true;
884       System.out.println("CMD [nocalculation] executed successfully!");
885     }
886   }
887
888   private void setFrameDependentProperties(ArgsParser aparser,
889           AlignFrame af)
890   {
891     String data = aparser.getValue(ArgsParser.COLOUR, true);
892     if (data != null)
893     {
894       data.replaceAll("%20", " ");
895
896       ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
897               af.getViewport(), af.getViewport().getAlignment(), data);
898
899       if (cs != null)
900       {
901         System.out.println(
902                 "CMD [-color " + data + "] executed successfully!");
903       }
904       af.changeColour(cs);
905     }
906
907     // Must maintain ability to use the groups flag
908     data = aparser.getValue(ArgsParser.GROUPS, true);
909     if (data != null)
910     {
911       af.parseFeaturesFile(data,
912               AppletFormatAdapter.checkProtocol(data));
913       // System.out.println("Added " + data);
914       System.out.println(
915               "CMD groups[-" + data + "]  executed successfully!");
916     }
917     data = aparser.getValue(ArgsParser.FEATURES, true);
918     if (data != null)
919     {
920       af.parseFeaturesFile(data,
921               AppletFormatAdapter.checkProtocol(data));
922       // System.out.println("Added " + data);
923       System.out.println(
924               "CMD [-features " + data + "]  executed successfully!");
925     }
926     data = aparser.getValue(ArgsParser.ANNOTATIONS, true);
927     if (data != null)
928     {
929       af.loadJalviewDataFile(data, null, null, null);
930       // System.out.println("Added " + data);
931       System.out.println(
932               "CMD [-annotations " + data + "] executed successfully!");
933     }
934
935     // JavaScript feature
936
937     if (aparser.contains(ArgsParser.SHOWOVERVIEW))
938     {
939       af.overviewMenuItem_actionPerformed(null);
940       System.out.println("CMD [showoverview] executed successfully!");
941     }
942
943     // set or clear the sortbytree flag.
944     if (aparser.contains(ArgsParser.SORTBYTREE))
945     {
946       af.getViewport().setSortByTree(true);
947       if (af.getViewport().getSortByTree())
948       {
949         System.out.println("CMD [-sortbytree] executed successfully!");
950       }
951     }
952
953     boolean doUpdateAnnotation = false;
954     /**
955      * we do this earlier in JalviewJS because of a complication with
956      * SHOWOVERVIEW
957      * 
958      * For now, just fixing this in JalviewJS.
959      *
960      * 
961      * @j2sIgnore
962      * 
963      */
964     {
965       if (noAnnotation)
966       {
967         af.getViewport().setShowAnnotation(false);
968         if (!af.getViewport().isShowAnnotation())
969         {
970           doUpdateAnnotation = true;
971         }
972       }
973
974     }
975
976     if (aparser.contains(ArgsParser.NOSORTBYTREE))
977     {
978       af.getViewport().setSortByTree(false);
979       if (!af.getViewport().getSortByTree())
980       {
981         doUpdateAnnotation = true;
982         System.out
983                 .println("CMD [-nosortbytree] executed successfully!");
984       }
985     }
986     if (doUpdateAnnotation)
987     { // BH 2019.07.24
988       af.setMenusForViewport();
989       af.alignPanel.updateLayout();
990     }
991
992     data = aparser.getValue(ArgsParser.TREE, true);
993     if (data != null)
994     {
995       try
996       {
997         NewickFile nf = new NewickFile(data,
998                 AppletFormatAdapter.checkProtocol(data));
999         af.getViewport()
1000                 .setCurrentTree(af.showNewickTree(nf, data).getTree());
1001         System.out.println(
1002                 "CMD [-tree " + data + "] executed successfully!");
1003       } catch (IOException ex)
1004       {
1005         System.err.println("Couldn't add tree " + data);
1006         ex.printStackTrace(System.err);
1007       }
1008     }
1009     // TODO - load PDB structure(s) to alignment JAL-629
1010     // (associate with identical sequence in alignment, or a specified
1011     // sequence)
1012
1013   }
1014
1015   /**
1016    * Writes an output file for each format (if any) specified in the
1017    * command-line arguments. Supported formats are currently
1018    * <ul>
1019    * <li>png</li>
1020    * <li>svg</li>
1021    * <li>html</li>
1022    * <li>biojsmsa</li>
1023    * <li>imgMap</li>
1024    * <li>eps</li>
1025    * </ul>
1026    * A format parameter should be followed by a parameter specifying the output
1027    * file name. {@code imgMap} parameters should follow those for the
1028    * corresponding alignment image output.
1029    * 
1030    * @param aparser
1031    * @param format
1032    */
1033   private void createOutputFiles(ArgsParser aparser, FileFormatI format)
1034   {
1035     // logic essentially the same as 2.11.2/2.11.3 but uses a switch instead
1036     AlignFrame af = currentAlignFrame;
1037     while (aparser.getSize() >= 2)
1038     {
1039       String outputFormat = aparser.nextValue();
1040       File imageFile;
1041       String fname;
1042       switch (outputFormat.toLowerCase(Locale.ROOT))
1043       {
1044       case "png":
1045         imageFile = new File(aparser.nextValue());
1046         af.createPNG(imageFile);
1047         System.out.println(
1048                 "Creating PNG image: " + imageFile.getAbsolutePath());
1049         continue;
1050       case "svg":
1051         imageFile = new File(aparser.nextValue());
1052         af.createSVG(imageFile);
1053         System.out.println(
1054                 "Creating SVG image: " + imageFile.getAbsolutePath());
1055         continue;
1056       case "eps":
1057         imageFile = new File(aparser.nextValue());
1058         System.out.println(
1059                 "Creating EPS file: " + imageFile.getAbsolutePath());
1060         af.createEPS(imageFile);
1061         continue;
1062       case "biojsmsa":
1063         fname = new File(aparser.nextValue()).getAbsolutePath();
1064         try
1065         {
1066           BioJsHTMLOutput.refreshVersionInfo(
1067                   BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
1068         } catch (URISyntaxException e)
1069         {
1070           e.printStackTrace();
1071         }
1072         BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
1073         bjs.exportHTML(fname);
1074         System.out.println("Creating BioJS MSA Viwer HTML file: " + fname);
1075         continue;
1076       case "html":
1077         fname = new File(aparser.nextValue()).getAbsolutePath();
1078         HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
1079         htmlSVG.exportHTML(fname);
1080         System.out.println("Creating HTML image: " + fname);
1081         continue;
1082       case "imgmap":
1083         imageFile = new File(aparser.nextValue());
1084         af.alignPanel.makePNGImageMap(imageFile, "unnamed.png");
1085         System.out.println(
1086                 "Creating image map: " + imageFile.getAbsolutePath());
1087         continue;
1088       default:
1089         // fall through - try to parse as an alignment data export format
1090         FileFormatI outFormat = null;
1091         try
1092         {
1093           outFormat = FileFormats.getInstance().forName(outputFormat);
1094         } catch (Exception formatP)
1095         {
1096         }
1097         if (outFormat == null)
1098         {
1099           System.out.println("Couldn't parse " + outputFormat
1100                   + " as a valid Jalview format string.");
1101           continue;
1102         }
1103         if (!outFormat.isWritable())
1104         {
1105           System.out.println(
1106                   "This version of Jalview does not support alignment export as "
1107                           + outputFormat);
1108           continue;
1109         }
1110         // record file as it was passed to Jalview so it is recognisable to the CLI
1111         // caller
1112         String file;
1113         fname = new File(file = aparser.nextValue()).getAbsolutePath();
1114         // JBPNote - yuck - really wish we did have a bean returned from this which gave
1115         // success/fail like before !
1116         af.saveAlignment(fname, outFormat);
1117         if (!af.isSaveAlignmentSuccessful())
1118         {
1119           System.out.println("Written alignment in " + outputFormat
1120                   + " format to " + file);
1121           continue;
1122         }
1123         else
1124         {
1125           System.out.println("Error writing file " + file + " in "
1126                   + outputFormat + " format!!");
1127         }
1128       }
1129     }
1130     // ??? Should report - 'ignoring' extra args here...
1131     while (aparser.getSize() > 0)
1132     {
1133       System.out.println("Ignoring extra argument: " + aparser.nextValue());
1134     }
1135   }
1136
1137   private static void setLookAndFeel()
1138   {
1139     // property laf = "crossplatform", "system", "gtk", "metal", "nimbus",
1140     // "mac" or "flat"
1141     // If not set (or chosen laf fails), use the normal SystemLaF and if on Mac,
1142     // try Quaqua/Vaqua.
1143     String lafProp = System.getProperty("laf");
1144     String lafSetting = Cache.getDefault("PREFERRED_LAF", null);
1145     String laf = "none";
1146     if (lafProp != null)
1147     {
1148       laf = lafProp;
1149     }
1150     else if (lafSetting != null)
1151     {
1152       laf = lafSetting;
1153     }
1154     boolean lafSet = false;
1155     switch (laf)
1156     {
1157     case "crossplatform":
1158       lafSet = setCrossPlatformLookAndFeel();
1159       if (!lafSet)
1160       {
1161         Console.error("Could not set requested laf=" + laf);
1162       }
1163       break;
1164     case "system":
1165       lafSet = setSystemLookAndFeel();
1166       if (!lafSet)
1167       {
1168         Console.error("Could not set requested laf=" + laf);
1169       }
1170       break;
1171     case "gtk":
1172       lafSet = setGtkLookAndFeel();
1173       if (!lafSet)
1174       {
1175         Console.error("Could not set requested laf=" + laf);
1176       }
1177       break;
1178     case "metal":
1179       lafSet = setMetalLookAndFeel();
1180       if (!lafSet)
1181       {
1182         Console.error("Could not set requested laf=" + laf);
1183       }
1184       break;
1185     case "nimbus":
1186       lafSet = setNimbusLookAndFeel();
1187       if (!lafSet)
1188       {
1189         Console.error("Could not set requested laf=" + laf);
1190       }
1191       break;
1192     case "flat":
1193       lafSet = setFlatLookAndFeel();
1194       if (!lafSet)
1195       {
1196         Console.error("Could not set requested laf=" + laf);
1197       }
1198       break;
1199     case "quaqua":
1200       lafSet = setQuaquaLookAndFeel();
1201       if (!lafSet)
1202       {
1203         Console.error("Could not set requested laf=" + laf);
1204       }
1205       break;
1206     case "vaqua":
1207       lafSet = setVaquaLookAndFeel();
1208       if (!lafSet)
1209       {
1210         Console.error("Could not set requested laf=" + laf);
1211       }
1212       break;
1213     case "mac":
1214       lafSet = setMacLookAndFeel();
1215       if (!lafSet)
1216       {
1217         Console.error("Could not set requested laf=" + laf);
1218       }
1219       break;
1220     case "none":
1221       break;
1222     default:
1223       Console.error("Requested laf=" + laf + " not implemented");
1224     }
1225     if (!lafSet)
1226     {
1227       setSystemLookAndFeel();
1228       if (Platform.isLinux())
1229       {
1230         setMetalLookAndFeel();
1231       }
1232       if (Platform.isMac())
1233       {
1234         setMacLookAndFeel();
1235       }
1236     }
1237   }
1238
1239   private static boolean setCrossPlatformLookAndFeel()
1240   {
1241     boolean set = false;
1242     try
1243     {
1244       UIManager.setLookAndFeel(
1245               UIManager.getCrossPlatformLookAndFeelClassName());
1246       set = true;
1247     } catch (Exception ex)
1248     {
1249       Console.error("Unexpected Look and Feel Exception");
1250       Console.error(ex.getMessage());
1251       Console.debug(Cache.getStackTraceString(ex));
1252     }
1253     return set;
1254   }
1255
1256   private static boolean setSystemLookAndFeel()
1257   {
1258     boolean set = false;
1259     try
1260     {
1261       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
1262       set = true;
1263     } catch (Exception ex)
1264     {
1265       Console.error("Unexpected Look and Feel Exception");
1266       Console.error(ex.getMessage());
1267       Console.debug(Cache.getStackTraceString(ex));
1268     }
1269     return set;
1270   }
1271
1272   private static boolean setSpecificLookAndFeel(String name,
1273           String className, boolean nameStartsWith)
1274   {
1275     boolean set = false;
1276     try
1277     {
1278       for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
1279       {
1280         if (info.getName() != null && nameStartsWith
1281                 ? info.getName().toLowerCase(Locale.ROOT)
1282                         .startsWith(name.toLowerCase(Locale.ROOT))
1283                 : info.getName().toLowerCase(Locale.ROOT)
1284                         .equals(name.toLowerCase(Locale.ROOT)))
1285         {
1286           className = info.getClassName();
1287           break;
1288         }
1289       }
1290       UIManager.setLookAndFeel(className);
1291       set = true;
1292     } catch (Exception ex)
1293     {
1294       Console.error("Unexpected Look and Feel Exception");
1295       Console.error(ex.getMessage());
1296       Console.debug(Cache.getStackTraceString(ex));
1297     }
1298     return set;
1299   }
1300
1301   private static boolean setGtkLookAndFeel()
1302   {
1303     return setSpecificLookAndFeel("gtk",
1304             "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true);
1305   }
1306
1307   private static boolean setMetalLookAndFeel()
1308   {
1309     return setSpecificLookAndFeel("metal",
1310             "javax.swing.plaf.metal.MetalLookAndFeel", false);
1311   }
1312
1313   private static boolean setNimbusLookAndFeel()
1314   {
1315     return setSpecificLookAndFeel("nimbus",
1316             "javax.swing.plaf.nimbus.NimbusLookAndFeel", false);
1317   }
1318
1319   private static boolean setFlatLookAndFeel()
1320   {
1321     boolean set = setSpecificLookAndFeel("flatlaf light",
1322             "com.formdev.flatlaf.FlatLightLaf", false);
1323     if (set)
1324     {
1325       if (Platform.isMac())
1326       {
1327         System.setProperty("apple.laf.useScreenMenuBar", "true");
1328         System.setProperty("apple.awt.application.name",
1329                 ChannelProperties.getProperty("app_name"));
1330         System.setProperty("apple.awt.application.appearance", "system");
1331         if (SystemInfo.isMacFullWindowContentSupported
1332             && Desktop.getInstance() != null)
1333         {
1334           Desktop.getInstance().getRootPane()
1335                   .putClientProperty("apple.awt.fullWindowContent", true);
1336           Desktop.getInstance().getRootPane()
1337                   .putClientProperty("apple.awt.transparentTitleBar", true);
1338         }
1339
1340         SwingUtilities.invokeLater(() -> {
1341           FlatLightLaf.setup();
1342         });
1343       }
1344
1345       UIManager.put("TabbedPane.showTabSeparators", true);
1346       UIManager.put("TabbedPane.tabSeparatorsFullHeight", true);
1347       UIManager.put("TabbedPane.tabsOverlapBorder", true);
1348       // UIManager.put("TabbedPane.hasFullBorder", true);
1349       UIManager.put("TabbedPane.tabLayoutPolicy", "scroll");
1350       UIManager.put("TabbedPane.scrollButtonsPolicy", "asNeeded");
1351       UIManager.put("TabbedPane.smoothScrolling", true);
1352       UIManager.put("TabbedPane.tabWidthMode", "compact");
1353       UIManager.put("TabbedPane.selectedBackground", Color.white);
1354     }
1355     return set;
1356   }
1357   private static boolean setQuaquaLookAndFeel()
1358   {
1359     return setSpecificLookAndFeel("quaqua",
1360             ch.randelshofer.quaqua.QuaquaManager.getLookAndFeel().getClass()
1361                     .getName(),
1362             false);
1363   }
1364
1365   private static boolean setVaquaLookAndFeel()
1366   {
1367     return setSpecificLookAndFeel("vaqua",
1368             "org.violetlib.aqua.AquaLookAndFeel", false);
1369   }
1370
1371   private static boolean setMacLookAndFeel()
1372   {
1373     boolean set = false;
1374     System.setProperty("com.apple.mrj.application.apple.menu.about.name",
1375             ChannelProperties.getProperty("app_name"));
1376     System.setProperty("apple.laf.useScreenMenuBar", "true");
1377     /*
1378      * broken native LAFs on (ARM?) macbooks
1379     set = setQuaquaLookAndFeel();
1380     if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
1381             .toLowerCase(Locale.ROOT).contains("quaqua"))
1382     {
1383       set = setVaquaLookAndFeel();
1384     }
1385      */
1386     set = setFlatLookAndFeel();
1387     return set;
1388   }
1389
1390   private static void showUsage()
1391   {
1392     System.out.println(
1393             "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
1394                     + "-nodisplay\tRun Jalview without User Interface.\n"
1395                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
1396                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
1397                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
1398                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
1399                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
1400                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
1401                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
1402                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
1403                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
1404                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
1405                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
1406                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
1407                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
1408                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
1409                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
1410                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
1411                     + "-html FILE\tCreate HTML file from alignment.\n"
1412                     + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
1413                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
1414                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
1415                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
1416                     + "-noquestionnaire\tTurn off questionnaire check.\n"
1417                     + "-nonews\tTurn off check for Jalview news.\n"
1418                     + "-nousagestats\tTurn off google analytics tracking for this session.\n"
1419                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
1420                     // +
1421                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
1422                     // after all other properties files have been read\n\t
1423                     // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
1424                     // passed in correctly)"
1425                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
1426                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
1427                     + "-groovy FILE\tExecute groovy script in FILE, after all other arguments have been processed (if FILE is the text 'STDIN' then the file will be read from STDIN)\n"
1428                     + "-jvmmempc=PERCENT\tOnly available with standalone executable jar or jalview.bin.Launcher. Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected. See https://www.jalview.org/help/html/memory.html for more details.\n"
1429                     + "-jvmmemmax=MAXMEMORY\tOnly available with standalone executable jar or jalview.bin.Launcher. Limit maximum heap size (memory) to MAXMEMORY. MAXMEMORY can be specified in bytes, kilobytes(k), megabytes(m), gigabytes(g) or if you're lucky enough, terabytes(t). This defaults to 32g if total physical memory can be detected, or to 8g if total physical memory cannot be detected. See https://www.jalview.org/help/html/memory.html for more details.\n"
1430                     + "\n~Read documentation in Application or visit https://www.jalview.org for description of Features and Annotations file~\n\n");
1431   }
1432
1433   private static void startUsageStats(final Desktop desktop)
1434   {
1435     /**
1436      * start a User Config prompt asking if we can log usage statistics.
1437      */
1438     PromptUserConfig prompter = new PromptUserConfig(Desktop.getDesktopPane(),
1439             "USAGESTATS", "Jalview Usage Statistics",
1440             "Do you want to help make Jalview better by enabling "
1441                     + "the collection of usage statistics with Google Analytics ?"
1442                     + "\n\n(you can enable or disable usage tracking in the preferences)",
1443             new Runnable()
1444             {
1445               @Override
1446               public void run()
1447               {
1448                 Console.debug(
1449                         "Initialising googletracker for usage stats.");
1450                 Cache.initGoogleTracker();
1451                 Console.debug("Tracking enabled.");
1452               }
1453             }, new Runnable()
1454             {
1455               @Override
1456               public void run()
1457               {
1458                 Console.debug("Not enabling Google Tracking.");
1459               }
1460             }, null, true);
1461     desktop.addDialogThread(prompter);
1462   }
1463
1464   /**
1465    * Locate the given string as a file and pass it to the groovy interpreter.
1466    * 
1467    * @param groovyscript
1468    *          the script to execute
1469    * @param jalviewContext
1470    *          the Jalview Desktop object passed in to the groovy binding as the
1471    *          'Jalview' object.
1472    */
1473   private void executeGroovyScript(String groovyscript, AlignFrame af)
1474   {
1475     /**
1476      * for scripts contained in files
1477      */
1478     File tfile = null;
1479     /**
1480      * script's URI
1481      */
1482     URL sfile = null;
1483     if (groovyscript.trim().equals("STDIN"))
1484     {
1485       // read from stdin into a tempfile and execute it
1486       try
1487       {
1488         tfile = File.createTempFile("jalview", "groovy");
1489         PrintWriter outfile = new PrintWriter(
1490                 new OutputStreamWriter(new FileOutputStream(tfile)));
1491         BufferedReader br = new BufferedReader(
1492                 new InputStreamReader(System.in));
1493         String line = null;
1494         while ((line = br.readLine()) != null)
1495         {
1496           outfile.write(line + "\n");
1497         }
1498         br.close();
1499         outfile.flush();
1500         outfile.close();
1501
1502       } catch (Exception ex)
1503       {
1504         System.err.println("Failed to read from STDIN into tempfile "
1505                 + ((tfile == null) ? "(tempfile wasn't created)"
1506                         : tfile.toString()));
1507         ex.printStackTrace();
1508         return;
1509       }
1510       try
1511       {
1512         sfile = tfile.toURI().toURL();
1513       } catch (Exception x)
1514       {
1515         System.err.println(
1516                 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1517                         + tfile.toURI());
1518         x.printStackTrace();
1519         return;
1520       }
1521     }
1522     else
1523     {
1524       try
1525       {
1526         sfile = new URI(groovyscript).toURL();
1527       } catch (Exception x)
1528       {
1529         tfile = new File(groovyscript);
1530         if (!tfile.exists())
1531         {
1532           System.err.println("File '" + groovyscript + "' does not exist.");
1533           return;
1534         }
1535         if (!tfile.canRead())
1536         {
1537           System.err.println("File '" + groovyscript + "' cannot be read.");
1538           return;
1539         }
1540         if (tfile.length() < 1)
1541         {
1542           System.err.println("File '" + groovyscript + "' is empty.");
1543           return;
1544         }
1545         try
1546         {
1547           sfile = tfile.getAbsoluteFile().toURI().toURL();
1548         } catch (Exception ex)
1549         {
1550           System.err.println("Failed to create a file URL for "
1551                   + tfile.getAbsoluteFile());
1552           return;
1553         }
1554       }
1555     }
1556     try
1557     {
1558       Map<String, java.lang.Object> vbinding = new HashMap<>();
1559       vbinding.put("Jalview", this);
1560       if (af != null)
1561       {
1562         vbinding.put("currentAlFrame", af);
1563       }
1564       Binding gbinding = new Binding(vbinding);
1565       GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1566       gse.run(sfile.toString(), gbinding);
1567       if ("STDIN".equals(groovyscript))
1568       {
1569         // delete temp file that we made -
1570         // only if it was successfully executed
1571         tfile.delete();
1572       }
1573     } catch (Exception e)
1574     {
1575       System.err.println("Exception Whilst trying to execute file " + sfile
1576               + " as a groovy script.");
1577       e.printStackTrace(System.err);
1578
1579     }
1580   }
1581
1582   public static boolean isHeadlessMode()
1583   {
1584     String isheadless = System.getProperty("java.awt.headless");
1585     if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1586     {
1587       return true;
1588     }
1589     return false;
1590   }
1591
1592   public AlignFrame[] getAlignFrames()
1593   {
1594     return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1595             : Desktop.getAlignFrames();
1596
1597   }
1598
1599   /**
1600    * Quit method delegates to Desktop.quit - unless running in headless mode when
1601    * it just ends the JVM
1602    */
1603   public void quit()
1604   {
1605     if (desktop != null)
1606     {
1607       desktop.quit();
1608     }
1609     else
1610     {
1611       System.exit(0);
1612     }
1613   }
1614
1615   public static AlignFrame getCurrentAlignFrame()
1616   {
1617     return Jalview.getInstance().currentAlignFrame;
1618   }
1619
1620   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
1621   {
1622     Jalview.getInstance().currentAlignFrame = currentAlignFrame;
1623   }
1624   
1625   public void notifyWorker(AlignCalcWorkerI worker, String status)
1626   {
1627     // System.out.println("Jalview worker " + worker.getClass().getSimpleName()
1628     // + " " + status);
1629   }
1630
1631
1632   private static boolean isInteractive = true;
1633
1634   public static boolean isInteractive()
1635   {
1636     return isInteractive;
1637   }
1638
1639   public static void setInteractive(boolean tf)
1640   {
1641     isInteractive = tf;
1642   }
1643 }