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