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