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