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