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