JAL-629 parse SubVals once with ArgValue. Ensure SubVals exists. Change to setprops...
[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 (bootstrapArgs.contains(Arg.HELP))
453       {
454         System.out.println(Arg.usage());
455         Jalview.exit(null, 0);
456       }
457       if (aparser.contains("help") || aparser.contains("h"))
458       {
459         showUsage();
460         Jalview.exit(null, 0);
461       }
462
463       if (bootstrapArgs.contains(Arg.HEADLESS))
464       {
465         System.setProperty("java.awt.headless", "true");
466         // new
467         headlessArg = argparser.getBool(Arg.HEADLESS);
468       }
469       if (aparser.contains("nodisplay") || aparser.contains("nogui")
470               || aparser.contains("headless"))
471       {
472         System.setProperty("java.awt.headless", "true");
473         // old
474         headless = true;
475       }
476       // anything else!
477
478       // allow https handshakes to download intermediate certs if necessary
479       System.setProperty("com.sun.security.enableAIAcaIssuers", "true");
480
481       final String jabawsUrl = aparser.getValue("jabaws");
482       if (jabawsUrl != null)
483       {
484         try
485         {
486           Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl);
487           System.out.println(
488                   "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
489         } catch (MalformedURLException e)
490         {
491           System.err.println(
492                   "Invalid jabaws parameter: " + jabawsUrl + " ignored");
493         }
494       }
495     }
496
497     List<String> setprops = new ArrayList<>();
498     if (bootstrapArgs.contains(Arg.SETPROP))
499     {
500       setprops = bootstrapArgs.getList(Arg.SETPROP);
501     }
502     else
503     {
504       String sp = aparser.getValue("setprop");
505       while (sp != null)
506       {
507         setprops.add(sp);
508         sp = aparser.getValue("setprop");
509       }
510     }
511     for (String setprop : setprops)
512     {
513       int p = setprop.indexOf('=');
514       if (p == -1)
515       {
516         System.err
517                 .println("Ignoring invalid setprop argument : " + setprop);
518       }
519       else
520       {
521         System.out.println("Executing setprop argument: " + setprop);
522         if (Platform.isJS())
523         {
524           Cache.setProperty(setprop.substring(0, p),
525                   setprop.substring(p + 1));
526         }
527         // DISABLED FOR SECURITY REASONS
528         // TODO: add a property to allow properties to be overriden by cli args
529         // Cache.setProperty(setprop.substring(0,p), setprop.substring(p+1));
530       }
531     }
532     if (System.getProperty("java.awt.headless") != null
533             && System.getProperty("java.awt.headless").equals("true"))
534     {
535       headless = true;
536     }
537     System.setProperty("http.agent",
538             "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown"));
539
540     try
541     {
542       Console.initLogger();
543     } catch (
544
545     NoClassDefFoundError error)
546     {
547       error.printStackTrace();
548       String message = "\nEssential logging libraries not found."
549               + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview";
550       Jalview.exit(message, 0);
551     }
552     desktop = null;
553
554     if (!(headless || headlessArg))
555       setLookAndFeel();
556
557     /*
558      * configure 'full' SO model if preferences say to, else use the default (full SO)
559      * - as JS currently doesn't have OBO parsing, it must use 'Lite' version
560      */
561     boolean soDefault = !Platform.isJS();
562     if (Cache.getDefault("USE_FULL_SO", soDefault))
563     {
564       SequenceOntologyFactory.setInstance(new SequenceOntology());
565     }
566
567     if (!(headless || headlessArg))
568     {
569       Desktop.nosplash = "false".equals(bootstrapArgs.get(Arg.SPLASH))
570               || aparser.contains("nosplash");
571       desktop = new Desktop();
572       desktop.setInBatchMode(true); // indicate we are starting up
573
574       try
575       {
576         JalviewTaskbar.setTaskbar(this);
577       } catch (Exception e)
578       {
579         Console.info("Cannot set Taskbar");
580         Console.error(e.getMessage());
581         // e.printStackTrace();
582       } catch (Throwable t)
583       {
584         Console.info("Cannot set Taskbar");
585         Console.error(t.getMessage());
586         // t.printStackTrace();
587       }
588
589       // set Proxy settings before all the internet calls
590       Cache.setProxyPropertiesFromPreferences();
591
592       desktop.setVisible(true);
593
594       if (!Platform.isJS())
595       /**
596        * Java only
597        * 
598        * @j2sIgnore
599        */
600       {
601
602         /**
603          * Check to see that the JVM version being run is suitable for the Java
604          * version this Jalview was compiled for. Popup a warning if not.
605          */
606         if (!LaunchUtils.checkJavaVersion())
607         {
608           Console.warn("The Java version being used (Java "
609                   + LaunchUtils.getJavaVersion()
610                   + ") may lead to problems. This installation of Jalview should be used with Java "
611                   + LaunchUtils.getJavaCompileVersion() + ".");
612
613           if (!LaunchUtils
614                   .getBooleanUserPreference("IGNORE_JVM_WARNING_POPUP"))
615           {
616             Object[] options = {
617                 MessageManager.getString("label.continue") };
618             JOptionPane.showOptionDialog(null,
619                     MessageManager.formatMessage(
620                             "warning.wrong_jvm_version_message",
621                             LaunchUtils.getJavaVersion(),
622                             LaunchUtils.getJavaCompileVersion()),
623                     MessageManager
624                             .getString("warning.wrong_jvm_version_title"),
625                     JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
626                     null, options, options[0]);
627           }
628         }
629
630         if (!aparser.contains("nowebservicediscovery"))
631         {
632           desktop.startServiceDiscovery();
633         }
634         if (!aparser.contains("nousagestats"))
635         {
636           startUsageStats(desktop);
637         }
638         else
639         {
640           System.err.println("CMD [-nousagestats] executed successfully!");
641         }
642
643         if (!aparser.contains("noquestionnaire"))
644         {
645           String url = aparser.getValue("questionnaire");
646           if (url != null)
647           {
648             // Start the desktop questionnaire prompter with the specified
649             // questionnaire
650             Console.debug("Starting questionnaire url at " + url);
651             desktop.checkForQuestionnaire(url);
652             System.out.println("CMD questionnaire[-" + url
653                     + "] executed successfully!");
654           }
655           else
656           {
657             if (Cache.getProperty("NOQUESTIONNAIRES") == null)
658             {
659               // Start the desktop questionnaire prompter with the specified
660               // questionnaire
661               // String defurl =
662               // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
663               // //
664               String defurl = "https://www.jalview.org/cgi-bin/questionnaire.pl";
665               Console.debug(
666                       "Starting questionnaire with default url: " + defurl);
667               desktop.checkForQuestionnaire(defurl);
668             }
669           }
670         }
671         else
672         {
673           System.err
674                   .println("CMD [-noquestionnaire] executed successfully!");
675         }
676
677         if ((!aparser.contains("nonews")
678                 && Cache.getProperty("NONEWS") == null
679                 && !"false".equals(bootstrapArgs.get(Arg.NEWS)))
680                 || "true".equals(bootstrapArgs.get(Arg.NEWS)))
681         {
682           desktop.checkForNews();
683         }
684
685         if (!aparser.contains("nohtmltemplates")
686                 && Cache.getProperty("NOHTMLTEMPLATES") == null)
687         {
688           BioJsHTMLOutput.updateBioJS();
689         }
690       }
691     }
692     // Run Commands from cli
693     cmds = new Commands(argparser, headlessArg);
694     boolean commandsSuccess = cmds.argsWereParsed();
695     if (commandsSuccess)
696     {
697       if (headlessArg)
698       {
699         Jalview.exit("Successfully completed commands in headless mode", 0);
700       }
701       Console.info("Successfully completed commands");
702     }
703     else
704     {
705       if (headlessArg)
706       {
707         Jalview.exit("Error when running Commands in headless mode", 1);
708       }
709       Console.warn("Error when running commands");
710     }
711
712     // Check if JVM and compile version might cause problems and log if it
713     // might.
714     if (headless && !Platform.isJS() && !LaunchUtils.checkJavaVersion())
715     {
716       Console.warn("The Java version being used (Java "
717               + LaunchUtils.getJavaVersion()
718               + ") may lead to problems. This installation of Jalview should be used with Java "
719               + LaunchUtils.getJavaCompileVersion() + ".");
720     }
721
722     String file = null, data = null;
723
724     FileFormatI format = null;
725
726     DataSourceType protocol = null;
727
728     FileLoader fileLoader = new FileLoader(!headless);
729
730     String groovyscript = null; // script to execute after all loading is
731     // completed one way or another
732     // extract groovy argument and execute if necessary
733     groovyscript = aparser.getValue("groovy", true);
734     file = aparser.getValue("open", true);
735
736     if (file == null && desktop == null && !commandsSuccess)
737     {
738       Jalview.exit("No files to open!", 1);
739     }
740
741     long progress = -1;
742     // Finally, deal with the remaining input data.
743     if (file != null)
744     {
745       if (!headless)
746       {
747         desktop.setProgressBar(
748                 MessageManager
749                         .getString("status.processing_commandline_args"),
750                 progress = System.currentTimeMillis());
751       }
752       System.out.println("CMD [-open " + file + "] executed successfully!");
753
754       if (!Platform.isJS())
755       /**
756        * ignore in JavaScript -- can't just file existence - could load it?
757        * 
758        * @j2sIgnore
759        */
760       {
761         if (!HttpUtils.startsWithHttpOrHttps(file))
762         {
763           if (!(new File(file)).exists())
764           {
765             if (headless)
766             {
767               Jalview.exit(
768                       "Can't find file '" + file + "' in headless mode", 1);
769             }
770             Console.warn("Can't find file'" + file + "'");
771           }
772         }
773       }
774
775       protocol = AppletFormatAdapter.checkProtocol(file);
776
777       try
778       {
779         format = new IdentifyFile().identify(file, protocol);
780       } catch (FileFormatException e1)
781       {
782         // TODO ?
783       }
784
785       AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
786               format);
787       if (af == null)
788       {
789         System.out.println("error");
790       }
791       else
792       {
793         setCurrentAlignFrame(af);
794         data = aparser.getValue("colour", true);
795         if (data != null)
796         {
797           data.replaceAll("%20", " ");
798
799           ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
800                   af.getViewport(), af.getViewport().getAlignment(), data);
801
802           if (cs != null)
803           {
804             System.out.println(
805                     "CMD [-colour " + data + "] executed successfully!");
806           }
807           af.changeColour(cs);
808         }
809
810         // Must maintain ability to use the groups flag
811         data = aparser.getValue("groups", true);
812         if (data != null)
813         {
814           af.parseFeaturesFile(data,
815                   AppletFormatAdapter.checkProtocol(data));
816           // System.out.println("Added " + data);
817           System.out.println(
818                   "CMD groups[-" + data + "]  executed successfully!");
819         }
820         data = aparser.getValue("features", true);
821         if (data != null)
822         {
823           af.parseFeaturesFile(data,
824                   AppletFormatAdapter.checkProtocol(data));
825           // System.out.println("Added " + data);
826           System.out.println(
827                   "CMD [-features " + data + "]  executed successfully!");
828         }
829
830         data = aparser.getValue("annotations", true);
831         if (data != null)
832         {
833           af.loadJalviewDataFile(data, null, null, null);
834           // System.out.println("Added " + data);
835           System.out.println(
836                   "CMD [-annotations " + data + "] executed successfully!");
837         }
838         // set or clear the sortbytree flag.
839         if (aparser.contains("sortbytree"))
840         {
841           af.getViewport().setSortByTree(true);
842           if (af.getViewport().getSortByTree())
843           {
844             System.out.println("CMD [-sortbytree] executed successfully!");
845           }
846         }
847         if (aparser.contains("no-annotation"))
848         {
849           af.getViewport().setShowAnnotation(false);
850           if (!af.getViewport().isShowAnnotation())
851           {
852             System.out.println("CMD no-annotation executed successfully!");
853           }
854         }
855         if (aparser.contains("nosortbytree"))
856         {
857           af.getViewport().setSortByTree(false);
858           if (!af.getViewport().getSortByTree())
859           {
860             System.out
861                     .println("CMD [-nosortbytree] executed successfully!");
862           }
863         }
864         data = aparser.getValue("tree", true);
865         if (data != null)
866         {
867           try
868           {
869             System.out.println(
870                     "CMD [-tree " + data + "] executed successfully!");
871             NewickFile nf = new NewickFile(data,
872                     AppletFormatAdapter.checkProtocol(data));
873             af.getViewport()
874                     .setCurrentTree(af.showNewickTree(nf, data).getTree());
875           } catch (IOException ex)
876           {
877             System.err.println("Couldn't add tree " + data);
878             ex.printStackTrace(System.err);
879           }
880         }
881         // TODO - load PDB structure(s) to alignment JAL-629
882         // (associate with identical sequence in alignment, or a specified
883         // sequence)
884         if (groovyscript != null)
885         {
886           // Execute the groovy script after we've done all the rendering stuff
887           // and before any images or figures are generated.
888           System.out.println("Executing script " + groovyscript);
889           executeGroovyScript(groovyscript, af);
890           System.out.println("CMD groovy[" + groovyscript
891                   + "] executed successfully!");
892           groovyscript = null;
893         }
894         String imageName = "unnamed.png";
895         while (aparser.getSize() > 1)
896         {
897           String outputFormat = aparser.nextValue();
898           file = aparser.nextValue();
899
900           if (outputFormat.equalsIgnoreCase("png"))
901           {
902             af.createPNG(new File(file));
903             imageName = (new File(file)).getName();
904             System.out.println("Creating PNG image: " + file);
905             continue;
906           }
907           else if (outputFormat.equalsIgnoreCase("svg"))
908           {
909             File imageFile = new File(file);
910             imageName = imageFile.getName();
911             af.createSVG(imageFile);
912             System.out.println("Creating SVG image: " + file);
913             continue;
914           }
915           else if (outputFormat.equalsIgnoreCase("html"))
916           {
917             File imageFile = new File(file);
918             imageName = imageFile.getName();
919             HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
920             htmlSVG.exportHTML(file);
921
922             System.out.println("Creating HTML image: " + file);
923             continue;
924           }
925           else if (outputFormat.equalsIgnoreCase("biojsmsa"))
926           {
927             if (file == null)
928             {
929               System.err.println("The output html file must not be null");
930               return;
931             }
932             try
933             {
934               BioJsHTMLOutput.refreshVersionInfo(
935                       BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
936             } catch (URISyntaxException e)
937             {
938               e.printStackTrace();
939             }
940             BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
941             bjs.exportHTML(file);
942             System.out
943                     .println("Creating BioJS MSA Viwer HTML file: " + file);
944             continue;
945           }
946           else if (outputFormat.equalsIgnoreCase("imgMap"))
947           {
948             af.createImageMap(new File(file), imageName);
949             System.out.println("Creating image map: " + file);
950             continue;
951           }
952           else if (outputFormat.equalsIgnoreCase("eps"))
953           {
954             File outputFile = new File(file);
955             System.out.println(
956                     "Creating EPS file: " + outputFile.getAbsolutePath());
957             af.createEPS(outputFile);
958             continue;
959           }
960           FileFormatI outFormat = null;
961           try
962           {
963             outFormat = FileFormats.getInstance().forName(outputFormat);
964           } catch (Exception formatP)
965           {
966             System.out.println("Couldn't parse " + outFormat
967                     + " as a valid Jalview format string.");
968           }
969           if (outFormat != null)
970           {
971             if (!outFormat.isWritable())
972             {
973               System.out.println(
974                       "This version of Jalview does not support alignment export as "
975                               + outputFormat);
976             }
977             else
978             {
979               af.saveAlignment(file, outFormat);
980               if (af.isSaveAlignmentSuccessful())
981               {
982                 System.out.println("Written alignment in "
983                         + outFormat.getName() + " format to " + file);
984               }
985               else
986               {
987                 System.out.println("Error writing file " + file + " in "
988                         + outFormat.getName() + " format!!");
989               }
990             }
991           }
992
993         }
994
995         while (aparser.getSize() > 0)
996         {
997           System.out.println("Unknown arg: " + aparser.nextValue());
998         }
999       }
1000     }
1001
1002     AlignFrame startUpAlframe = null;
1003     // We'll only open the default file if the desktop is visible.
1004     // And the user
1005     // ////////////////////
1006
1007     if (!Platform.isJS() && !headless && file == null
1008             && Cache.getDefault("SHOW_STARTUP_FILE", true)
1009             && !cmds.commandArgsProvided())
1010     // don't open the startup file if command line args have been processed
1011     // (&& !Commands.commandArgsProvided())
1012     /**
1013      * Java only
1014      * 
1015      * @j2sIgnore
1016      */
1017     {
1018       file = Cache.getDefault("STARTUP_FILE",
1019               Cache.getDefault("www.jalview.org", "https://www.jalview.org")
1020                       + "/examples/exampleFile_2_7.jvp");
1021       if (file.equals("http://www.jalview.org/examples/exampleFile_2_3.jar")
1022               || file.equals(
1023                       "http://www.jalview.org/examples/exampleFile_2_7.jar"))
1024       {
1025         file.replace("http:", "https:");
1026         // hardwire upgrade of the startup file
1027         file.replace("_2_3", "_2_7");
1028         file.replace("2_7.jar", "2_7.jvp");
1029         // and remove the stale setting
1030         Cache.removeProperty("STARTUP_FILE");
1031       }
1032
1033       protocol = AppletFormatAdapter.checkProtocol(file);
1034
1035       if (file.endsWith(".jar"))
1036       {
1037         format = FileFormat.Jalview;
1038       }
1039       else
1040       {
1041         try
1042         {
1043           format = new IdentifyFile().identify(file, protocol);
1044         } catch (FileFormatException e)
1045         {
1046           // TODO what?
1047         }
1048       }
1049
1050       startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
1051               format);
1052       // don't ask to save when quitting if only the startup file has been
1053       // opened
1054       Console.debug("Resetting up-to-date flag for startup file");
1055       startUpAlframe.getViewport().setSavedUpToDate(true);
1056       // extract groovy arguments before anything else.
1057     }
1058
1059     // Once all other stuff is done, execute any groovy scripts (in order)
1060     if (groovyscript != null)
1061     {
1062       if (Cache.groovyJarsPresent())
1063       {
1064         System.out.println("Executing script " + groovyscript);
1065         executeGroovyScript(groovyscript, startUpAlframe);
1066       }
1067       else
1068       {
1069         System.err.println(
1070                 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
1071                         + groovyscript);
1072       }
1073     }
1074     // and finally, turn off batch mode indicator - if the desktop still exists
1075     if (desktop != null)
1076     {
1077       if (progress != -1)
1078       {
1079         desktop.setProgressBar(null, progress);
1080       }
1081       desktop.setInBatchMode(false);
1082     }
1083   }
1084
1085   private static void setLookAndFeel()
1086   {
1087     // property laf = "crossplatform", "system", "gtk", "metal", "nimbus",
1088     // "mac" or "flat"
1089     // If not set (or chosen laf fails), use the normal SystemLaF and if on Mac,
1090     // try Quaqua/Vaqua.
1091     String lafProp = System.getProperty("laf");
1092     String lafSetting = Cache.getDefault("PREFERRED_LAF", null);
1093     String laf = "none";
1094     if (lafProp != null)
1095     {
1096       laf = lafProp;
1097     }
1098     else if (lafSetting != null)
1099     {
1100       laf = lafSetting;
1101     }
1102     boolean lafSet = false;
1103     switch (laf)
1104     {
1105     case "crossplatform":
1106       lafSet = setCrossPlatformLookAndFeel();
1107       if (!lafSet)
1108       {
1109         Console.error("Could not set requested laf=" + laf);
1110       }
1111       break;
1112     case "system":
1113       lafSet = setSystemLookAndFeel();
1114       if (!lafSet)
1115       {
1116         Console.error("Could not set requested laf=" + laf);
1117       }
1118       break;
1119     case "gtk":
1120       lafSet = setGtkLookAndFeel();
1121       if (!lafSet)
1122       {
1123         Console.error("Could not set requested laf=" + laf);
1124       }
1125       break;
1126     case "metal":
1127       lafSet = setMetalLookAndFeel();
1128       if (!lafSet)
1129       {
1130         Console.error("Could not set requested laf=" + laf);
1131       }
1132       break;
1133     case "nimbus":
1134       lafSet = setNimbusLookAndFeel();
1135       if (!lafSet)
1136       {
1137         Console.error("Could not set requested laf=" + laf);
1138       }
1139       break;
1140     case "flat":
1141       lafSet = setFlatLookAndFeel();
1142       if (!lafSet)
1143       {
1144         Console.error("Could not set requested laf=" + laf);
1145       }
1146       break;
1147     case "mac":
1148       lafSet = setMacLookAndFeel();
1149       if (!lafSet)
1150       {
1151         Console.error("Could not set requested laf=" + laf);
1152       }
1153       break;
1154     case "none":
1155       break;
1156     default:
1157       Console.error("Requested laf=" + laf + " not implemented");
1158     }
1159     if (!lafSet)
1160     {
1161       setSystemLookAndFeel();
1162       if (Platform.isLinux())
1163       {
1164         setLinuxLookAndFeel();
1165       }
1166       if (Platform.isMac())
1167       {
1168         setMacLookAndFeel();
1169       }
1170     }
1171   }
1172
1173   private static boolean setCrossPlatformLookAndFeel()
1174   {
1175     boolean set = false;
1176     try
1177     {
1178       UIManager.setLookAndFeel(
1179               UIManager.getCrossPlatformLookAndFeelClassName());
1180       set = true;
1181     } catch (Exception ex)
1182     {
1183       Console.error("Unexpected Look and Feel Exception");
1184       Console.error(ex.getMessage());
1185       Console.debug(Cache.getStackTraceString(ex));
1186     }
1187     return set;
1188   }
1189
1190   private static boolean setSystemLookAndFeel()
1191   {
1192     boolean set = false;
1193     try
1194     {
1195       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
1196       set = true;
1197     } catch (Exception ex)
1198     {
1199       Console.error("Unexpected Look and Feel Exception");
1200       Console.error(ex.getMessage());
1201       Console.debug(Cache.getStackTraceString(ex));
1202     }
1203     return set;
1204   }
1205
1206   private static boolean setSpecificLookAndFeel(String name,
1207           String className, boolean nameStartsWith)
1208   {
1209     boolean set = false;
1210     try
1211     {
1212       for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
1213       {
1214         if (info.getName() != null && nameStartsWith
1215                 ? info.getName().toLowerCase(Locale.ROOT)
1216                         .startsWith(name.toLowerCase(Locale.ROOT))
1217                 : info.getName().toLowerCase(Locale.ROOT)
1218                         .equals(name.toLowerCase(Locale.ROOT)))
1219         {
1220           className = info.getClassName();
1221           break;
1222         }
1223       }
1224       UIManager.setLookAndFeel(className);
1225       set = true;
1226     } catch (Exception ex)
1227     {
1228       Console.error("Unexpected Look and Feel Exception");
1229       Console.error(ex.getMessage());
1230       Console.debug(Cache.getStackTraceString(ex));
1231     }
1232     return set;
1233   }
1234
1235   private static boolean setGtkLookAndFeel()
1236   {
1237     return setSpecificLookAndFeel("gtk",
1238             "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true);
1239   }
1240
1241   private static boolean setMetalLookAndFeel()
1242   {
1243     return setSpecificLookAndFeel("metal",
1244             "javax.swing.plaf.metal.MetalLookAndFeel", false);
1245   }
1246
1247   private static boolean setNimbusLookAndFeel()
1248   {
1249     return setSpecificLookAndFeel("nimbus",
1250             "javax.swing.plaf.nimbus.NimbusLookAndFeel", false);
1251   }
1252
1253   private static boolean setFlatLookAndFeel()
1254   {
1255     boolean set = false;
1256     if (SystemInfo.isMacOS)
1257     {
1258       try
1259       {
1260         UIManager.setLookAndFeel(
1261                 "com.formdev.flatlaf.themes.FlatMacLightLaf");
1262         set = true;
1263         Console.debug("Using FlatMacLightLaf");
1264       } catch (ClassNotFoundException | InstantiationException
1265               | IllegalAccessException | UnsupportedLookAndFeelException e)
1266       {
1267         Console.debug("Exception loading FlatLightLaf", e);
1268       }
1269       System.setProperty("apple.laf.useScreenMenuBar", "true");
1270       System.setProperty("apple.awt.application.name",
1271               ChannelProperties.getProperty("app_name"));
1272       System.setProperty("apple.awt.application.appearance", "system");
1273       if (SystemInfo.isMacFullWindowContentSupported
1274               && Desktop.desktop != null)
1275       {
1276         Console.debug("Setting transparent title bar");
1277         Desktop.desktop.getRootPane()
1278                 .putClientProperty("apple.awt.fullWindowContent", true);
1279         Desktop.desktop.getRootPane()
1280                 .putClientProperty("apple.awt.transparentTitleBar", true);
1281         Desktop.desktop.getRootPane()
1282                 .putClientProperty("apple.awt.fullscreenable", true);
1283       }
1284       SwingUtilities.invokeLater(() -> {
1285         FlatMacLightLaf.setup();
1286       });
1287       Console.debug("Using FlatMacLightLaf");
1288       set = true;
1289     }
1290     if (!set)
1291     {
1292       try
1293       {
1294         UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1295         set = true;
1296         Console.debug("Using FlatLightLaf");
1297       } catch (ClassNotFoundException | InstantiationException
1298               | IllegalAccessException | UnsupportedLookAndFeelException e)
1299       {
1300         Console.debug("Exception loading FlatLightLaf", e);
1301       }
1302       // Windows specific properties here
1303       SwingUtilities.invokeLater(() -> {
1304         FlatLightLaf.setup();
1305       });
1306       Console.debug("Using FlatLightLaf");
1307       set = true;
1308     }
1309     else if (SystemInfo.isLinux)
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       // enable custom window decorations
1322       JFrame.setDefaultLookAndFeelDecorated(true);
1323       JDialog.setDefaultLookAndFeelDecorated(true);
1324       SwingUtilities.invokeLater(() -> {
1325         FlatLightLaf.setup();
1326       });
1327       Console.debug("Using FlatLightLaf");
1328       set = true;
1329     }
1330
1331     if (!set)
1332     {
1333       try
1334       {
1335         UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1336         set = true;
1337         Console.debug("Using FlatLightLaf");
1338       } catch (ClassNotFoundException | InstantiationException
1339               | IllegalAccessException | UnsupportedLookAndFeelException e)
1340       {
1341         Console.debug("Exception loading FlatLightLaf", e);
1342       }
1343     }
1344
1345     if (set)
1346     {
1347       UIManager.put("TabbedPane.tabType", "card");
1348       UIManager.put("TabbedPane.showTabSeparators", true);
1349       UIManager.put("TabbedPane.showContentSeparator", true);
1350       UIManager.put("TabbedPane.tabSeparatorsFullHeight", true);
1351       UIManager.put("TabbedPane.tabsOverlapBorder", true);
1352       UIManager.put("TabbedPane.hasFullBorder", true);
1353       UIManager.put("TabbedPane.tabLayoutPolicy", "scroll");
1354       UIManager.put("TabbedPane.scrollButtonsPolicy", "asNeeded");
1355       UIManager.put("TabbedPane.smoothScrolling", true);
1356       UIManager.put("TabbedPane.tabWidthMode", "compact");
1357       UIManager.put("TabbedPane.selectedBackground", Color.white);
1358     }
1359
1360     Desktop.setLiveDragMode(Cache.getDefault("FLAT_LIVE_DRAG_MODE", true));
1361     return set;
1362   }
1363
1364   private static boolean setMacLookAndFeel()
1365   {
1366     boolean set = false;
1367     System.setProperty("com.apple.mrj.application.apple.menu.about.name",
1368             ChannelProperties.getProperty("app_name"));
1369     System.setProperty("apple.laf.useScreenMenuBar", "true");
1370     /*
1371      * broken native LAFs on (ARM?) macbooks
1372     set = setQuaquaLookAndFeel();
1373     if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
1374             .toLowerCase(Locale.ROOT).contains("quaqua"))
1375     {
1376       set = setVaquaLookAndFeel();
1377     }
1378      */
1379     set = setFlatLookAndFeel();
1380     return set;
1381   }
1382
1383   private static boolean setLinuxLookAndFeel()
1384   {
1385     boolean set = false;
1386     set = setFlatLookAndFeel();
1387     if (!set)
1388       set = setMetalLookAndFeel();
1389     // avoid GtkLookAndFeel -- not good results especially on HiDPI
1390     if (!set)
1391       set = setNimbusLookAndFeel();
1392     return set;
1393   }
1394
1395   private static void showUsage()
1396   {
1397     System.out.println(
1398             "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
1399                     + "-nodisplay\tRun Jalview without User Interface.\n"
1400                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
1401                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
1402                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
1403                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
1404                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
1405                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
1406                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
1407                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
1408                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
1409                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
1410                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
1411                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
1412                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
1413                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
1414                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
1415                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
1416                     + "-html FILE\tCreate HTML file from alignment.\n"
1417                     + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
1418                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
1419                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
1420                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
1421                     + "-noquestionnaire\tTurn off questionnaire check.\n"
1422                     + "-nonews\tTurn off check for Jalview news.\n"
1423                     + "-nousagestats\tTurn off google analytics tracking for this session.\n"
1424                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
1425                     // +
1426                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
1427                     // after all other properties files have been read\n\t
1428                     // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
1429                     // passed in correctly)"
1430                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
1431                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
1432                     + "-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"
1433                     + "-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"
1434                     + "-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"
1435                     + "\n~Read documentation in Application or visit https://www.jalview.org for description of Features and Annotations file~\n\n");
1436   }
1437
1438   private static void startUsageStats(final Desktop desktop)
1439   {
1440     /**
1441      * start a User Config prompt asking if we can log usage statistics.
1442      */
1443     PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
1444             "USAGESTATS", "Jalview Usage Statistics",
1445             "Do you want to help make Jalview better by enabling "
1446                     + "the collection of usage statistics with Google Analytics ?"
1447                     + "\n\n(you can enable or disable usage tracking in the preferences)",
1448             new Runnable()
1449             {
1450               @Override
1451               public void run()
1452               {
1453                 Console.debug(
1454                         "Initialising googletracker for usage stats.");
1455                 Cache.initGoogleTracker();
1456                 Console.debug("Tracking enabled.");
1457               }
1458             }, new Runnable()
1459             {
1460               @Override
1461               public void run()
1462               {
1463                 Console.debug("Not enabling Google Tracking.");
1464               }
1465             }, null, true);
1466     desktop.addDialogThread(prompter);
1467   }
1468
1469   /**
1470    * Locate the given string as a file and pass it to the groovy interpreter.
1471    * 
1472    * @param groovyscript
1473    *          the script to execute
1474    * @param jalviewContext
1475    *          the Jalview Desktop object passed in to the groovy binding as the
1476    *          'Jalview' object.
1477    */
1478   private void executeGroovyScript(String groovyscript, AlignFrame af)
1479   {
1480     /**
1481      * for scripts contained in files
1482      */
1483     File tfile = null;
1484     /**
1485      * script's URI
1486      */
1487     URL sfile = null;
1488     if (groovyscript.trim().equals("STDIN"))
1489     {
1490       // read from stdin into a tempfile and execute it
1491       try
1492       {
1493         tfile = File.createTempFile("jalview", "groovy");
1494         PrintWriter outfile = new PrintWriter(
1495                 new OutputStreamWriter(new FileOutputStream(tfile)));
1496         BufferedReader br = new BufferedReader(
1497                 new InputStreamReader(System.in));
1498         String line = null;
1499         while ((line = br.readLine()) != null)
1500         {
1501           outfile.write(line + "\n");
1502         }
1503         br.close();
1504         outfile.flush();
1505         outfile.close();
1506
1507       } catch (Exception ex)
1508       {
1509         System.err.println("Failed to read from STDIN into tempfile "
1510                 + ((tfile == null) ? "(tempfile wasn't created)"
1511                         : tfile.toString()));
1512         ex.printStackTrace();
1513         return;
1514       }
1515       try
1516       {
1517         sfile = tfile.toURI().toURL();
1518       } catch (Exception x)
1519       {
1520         System.err.println(
1521                 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1522                         + tfile.toURI());
1523         x.printStackTrace();
1524         return;
1525       }
1526     }
1527     else
1528     {
1529       try
1530       {
1531         sfile = new URI(groovyscript).toURL();
1532       } catch (Exception x)
1533       {
1534         tfile = new File(groovyscript);
1535         if (!tfile.exists())
1536         {
1537           System.err.println("File '" + groovyscript + "' does not exist.");
1538           return;
1539         }
1540         if (!tfile.canRead())
1541         {
1542           System.err.println("File '" + groovyscript + "' cannot be read.");
1543           return;
1544         }
1545         if (tfile.length() < 1)
1546         {
1547           System.err.println("File '" + groovyscript + "' is empty.");
1548           return;
1549         }
1550         try
1551         {
1552           sfile = tfile.getAbsoluteFile().toURI().toURL();
1553         } catch (Exception ex)
1554         {
1555           System.err.println("Failed to create a file URL for "
1556                   + tfile.getAbsoluteFile());
1557           return;
1558         }
1559       }
1560     }
1561     try
1562     {
1563       Map<String, java.lang.Object> vbinding = new HashMap<>();
1564       vbinding.put("Jalview", this);
1565       if (af != null)
1566       {
1567         vbinding.put("currentAlFrame", af);
1568       }
1569       Binding gbinding = new Binding(vbinding);
1570       GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1571       gse.run(sfile.toString(), gbinding);
1572       if ("STDIN".equals(groovyscript))
1573       {
1574         // delete temp file that we made -
1575         // only if it was successfully executed
1576         tfile.delete();
1577       }
1578     } catch (Exception e)
1579     {
1580       System.err.println("Exception Whilst trying to execute file " + sfile
1581               + " as a groovy script.");
1582       e.printStackTrace(System.err);
1583
1584     }
1585   }
1586
1587   public static boolean isHeadlessMode()
1588   {
1589     String isheadless = System.getProperty("java.awt.headless");
1590     if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1591     {
1592       return true;
1593     }
1594     return false;
1595   }
1596
1597   public AlignFrame[] getAlignFrames()
1598   {
1599     return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1600             : Desktop.getAlignFrames();
1601
1602   }
1603
1604   /**
1605    * jalview.bin.Jalview.quit() will just run the non-GUI shutdownHook and exit
1606    */
1607   public void quit()
1608   {
1609     // System.exit will run the shutdownHook first
1610     Jalview.exit("Quitting now. Bye!", 0);
1611   }
1612
1613   public static AlignFrame getCurrentAlignFrame()
1614   {
1615     return Jalview.currentAlignFrame;
1616   }
1617
1618   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
1619   {
1620     Jalview.currentAlignFrame = currentAlignFrame;
1621   }
1622
1623   protected Commands getCommands()
1624   {
1625     return cmds;
1626   }
1627
1628   public static void exit(String message, int exitcode)
1629   {
1630     Console.debug("Using Jalview.exit");
1631     if (message != null)
1632       if (exitcode == 0)
1633         Console.info(message);
1634       else
1635         Console.error(message);
1636     if (exitcode > -1)
1637       System.exit(exitcode);
1638   }
1639 }