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