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