Clover icon

Coverage Report

  1. Project Clover database Wed Feb 4 2026 17:46:51 GMT
  2. Package jalview.bin

File Jalview.java

Code metrics

384
811
61
3
2,456
1,858
354
0.44
13.3
20.33
5.8
Syntax error detected in source file:
/srv/bamboo/bamboo-home/xml-data/build-dir/196609/JB-GDB744-JOB1/src/jalview/bin/Jalview.java
Comparison method violates its general contract!
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 .
19   * The Jalview Authors are detailed in the 'AUTHORS' file.
20   */
21  
22   package jalview.bin;
23  
24   import java.awt.Color;
25   import java.awt.GraphicsEnvironment;
26   import java.io.BufferedReader;
27   import java.io.File;
28   import java.io.FileNotFoundException;
29   import java.io.FileOutputStream;
30   import java.io.IOException;
31   import java.io.InputStreamReader;
32   import java.io.OutputStream;
33   import java.io.OutputStreamWriter;
34   import java.io.PrintStream;
35   import java.io.PrintWriter;
36   import java.net.MalformedURLException;
37   import java.net.URI;
38   import java.net.URISyntaxException;
39   import java.net.URL;
40   import java.security.AllPermission;
41   import java.security.CodeSource;
42   import java.security.PermissionCollection;
43   import java.security.Permissions;
44   import java.security.Policy;
45   import java.util.ArrayList;
46   import java.util.HashMap;
47   import java.util.List;
48   import java.util.Locale;
49   import java.util.Map;
50   import java.util.Properties;
51   import java.util.Vector;
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.api.AlignCalcWorkerI;
72   import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
73   import jalview.bin.argparser.Arg;
74   import jalview.bin.argparser.Arg.Opt;
75   import jalview.bin.argparser.Arg.Type;
76   import jalview.bin.argparser.ArgParser;
77   import jalview.bin.argparser.BootstrapArgs;
78   import jalview.bin.groovy.JalviewObject;
79   import jalview.bin.groovy.JalviewObjectI;
80   import jalview.ext.so.SequenceOntology;
81   import jalview.gui.AlignFrame;
82   import jalview.gui.AlignViewport;
83   import jalview.gui.Desktop;
84   import jalview.gui.JvOptionPane;
85   import jalview.gui.Preferences;
86   import jalview.gui.PromptUserConfig;
87   import jalview.gui.QuitHandler;
88   import jalview.gui.QuitHandler.QResponse;
89   import jalview.gui.StructureViewerBase;
90   import jalview.io.AppletFormatAdapter;
91   import jalview.io.BioJsHTMLOutput;
92   import jalview.io.DataSourceType;
93   import jalview.io.FileFormat;
94   import jalview.io.FileFormatException;
95   import jalview.io.FileFormatI;
96   import jalview.io.FileFormats;
97   import jalview.io.FileLoader;
98   import jalview.io.HtmlSvgOutput;
99   import jalview.io.IdentifyFile;
100   import jalview.io.NewickFile;
101   import jalview.io.exceptions.ImageOutputException;
102   import jalview.io.gff.SequenceOntologyFactory;
103   import jalview.schemes.ColourSchemeI;
104   import jalview.schemes.ColourSchemeProperty;
105   import jalview.util.ChannelProperties;
106   import jalview.util.HttpUtils;
107   import jalview.util.LaunchUtils;
108   import jalview.util.MessageManager;
109   import jalview.util.Platform;
110   import jalview.util.UserAgent;
111   import jalview.ws.jws2.Jws2Discoverer;
112  
113   /**
114   * Main class for Jalview Application
115   *
116   * start with: java -classpath "$PATH_TO_LIB$/*:$PATH_TO_CLASSES$" \
117   * jalview.bin.Jalview
118   *
119   * or on Windows: java -classpath "$PATH_TO_LIB$/*;$PATH_TO_CLASSES$" \
120   * jalview.bin.Jalview jalview.bin.Jalview
121   *
122   * (ensure -classpath arg is quoted to avoid shell expansion of '*' and do not
123   * embellish '*' to e.g. '*.jar')
124   *
125   * @author $author$
126   * @version $Revision$
127   */
128   public class Jalview implements JalviewObjectI, ApplicationSingletonI
129   {
130   // TODO JAL-4107 - does Jalview need to do both objects explicitly
131   // for testing those nasty messages you cannot ever find.
132   // static
133   // {
134   // System.setOut(new PrintStream(new ByteArrayOutputStream())
135   // {
136   // @Override
137   // public void println(Object o)
138   // {
139   // if (o != null)
140   // {
141   // System.err.println(o);
142   // }
143   // }
144   //
145   // });
146   // }
147   public static Jalview getInstance()
148   {
149   return ApplicationSingletonProvider.getInstance(Jalview.class);
150   }
151  
152   public static boolean instanceExists()
153   {
154   return ApplicationSingletonProvider.instanceExists(Jalview.class);
155   }
156  
157   private Jalview()
158   {
159   Platform.getURLCommandArguments();
160   Platform.addJ2SDirectDatabaseCall("https://www.jalview.org");
161   Platform.addJ2SDirectDatabaseCall("http://www.jalview.org");
162   Platform.addJ2SDirectDatabaseCall("http://www.compbio.dundee.ac.uk");
163   Platform.addJ2SDirectDatabaseCall("https://www.compbio.dundee.ac.uk");
164   }
165  
166   private boolean headless;
167  
168   private Desktop desktop;
169  
170   protected Commands cmds;
171  
172   public AlignFrame currentAlignFrame = null;
173  
174   private ArgParser argparser = null;
175  
176   private BootstrapArgs bootstrapArgs = null;
177  
178   private boolean QUIET = false;
179  
180   public static boolean quiet()
181   {
182   return Jalview.instanceExists() && Jalview.getInstance().QUIET;
183   }
184  
185   public String appletResourcePath;
186  
187   public String j2sAppletID;
188  
189   private boolean noCalculation, noMenuBar, noStatus;
190  
191   private boolean noAnnotation;
192  
193   public boolean getStartCalculations()
194   {
195   return !noCalculation;
196   }
197  
198   public boolean getAllowMenuBar()
199   {
200   return !noMenuBar;
201   }
202  
203   public boolean getShowStatus()
204   {
205   return !noStatus;
206   }
207  
208   public boolean getShowAnnotation()
209   {
210   return !noAnnotation;
211   }
212  
213   static
214   {
215   if (Platform.isJS())
216   {
217   Platform.getURLCommandArguments();
218   }
219   else
220   /**
221   * Java only
222   *
223   * @j2sIgnore
224   */
225   {
226   // grab all the rights we can for the JVM
227   Policy.setPolicy(new Policy()
228   {
229   @Override
230   public PermissionCollection getPermissions(CodeSource codesource)
231   {
232   Permissions perms = new Permissions();
233   perms.add(new AllPermission());
234   return (perms);
235   }
236  
237   @Override
238   public void refresh()
239   {
240   }
241   });
242   }
243   }
244  
245   /**
246   * keep track of feature fetching tasks.
247   *
248   * @author JimP
249   *
250   */
251   class FeatureFetcher
252   {
253   /*
254   * TODO: generalise to track all jalview events to orchestrate batch processing
255   * events.
256   */
257  
258   private int queued = 0;
259  
260   private int running = 0;
261  
262   public FeatureFetcher()
263   {
264  
265   }
266  
267   public void addFetcher(final AlignFrame af,
268   final Vector dasSources)
269   {
270   final long id = System.currentTimeMillis();
271   queued++;
272   final FeatureFetcher us = this;
273   new Thread(new Runnable()
274   {
275  
276   @Override
277   public void run()
278   {
279   synchronized (us)
280   {
281   queued--;
282   running++;
283   }
284  
285   af.setProgressBar(MessageManager
286   .getString("status.das_features_being_retrived"), id);
287   af.featureSettings_actionPerformed(null);
288   af.setProgressBar(null, id);
289   synchronized (us)
290   {
291   running--;
292   }
293   }
294   }).start();
295   }
296  
297   public synchronized boolean allFinished()
298   {
299   return queued == 0 && running == 0;
300   }
301  
302   }
303  
304   private final static boolean doPlatformLogging = false;
305  
306   /**
307   * main class for Jalview application
308   *
309   * @param args
310   * open filename
311   */
312   public static void main(String[] args)
313   {
314   if (doPlatformLogging)
315   {
316   Platform.startJavaLogging();
317   }
318  
319   getInstance().doMain(args);
320  
321   }
322  
323   /**
324   * @param args
325   */
326   void doMain(String[] args)
327   {
328   boolean isJS = Platform.isJS();
329   if (!isJS)
330   {
331   System.setSecurityManager(null);
332   }
333  
334   /*
335   * @j2sNative J2S.db._DirectDatabaseCalls["compbio.dundee.ac.uk"]=null;
336   * @j2sNative J2S.db._DirectDatabaseCalls["jalview.org"]=null;
337   *
338   */
339   if (args == null || args.length == 0 || (args.length == 1
340   && (args[0] == null || args[0].length() == 0)))
341   {
342   args = new String[] {};
343   }
344  
345   // get args needed before proper ArgParser
346   bootstrapArgs = BootstrapArgs.getBootstrapArgs(args);
347  
348   boolean usingLogfile = false;
349   if (!Platform.isJS())
350   {
351   // required to ensure log4j doesn't think it's running in a servlet
352   System.setProperty("log4j2.isWebapp", "false");
353  
354   // are we using a logfile?
355   String logfilename = System.getProperty("installer.logfile");
356   boolean append = Boolean
357   .parseBoolean(System.getProperty("installer.logfile_append"));
358  
359   usingLogfile = Console.setLogFile(logfilename, append);
360  
361   // are we being --quiet ? (doesn't matter if using a logfile)
362   if (!usingLogfile && bootstrapArgs.contains(Arg.QUIET))
363   {
364   QUIET = true;
365   OutputStream devNull = new OutputStream()
366   {
367   @Override
368   public void write(int b)
369   {
370   // DO NOTHING
371   }
372   };
373   System.setOut(new PrintStream(devNull));
374   // redirecting stderr not working
375   if (bootstrapArgs.getList(Arg.QUIET).size() > 1)
376   {
377   System.setErr(new PrintStream(devNull));
378   }
379   }
380  
381   if (bootstrapArgs.contains(Arg.HELP)
382   || bootstrapArgs.contains(Arg.VERSION))
383   {
384   QUIET = true;
385   }
386   }
387  
388   // set individual session preferences
389   if (bootstrapArgs.contains(Arg.P))
390   {
391   for (String kev : bootstrapArgs.getValueList(Arg.P))
392   {
393   if (kev == null)
394   {
395   continue;
396   }
397   int equalsIndex = kev.indexOf(ArgParser.EQUALS);
398   if (equalsIndex > -1)
399   {
400   String key = kev.substring(0, equalsIndex);
401   String val = kev.substring(equalsIndex + 1);
402   Cache.setSessionProperty(key, val);
403   }
404   }
405   }
406  
407   // Move any new getdown-launcher-new.jar into place over old
408   // getdown-launcher.jar
409   final String appdirString = System
410   .getProperty("launcher.appdir") != null
411   ? System.getProperty("launcher.appdir")
412   :
413   // maybe an old install4j launch
414   System.getProperty("getdownappdir");
415   if (appdirString != null && appdirString.length() > 0)
416   {
417   Console.errPrintln("launcher.appdir property: " + appdirString);
418   new Thread()
419   {
420  
421   @Override
422   public void run()
423   {
424   GetdownLauncherUpdate.main(new String[] { appdirString });
425   }
426   }.start();
427   }
428   else
429   {
430   Console.errPrintln("launcher.appdir property not found");
431   }
432  
433   if ((usingLogfile || !quiet()) || !bootstrapArgs.outputToStdout()
434   || bootstrapArgs.contains(Arg.VERSION))
435   {
436   if (usingLogfile)
437   {
438   Console.outPrintln("-------");
439   }
440   Console.outPrint(Cache.getVersionDetailsForConsole());
441   if (usingLogfile)
442   {
443   Console.outPrintln("-------");
444   }
445   }
446  
447   if (Platform.isLinux() && LaunchUtils.getJavaVersion() < 11)
448   {
449   System.setProperty("flatlaf.uiScale", "1");
450   }
451  
452   // get bootstrap properties (mainly for the logger level)
453   Properties bootstrapProperties = Cache
454   .bootstrapProperties(bootstrapArgs.getValue(Arg.PROPS));
455  
456   // report Jalview version
457   Cache.getInstance().loadBuildProperties(
458   !quiet() || bootstrapArgs.contains(Arg.VERSION));
459  
460   // stop now if only after --version
461   if (bootstrapArgs.contains(Arg.VERSION))
462   {
463   Jalview.exit(null, ExitCode.OK);
464   }
465  
466   // old ArgsParser
467   ArgsParser aparser = new ArgsParser(args);
468  
469   // old
470   boolean headless = false;
471   // new
472   boolean headlessArg = false;
473  
474   try
475   {
476   String logLevel = null;
477   if (bootstrapArgs.contains(Arg.TRACE))
478   {
479   logLevel = "TRACE";
480   }
481   else if (bootstrapArgs.contains(Arg.DEBUG))
482   {
483   logLevel = bootstrapArgs.getBoolean(Arg.DEBUG) ? "DEBUG" : "INFO";
484   }
485   if (logLevel == null && !(bootstrapProperties == null))
486   {
487   logLevel = bootstrapProperties.getProperty(Cache.JALVIEWLOGLEVEL);
488   }
489   Console.initLogger(logLevel);
490   } catch (NoClassDefFoundError error)
491   {
492   error.printStackTrace();
493   String message = "\nEssential logging libraries not found."
494   + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview";
495   Jalview.exit(message, ExitCode.OK);
496   }
497  
498   if (!Platform.isJS())
499   {
500   // register SIGTERM listener
501   Runtime.getRuntime().addShutdownHook(new Thread()
502   {
503   @Override
504   public void run()
505   {
506   Console.debug("Running shutdown hook");
507   QuitHandler.startForceQuit();
508   boolean closeExternal = Cache
509   .getDefault("DEFAULT_CLOSE_EXTERNAL_VIEWERS", false)
510   || Cache.getDefault("ALWAYS_CLOSE_EXTERNAL_VIEWERS",
511   false);
512   StructureViewerBase.setQuitClose(closeExternal);
513   if (desktop != null)
514   {
515   for (JInternalFrame frame : Desktop.getInstance()
516   .getAllFrames())
517   {
518   if (frame instanceof StructureViewerBase)
519   {
520   ((StructureViewerBase) frame).closeViewer(closeExternal);
521   }
522   }
523   }
524  
525   if (QuitHandler.gotQuitResponse() == QResponse.CANCEL_QUIT)
526   {
527   // Got to here by a SIGTERM signal.
528   // Note we will not actually cancel the quit from here -- it's too
529   // late -- but we can wait for saving files and close external
530   // viewers
531   // if configured.
532   // Close viewers/Leave viewers open
533   Console.debug("Checking for saving files");
534   QuitHandler.getQuitResponse(false);
535   }
536   else
537   {
538   Console.debug("Nothing more to do");
539   }
540   Console.debug("Exiting, bye!");
541   // shutdownHook cannot be cancelled, JVM will now halt
542   }
543   });
544   }
545  
546   String usrPropsFile = bootstrapArgs.contains(Arg.PROPS)
547   ? bootstrapArgs.getValue(Arg.PROPS)
548   : aparser.getValue("props");
549   // if usrPropsFile == null, loadProperties will use the Channel
550   // preferences.file
551   Cache.loadProperties(usrPropsFile);
552   if (usrPropsFile != null)
553   {
554   Console.outPrintln(
555   "CMD [-props " + usrPropsFile + "] executed successfully!");
556   testoutput(bootstrapArgs, Arg.PROPS,
557   "test/jalview/bin/testProps.jvprops", usrPropsFile);
558   }
559  
560   // --argfile=... -- OVERRIDES ALL NON-BOOTSTRAP ARGS
561   if (bootstrapArgs.contains(Arg.ARGFILE))
562   {
563   argparser = ArgParser.parseArgFiles(
564   bootstrapArgs.getValueList(Arg.ARGFILE),
565   bootstrapArgs.getBoolean(Arg.INITSUBSTITUTIONS),
566   bootstrapArgs);
567   }
568   else
569   {
570   argparser = new ArgParser(args,
571   bootstrapArgs.getBoolean(Arg.INITSUBSTITUTIONS),
572   bootstrapArgs);
573   }
574  
575   boolean allowServices = true;
576  
577   if (isJS)
578   {
579   j2sAppletID = Platform.getAppID(null);
580   Preferences.setAppletDefaults();
581   Cache.loadProperties(usrPropsFile); // again, because we
582   // might be changing defaults here?
583   appletResourcePath = (String) aparser.getAppletValue("resourcepath",
584   null, true);
585   }
586   else
587   /**
588   * Java only
589   *
590   * @j2sIgnore
591   */
592   {
593   if (bootstrapArgs.contains(Arg.HELP))
594   {
595   List> helpArgs = bootstrapArgs
596   .getList(Arg.HELP);
597   Console.outPrintln(Arg.usage(helpArgs.stream().map(e -> e.getKey())
598   .collect(Collectors.toList())));
599   Jalview.exit(null, ExitCode.OK);
600   }
601   if (aparser.contains("help") || aparser.contains("h"))
602   {
603   /*
604   * Now using new usage statement.
605   showUsage();
606   */
607   Console.outPrintln(Arg.usage());
608   Jalview.exit(null, ExitCode.OK);
609   }
610  
611   // new CLI
612   headlessArg = bootstrapArgs.isHeadless();
613   if (headlessArg)
614   {
615   System.setProperty("java.awt.headless", "true");
616   }
617   // old CLI
618   // BH note: Only -nodisplay is official; others are deprecated?
619   if (aparser.contains("nodisplay") || aparser.contains("nogui")
620   || aparser.contains("headless")
621   || GraphicsEnvironment.isHeadless())
622   {
623   if (!isJS)
624   {
625   // BH Definitely not a good idea in JavaScript;
626   // probably should not be here for Java, either.
627   System.setProperty("java.awt.headless", "true");
628   }
629   headless = true;
630   }
631   // anything else!
632  
633   // allow https handshakes to download intermediate certs if necessary
634   System.setProperty("com.sun.security.enableAIAcaIssuers", "true");
635   String jabawsUrl = bootstrapArgs.getValue(Arg.JABAWS);
636   if (jabawsUrl == null)
637   jabawsUrl = aparser.getValue(ArgsParser.JABAWS);
638   allowServices = !("none".equals(jabawsUrl));
639   if (allowServices && jabawsUrl != null)
640   {
641   try
642   {
643   Jws2Discoverer.getInstance().setPreferredUrl(jabawsUrl);
644   Console.outPrintln(
645   "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
646   testoutput(bootstrapArgs, Arg.JABAWS,
647   "http://www.compbio.dundee.ac.uk/jabaws", jabawsUrl);
648   } catch (MalformedURLException e)
649   {
650   jalview.bin.Console.errPrintln(
651   "Invalid jabaws parameter: " + jabawsUrl + " ignored");
652   }
653   }
654   }
655  
656   List setprops = new ArrayList<>();
657   if (bootstrapArgs.contains(Arg.SETPROP))
658   {
659   setprops = bootstrapArgs.getValueList(Arg.SETPROP);
660   }
661   else
662   {
663   String sp = aparser.getValue("setprop");
664   while (sp != null)
665   {
666   setprops.add(sp);
667   sp = aparser.getValue("setprop");
668   }
669   }
670   for (String setprop : setprops)
671   {
672   int p = setprop.indexOf('=');
673   if (p == -1)
674   {
675   jalview.bin.Console.errPrintln(
676   "Ignoring invalid setprop argument : " + setprop);
677   }
678   else
679   {
680   jalview.bin.Console
681   .errPrintln("Executing setprop argument: " + setprop);
682   if (isJS)
683   {
684   Cache.setProperty(setprop.substring(0, p),
685   setprop.substring(p + 1));
686   }
687   }
688   }
689   if (System.getProperty("java.awt.headless") != null
690   && System.getProperty("java.awt.headless").equals("true"))
691   {
692   headless = true;
693   }
694   System.setProperty("http.agent", UserAgent.getUserAgent());
695  
696   // Initialise the logger
697   try
698   {
699   Console.initLogger();
700   } catch (NoClassDefFoundError error)
701   {
702   error.printStackTrace();
703   String message = "\nEssential logging libraries not found."
704   + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview";
705   Jalview.exit(message, ExitCode.NO_LOGGING);
706   }
707   desktop = null;
708  
709   if (!(headless || headlessArg))
710   {
711   setLookAndFeel();
712   }
713  
714   /*
715   * configure 'full' SO model if preferences say to, else use the default (full SO)
716   * - as JS currently doesn't have OBO parsing, it must use 'Lite' version
717   */
718   boolean soDefault = !isJS;
719   if (Cache.getDefault("USE_FULL_SO", soDefault))
720   {
721   SequenceOntologyFactory.setSequenceOntology(new SequenceOntology());
722   }
723  
724   if (!(headless || headlessArg))
725   {
726   Desktop.nosplash = "false".equals(bootstrapArgs.getValue(Arg.SPLASH))
727   || aparser.contains("nosplash")
728   || Cache.getDefault("SPLASH", "true").equals("false");
729   desktop = Desktop.getInstance();
730   desktop.setInBatchMode(true); // indicate we are starting up
731  
732   mixedCliWarning();
733  
734   try
735   {
736   JalviewTaskbar.setTaskbar(this);
737   } catch (Exception e)
738   {
739   Console.info("Cannot set Taskbar");
740   Console.error(e.getMessage());
741   // e.printStackTrace();
742   } catch (Throwable t)
743   {
744   Console.info("Cannot set Taskbar");
745   Console.error(t.getMessage());
746   // t.printStackTrace();
747   }
748  
749   // set Proxy settings before all the internet calls
750   Cache.setProxyPropertiesFromPreferences();
751  
752   desktop.setVisible(true);
753  
754   if (isJS)
755   {
756   Cache.setProperty("SHOW_JWS2_SERVICES", "false");
757   }
758   if (allowServices && !aparser.contains("nowebservicediscovery"))
759   {
760   desktop.startServiceDiscovery();
761   }
762  
763   if (!isJS)
764   /**
765   * Java only
766   *
767   * @j2sIgnore
768   */
769   {
770  
771   String appName = ChannelProperties.getProperty("app_name");
772  
773   /**
774   * Check to see that the JVM version being run is suitable for the Java
775   * version this Jalview was compiled for. Popup a warning if not.
776   */
777   if (!LaunchUtils.checkJavaVersion())
778   {
779   Console.warn("The Java version being used (Java "
780   + LaunchUtils.getJavaVersion()
781   + ") may lead to problems. This installation of "
782   + appName + " should be used with Java "
783   + LaunchUtils.getJavaCompileVersion() + ".");
784  
785   if (!LaunchUtils
786   .getBooleanUserPreference("IGNORE_JVM_WARNING_POPUP"))
787   {
788   Object[] options = {
789   MessageManager.getString("label.continue") };
790   JOptionPane.showOptionDialog(null,
791   MessageManager.formatMessage(
792   "warning.wrong_jvm_version_message",
793   LaunchUtils.getJavaVersion(),
794   LaunchUtils.getJavaCompileVersion()),
795   MessageManager
796   .getString("warning.wrong_jvm_version_title"),
797   JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
798   null, options, options[0]);
799   }
800   }
801  
802   /**
803   * Check to see if we've been launched from the installer volume
804   * (macOS).
805   */
806   String installerappdirString = System
807   .getProperty("installer.appdir");
808   if (Platform.isMac() && installerappdirString != null
809   && installerappdirString.startsWith("/Volumes/"))
810   {
811   Console.warn("You appear to be running " + appName
812   + " from the Installer volume. Please drag and drop the "
813   + appName + " icon into the Applications folder.");
814  
815   Object[] options = { MessageManager.getString("action.quit") };
816   JOptionPane.showOptionDialog(null, MessageManager.formatMessage(
817   "warning.running_from_installer_volume_message", appName),
818   MessageManager.getString(
819   "warning.running_from_installer_volume_title"),
820   JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
821   null, options, options[0]);
822   quit();
823   }
824  
825   boolean webservicediscovery = bootstrapArgs
826   .getBoolean(Arg.WEBSERVICEDISCOVERY);
827   if (aparser.contains("nowebservicediscovery"))
828   webservicediscovery = false;
829   if (webservicediscovery)
830   {
831   desktop.startServiceDiscovery();
832   }
833   else
834   {
835   testoutput(argparser, Arg.WEBSERVICEDISCOVERY);
836   }
837  
838   boolean usagestats = !bootstrapArgs.getBoolean(Arg.NOUSAGESTATS);
839   if (aparser.contains("nousagestats"))
840   usagestats = false;
841   if (usagestats)
842   {
843   startUsageStats(desktop);
844   testoutput(argparser, Arg.NOUSAGESTATS);
845   }
846   else
847   {
848   Console.outPrintln("CMD [-nousagestats] executed successfully!");
849   testoutput(argparser, Arg.NOUSAGESTATS);
850   }
851  
852   boolean questionnaire = bootstrapArgs.getBoolean(Arg.QUESTIONNAIRE);
853   if (aparser.contains("noquestionnaire"))
854   questionnaire = false;
855   if (questionnaire)
856   {
857   String url = aparser.getValue("questionnaire");
858   if (url != null)
859   {
860   // Start the desktop questionnaire prompter with the specified
861   // questionnaire
862   Console.debug("Starting questionnaire url at " + url);
863   desktop.checkForQuestionnaire(url);
864   Console.outPrintln("CMD questionnaire[-" + url
865   + "] executed successfully!");
866   }
867   else
868   {
869   if (Cache.getProperty("NOQUESTIONNAIRES") == null)
870   {
871   // Start the desktop questionnaire prompter with the specified
872   // questionnaire
873   // String defurl =
874   // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
875   // //
876   String defurl = "https://www.jalview.org/cgi-bin/questionnaire.pl";
877   Console.debug(
878   "Starting questionnaire with default url: " + defurl);
879   desktop.checkForQuestionnaire(defurl);
880   }
881   }
882   }
883   else
884   {
885   Console.outPrintln(
886   "CMD [-noquestionnaire] executed successfully!");
887   testoutput(argparser, Arg.QUESTIONNAIRE);
888   }
889  
890   if ((!aparser.contains("nonews")
891   && Cache.getProperty("NONEWS") == null
892   && !"false".equals(bootstrapArgs.getValue(Arg.NEWS)))
893   || "true".equals(bootstrapArgs.getValue(Arg.NEWS)))
894   {
895   desktop.checkForNews();
896   }
897  
898   if (!aparser.contains("nohtmltemplates")
899   && Cache.getProperty("NOHTMLTEMPLATES") == null)
900   {
901   BioJsHTMLOutput.updateBioJS();
902   }
903   }
904   }
905   else
906   {
907  
908   if (getArgParser().isMixedStyle())
909   {
910   String warning = MessageManager.formatMessage(
911   "warning.using_mixed_command_line_arguments",
912   getArgParser().getMixedExamples());
913   Console.warn(warning);
914   Jalview.exit(
915   "Exiting due to mixed old and new command line arguments",
916   ExitCode.INVALID_ARGUMENT);
917   }
918   if (getArgParser().isOldStyle())
919   {
920   String warning = MessageManager
921   .getString("warning.using_old_command_line_arguments")
922   .replace("\n", " ")
923   + "https://www.jalview.org/help/html/features/commandline.html";
924   Console.warn(warning);
925   }
926  
927   }
928  
929   // Run Commands from cli
930   cmds = new Commands(desktop, argparser, headlessArg);
931   cmds.processArgs();
932   boolean commandsSuccess = cmds.argsWereParsed();
933  
934   if (commandsSuccess)
935   {
936   if (headlessArg)
937   {
938   if (argparser.getBoolean(Arg.NOQUIT))
939   {
940   Console.warn(
941   "Completed " + Arg.HEADLESS.getName() + " commands, but "
942   + Arg.NOQUIT + " is set so not quitting!");
943   }
944   else
945   {
946   Jalview.exit("Successfully completed commands in headless mode",
947   ExitCode.OK);
948   }
949   }
950   Console.info("Successfully completed commands");
951   }
952   else
953   {
954   if (headlessArg)
955   {
956   Jalview.exit("Error when running Commands in headless mode",
957   ExitCode.ERROR_RUNNING_COMMANDS);
958   }
959   Console.warn("Error when running commands");
960   }
961  
962   // Check if JVM and compile version might cause problems and log if it
963   // might.
964   if (headless && !Platform.isJS() && !LaunchUtils.checkJavaVersion())
965   {
966   Console.warn("The Java version being used (Java "
967   + LaunchUtils.getJavaVersion()
968   + ") may lead to problems. This installation of Jalview should be used with Java "
969   + LaunchUtils.getJavaCompileVersion() + ".");
970   }
971  
972   // TODO JAL-4107 - integrate old-style 2.12 and new style 2.11 args handling
973   // !
974   parseArguments(aparser, true, commandsSuccess);
975  
976   cliWarning();
977   }
978  
979   /**
980   * Parse all command-line String[] arguments as well as all JavaScript-derived
981   * parameters from Info.
982   *
983   * We allow for this method to be run from JavaScript. Basically allowing
984   * simple scripting.
985   *
986   * @param aparser
987   * @param isStartup
988   */
989   public void parseArguments(ArgsParser aparser, boolean isStartup)
990   {
991   // TODO: JAL-4107 - @bsoares - need to reconcile JS calling and CLI calling
992   // of --args
993  
994   parseArguments(aparser, isStartup, true); // allow call from JS which does
995   // not result in fileloading ops
996   }
997  
998   public void parseArguments(ArgsParser aparser, boolean isStartup,
999   boolean commandsSuccess)
1000   {
1001   String groovyscript = null; // script to execute after all loading is
1002   boolean isJS = Platform.isJS();
1003   if (!isJS)
1004   /** @j2sIgnore */
1005   {
1006   // Move any new getdown-launcher-new.jar into place over old
1007   // getdown-launcher.jar
1008   String appdirString = System.getProperty("getdownappdir");
1009   if (appdirString != null && appdirString.length() > 0)
1010   {
1011   final File appdir = new File(appdirString);
1012   new Thread()
1013   {
1014   @Override
1015   public void run()
1016   {
1017   LaunchUtil.upgradeGetdown(
1018   new File(appdir, "getdown-launcher-old.jar"),
1019   new File(appdir, "getdown-launcher.jar"),
1020   new File(appdir, "getdown-launcher-new.jar"));
1021   }
1022   }.start();
1023   }
1024   }
1025  
1026   String file = null, data = null;
1027  
1028   FileFormatI format = null;
1029  
1030   DataSourceType protocol = null;
1031  
1032   FileLoader fileLoader = new FileLoader(!headless);
1033  
1034   groovyscript = aparser.getValue("groovy", true);
1035   file = aparser.getValue("open", true);
1036  
1037   if (!isJS && file == null && desktop == null && !commandsSuccess)
1038   {
1039   Jalview.exit("No files to open!", ExitCode.NO_FILES);
1040   }
1041   setDisplayParameters(aparser);
1042  
1043   // time to open a file.
1044   long progress = -1;
1045   // Finally, deal with the remaining input data.
1046   AlignFrame af = null;
1047  
1048   JalviewJSApp jsApp = (isJS ? new JalviewJSApp(this, aparser) : null);
1049  
1050   if (file == null)
1051   {
1052   if (isJS)
1053   {
1054   // JalviewJS allows sequence1 sequence2 ....
1055  
1056   }
1057   }
1058  
1059   if (file != null)
1060   {
1061  
1062   if (!(headless || isHeadlessMode()))
1063   {
1064   desktop.setProgressBar(
1065   MessageManager
1066   .getString("status.processing_commandline_args"),
1067   progress = System.currentTimeMillis());
1068   }
1069  
1070   Console.outPrintln("CMD [-open " + file + "] executed successfully!");
1071  
1072   if (!Platform.isJS())
1073   /**
1074   * ignore in JavaScript -- can't just file existence - could load it?
1075   *
1076   * @j2sIgnore
1077   */
1078   {
1079   if (!HttpUtils.startsWithHttpOrHttps(file))
1080   {
1081   if (!(new File(file)).exists())
1082   {
1083   if (headless)
1084   {
1085   Jalview.exit(
1086   "Can't find file '" + file + "' in headless mode",
1087   ExitCode.FILE_NOT_FOUND);
1088   }
1089   Console.warn("Can't find file'" + file + "'");
1090   }
1091   }
1092   }
1093  
1094   // JS Only argument to provide a format parameter to specify what format
1095   // to use
1096   String fileFormat = (isJS
1097   ? (String) aparser.getAppletValue("format", null, true)
1098   : null);
1099  
1100   protocol = AppletFormatAdapter.checkProtocol(file);
1101  
1102   try
1103   {
1104   format = (fileFormat != null
1105   ? FileFormats.getInstance().forName(fileFormat)
1106   : null);
1107   if (format == null)
1108   {
1109   format = new IdentifyFile().identify(file, protocol);
1110   }
1111   } catch (FileNotFoundException e)
1112   {
1113   Console.error("File at '" + file + "' not found", e);
1114   } catch (FileFormatException e)
1115   {
1116   Console.error("File '" + file + "' format not recognised", e);
1117   }
1118  
1119   af = new FileLoader(!headless).LoadFileWaitTillLoaded(file, protocol,
1120   format);
1121   if (af == null)
1122   {
1123   Console.outPrintln("jalview error - AlignFrame was not created");
1124   }
1125   else
1126   {
1127  
1128   // JalviewLite interface for JavaScript allows second file open
1129   String file2 = aparser.getValue(ArgsParser.OPEN2, true);
1130   if (file2 != null)
1131   {
1132   protocol = AppletFormatAdapter.checkProtocol(file2);
1133   try
1134   {
1135   format = new IdentifyFile().identify(file2, protocol);
1136   } catch (FileNotFoundException e)
1137   {
1138   Console.error("File at '" + file2 + "' not found", e);
1139   } catch (FileFormatException e)
1140   {
1141   Console.error("File '" + file2 + "' format not recognised", e);
1142   }
1143   AlignFrame af2 = new FileLoader(!headless)
1144   .LoadFileWaitTillLoaded(file2, protocol, format);
1145   if (af2 == null)
1146   {
1147   Console.outPrintln("error");
1148   }
1149   else
1150   {
1151   AlignViewport.openLinkedAlignmentAs(af,
1152   af.getViewport().getAlignment(),
1153   af2.getViewport().getAlignment(), "",
1154   AlignViewport.SPLIT_FRAME);
1155   Console.outPrintln(
1156   "CMD [-open2 " + file2 + "] executed successfully!");
1157   }
1158   }
1159   // af is loaded - so set it as current frame
1160   setCurrentAlignFrame(af);
1161  
1162   setFrameDependentProperties(aparser, af);
1163  
1164   if (isJS)
1165   {
1166   jsApp.initFromParams(af);
1167   }
1168   else
1169   /**
1170   * Java only
1171   *
1172   * @j2sIgnore
1173   */
1174   {
1175   if (groovyscript != null)
1176   {
1177   // Execute the groovy script after we've done all the rendering
1178   // stuff
1179   // and before any images or figures are generated.
1180   Console.outPrintln("Executing script " + groovyscript);
1181   executeGroovyScript(groovyscript, af);
1182   Console.outPrintln("CMD groovy[" + groovyscript
1183   + "] executed successfully!");
1184   groovyscript = null;
1185   }
1186   }
1187   if (!isJS || !isStartup)
1188   {
1189   createOutputFiles(aparser, format);
1190   }
1191   }
1192   }
1193   // extract groovy arguments before anything else.
1194   // Once all other stuff is done, execute any groovy scripts (in order)
1195   if (!isJS && (headless || isHeadlessMode()))
1196   {
1197   // TODO JAL-4107 - why doesn't Jalview.exit() do this ?
1198   if (af != null)
1199   {
1200   af.getViewport().getCalcManager().shutdown();
1201   }
1202   Jalview.exit("All done.", ExitCode.OK);
1203   }
1204  
1205   AlignFrame startUpAlframe = null;
1206   // We'll only open the default file if the desktop is visible.
1207   // And the user
1208   // ////////////////////
1209  
1210   if (!Platform.isJS() && !headless && file == null
1211   && Cache.getDefault("SHOW_STARTUP_FILE", true)
1212   && !cmds.commandArgsProvided()
1213   && !bootstrapArgs.getBoolean(Arg.NOSTARTUPFILE))
1214   // don't open the startup file if command line args have been processed
1215   // (&& !Commands.commandArgsProvided())
1216   /**
1217   * Java only
1218   *
1219   * @j2sIgnore
1220   */
1221   {
1222   file = Cache.getDefault("STARTUP_FILE",
1223   Cache.getDefault("www.jalview.org", "https://www.jalview.org")
1224   + "/examples/exampleFile_2_7.jvp");
1225   if (file.equals("http://www.jalview.org/examples/exampleFile_2_3.jar")
1226   || file.equals(
1227   "http://www.jalview.org/examples/exampleFile_2_7.jar"))
1228   {
1229   file.replace("http:", "https:");
1230   // hardwire upgrade of the startup file
1231   file.replace("_2_3", "_2_7");
1232   file.replace("2_7.jar", "2_7.jvp");
1233   // and remove the stale setting
1234   Cache.removeProperty("STARTUP_FILE");
1235   }
1236  
1237   protocol = AppletFormatAdapter.checkProtocol(file);
1238  
1239   if (file.endsWith(".jar"))
1240   {
1241   format = FileFormat.Jalview;
1242   }
1243   else
1244   {
1245   try
1246   {
1247   format = new IdentifyFile().identify(file, protocol);
1248   } catch (FileNotFoundException e)
1249   {
1250   Console.error("File at '" + file + "' not found", e);
1251   } catch (FileFormatException e)
1252   {
1253   Console.error("File '" + file + "' format not recognised", e);
1254   }
1255   }
1256  
1257   startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
1258   format);
1259   // don't ask to save when quitting if only the startup file has been
1260   // opened
1261   Console.debug("Resetting up-to-date flag for startup file");
1262   startUpAlframe.getViewport().setSavedUpToDate(true);
1263   // extract groovy arguments before anything else.
1264   }
1265   if (!isJS && groovyscript != null)
1266   {
1267   if (Cache.groovyJarsPresent())
1268   {
1269   // TODO: DECIDE IF THIS SECOND PASS AT GROOVY EXECUTION IS STILL
1270   // REQUIRED !!
1271   Console.outPrintln("Executing script " + groovyscript);
1272   executeGroovyScript(groovyscript, af);
1273   Console.outPrintln(
1274   "CMD groovy[" + groovyscript + "] executed successfully!");
1275  
1276   }
1277   else
1278   {
1279   Console.errPrintln(
1280   "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
1281   + groovyscript);
1282   }
1283   }
1284  
1285   // and finally, turn off batch mode indicator - if the desktop still
1286   // exists
1287   if (desktop != null)
1288   {
1289   if (progress != -1)
1290   {
1291   desktop.setProgressBar(null, progress);
1292   }
1293   desktop.setInBatchMode(false);
1294   }
1295  
1296   if (jsApp != null)
1297   {
1298   jsApp.callInitCallback();
1299   }
1300   }
1301  
1302   /**
1303   * Set general display parameters irrespective of file loading or
1304   * headlessness.
1305   *
1306   * @param aparser
1307   */
1308   private void setDisplayParameters(ArgsParser aparser)
1309   {
1310   if (aparser.contains(ArgsParser.NOMENUBAR))
1311   {
1312   noMenuBar = true;
1313   Console.outPrintln("CMD [nomenu] executed successfully!");
1314   }
1315  
1316   if (aparser.contains(ArgsParser.NOSTATUS))
1317   {
1318   noStatus = true;
1319   Console.outPrintln("CMD [nostatus] executed successfully!");
1320   }
1321  
1322   if (aparser.contains(ArgsParser.NOANNOTATION)
1323   || aparser.contains(ArgsParser.NOANNOTATION2))
1324   {
1325   noAnnotation = true;
1326   Console.outPrintln("CMD no-annotation executed successfully!");
1327   }
1328   if (aparser.contains(ArgsParser.NOCALCULATION))
1329   {
1330   noCalculation = true;
1331   Console.outPrintln("CMD [nocalculation] executed successfully!");
1332   }
1333   }
1334  
1335   private void setFrameDependentProperties(ArgsParser aparser,
1336   AlignFrame af)
1337   {
1338   String data = aparser.getValue(ArgsParser.COLOUR, true);
1339   if (data != null)
1340   {
1341   data.replaceAll("%20", " ");
1342  
1343   ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
1344   af.getViewport(), af.getViewport().getAlignment(), data);
1345  
1346   if (cs != null)
1347   {
1348   Console.outPrintln(
1349   "CMD [-colour " + data + "] executed successfully!");
1350   }
1351   af.changeColour(cs);
1352   }
1353  
1354   // Must maintain ability to use the groups flag
1355   data = aparser.getValue(ArgsParser.GROUPS, true);
1356   if (data != null)
1357   {
1358   af.parseFeaturesFile(data, AppletFormatAdapter.checkProtocol(data));
1359   // System.out.println("Added " + data);
1360   Console.outPrintln(
1361   "CMD groups[-" + data + "] executed successfully!");
1362   }
1363   data = aparser.getValue(ArgsParser.FEATURES, true);
1364   if (data != null)
1365   {
1366   af.parseFeaturesFile(data, AppletFormatAdapter.checkProtocol(data));
1367   // System.out.println("Added " + data);
1368   Console.outPrintln(
1369   "CMD [-features " + data + "] executed successfully!");
1370   }
1371   data = aparser.getValue(ArgsParser.ANNOTATIONS, true);
1372   if (data != null)
1373   {
1374   af.loadJalviewDataFile(data, null, null, null);
1375   // System.out.println("Added " + data);
1376   Console.outPrintln(
1377   "CMD [-annotations " + data + "] executed successfully!");
1378   }
1379  
1380   // JavaScript feature
1381  
1382   if (aparser.contains(ArgsParser.SHOWOVERVIEW))
1383   {
1384   af.overviewMenuItem_actionPerformed(null);
1385   Console.outPrintln("CMD [showoverview] executed successfully!");
1386   }
1387  
1388   // set or clear the sortbytree flag.
1389   if (aparser.contains(ArgsParser.SORTBYTREE))
1390   {
1391   af.getViewport().setSortByTree(true);
1392   if (af.getViewport().getSortByTree())
1393   {
1394   Console.outPrintln("CMD [-sortbytree] executed successfully!");
1395   }
1396   }
1397  
1398   boolean doUpdateAnnotation = false;
1399   /**
1400   * we do this earlier in JalviewJS because of a complication with
1401   * SHOWOVERVIEW
1402   *
1403   * For now, just fixing this in JalviewJS.
1404   *
1405   *
1406   * @j2sIgnore
1407   *
1408   */
1409   {
1410   if (noAnnotation)
1411   {
1412   af.getViewport().setShowAnnotation(false);
1413   if (!af.getViewport().isShowAnnotation())
1414   {
1415   doUpdateAnnotation = true;
1416   }
1417   }
1418  
1419   }
1420  
1421   if (aparser.contains(ArgsParser.NOSORTBYTREE))
1422   {
1423   af.getViewport().setSortByTree(false);
1424   if (!af.getViewport().getSortByTree())
1425   {
1426   doUpdateAnnotation = true;
1427   Console.outPrintln("CMD [-nosortbytree] executed successfully!");
1428   }
1429   }
1430   if (doUpdateAnnotation)
1431   { // BH 2019.07.24
1432   af.setMenusForViewport();
1433   af.alignPanel.updateLayout();
1434   }
1435  
1436   data = aparser.getValue(ArgsParser.TREE, true);
1437   if (data != null)
1438   {
1439   try
1440   {
1441   NewickFile nf = new NewickFile(data,
1442   AppletFormatAdapter.checkProtocol(data));
1443   af.getViewport()
1444   .setCurrentTree(af.showNewickTree(nf, data).getTree());
1445   Console.outPrintln(
1446   "CMD [-tree " + data + "] executed successfully!");
1447   } catch (IOException ex)
1448   {
1449   Console.errPrintln("Couldn't add tree " + data);
1450   ex.printStackTrace(System.err);
1451   }
1452   }
1453   // JAL-4107 - DELETE THIS TODO ??? NO NEED TO LOAD PDB STRUCTURES ANYMORE
1454   // ??? ALL DONE IN NEW Commands ?
1455   // TODO - load PDB structure(s) to alignment JAL-629
1456   // (associate with identical sequence in alignment, or a specified
1457   // sequence)
1458   //
1459  
1460   }
1461  
1462   /**
1463   * Writes an output file for each format (if any) specified in the
1464   * command-line arguments. Supported formats are currently
1465   *
1466   *
  • png
  • 1467   *
  • svg
  • 1468   *
  • html
  • 1469   *
  • biojsmsa
  • 1470   *
  • imgMap
  • 1471   *
  • eps
  • 1472   *
    1473   * A format parameter should be followed by a parameter specifying the output
    1474   * file name. {@code imgMap} parameters should follow those for the
    1475   * corresponding alignment image output.
    1476   *
    1477   * @param aparser
    1478   * @param format
    1479   */
    1480   private void createOutputFiles(ArgsParser aparser, FileFormatI format)
    1481   {
    1482   // logic essentially the same as 2.11.2/2.11.3 but uses a switch instead
    1483   AlignFrame af = currentAlignFrame;
    1484   String file = null;
    1485   while (aparser.getSize() >= 2)
    1486   {
    1487   try
    1488   {
    1489   String outputFormat = aparser.nextValue();
    1490   File imageFile;
    1491   file = aparser.nextValue();
    1492   String fname;
    1493   switch (outputFormat.toLowerCase(Locale.ROOT))
    1494   {
    1495   case "png":
    1496   imageFile = new File(file);
    1497   af.createPNG(imageFile);
    1498   Console.outPrintln(
    1499   "Creating PNG image: " + imageFile.getAbsolutePath());
    1500   continue;
    1501   case "svg":
    1502   imageFile = new File(file);
    1503   af.createSVG(imageFile);
    1504   Console.outPrintln(
    1505   "Creating SVG image: " + imageFile.getAbsolutePath());
    1506   continue;
    1507   case "eps":
    1508   imageFile = new File(file);
    1509   Console.outPrintln(
    1510   "Creating EPS file: " + imageFile.getAbsolutePath());
    1511   af.createEPS(imageFile);
    1512   continue;
    1513   case "biojsmsa":
    1514   fname = new File(file).getAbsolutePath();
    1515   try
    1516   {
    1517   BioJsHTMLOutput.refreshVersionInfo(
    1518   BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
    1519   } catch (URISyntaxException e)
    1520   {
    1521   Console.errPrintln(Cache.getStackTraceString(e));
    1522   }
    1523   BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
    1524   bjs.exportHTML(fname);
    1525   Console.outPrintln(
    1526   "Creating BioJS MSA Viwer HTML file: " + fname);
    1527   continue;
    1528   case "html":
    1529   fname = new File(file).getAbsolutePath();
    1530   HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
    1531   htmlSVG.exportHTML(fname);
    1532   Console.outPrintln("Creating HTML image: " + fname);
    1533   continue;
    1534   case "imgmap":
    1535   imageFile = new File(file);
    1536   af.alignPanel.makePNGImageMap(imageFile, "unnamed.png");
    1537   Console.outPrintln(
    1538   "Creating image map: " + imageFile.getAbsolutePath());
    1539   continue;
    1540   default:
    1541   // fall through - try to parse as an alignment data export format
    1542   FileFormatI outFormat = null;
    1543   try
    1544   {
    1545   outFormat = FileFormats.getInstance().forName(outputFormat);
    1546   } catch (Exception formatP)
    1547   {
    1548   Console.errPrintln(Cache.getStackTraceString(formatP));
    1549   }
    1550   if (outFormat == null)
    1551   {
    1552   Console.outPrintln("Couldn't parse " + outputFormat
    1553   + " as a valid Jalview format string.");
    1554   continue;
    1555   }
    1556   if (!outFormat.isWritable())
    1557   {
    1558   Console.outPrintln(
    1559   "This version of Jalview does not support alignment export as "
    1560   + outputFormat);
    1561   continue;
    1562   }
    1563   // record file as it was passed to Jalview so it is recognisable to
    1564   // the CLI
    1565   // caller
    1566  
    1567   fname = new File(file).getAbsolutePath();
    1568   // JBPNote - yuck - really wish we did have a bean returned from this
    1569   // which gave
    1570   // success/fail like before !
    1571   af.saveAlignment(fname, outFormat);
    1572   if (af.isSaveAlignmentSuccessful())
    1573   {
    1574   Console.outPrintln("Written alignment in " + outputFormat
    1575   + " format to " + file);
    1576   }
    1577   else
    1578   {
    1579   Console.errPrintln("Error writing file " + file + " in "
    1580   + outputFormat + " format!!");
    1581   }
    1582   }
    1583   }
    1584  
    1585   catch (ImageOutputException ioexc)
    1586   {
    1587   Console.outPrintln(
    1588   "Unexpected error whilst exporting image to " + file);
    1589   ioexc.printStackTrace();
    1590   }
    1591   }
    1592   // ??? Should report - 'ignoring' extra args here...
    1593   while (aparser.getSize() > 0)
    1594   {
    1595   Console.outPrintln("Ignoring extra argument: " + aparser.nextValue());
    1596   }
    1597  
    1598   cliWarning();
    1599   }
    1600  
    1601   private static void setLookAndFeel()
    1602   {
    1603   if (!Platform.isJS())
    1604   /**
    1605   * Java only
    1606   *
    1607   * @j2sIgnore
    1608   */
    1609   {
    1610   // property laf = "crossplatform", "system", "gtk", "metal", "nimbus",
    1611   // "mac" or "flat"
    1612   // If not set (or chosen laf fails), use the normal SystemLaF and if on
    1613   // Mac,
    1614   // try Quaqua/Vaqua.
    1615   String lafProp = System.getProperty("laf");
    1616   String lafSetting = Cache.getDefault("PREFERRED_LAF", null);
    1617   String laf = "none";
    1618   if (lafProp != null)
    1619   {
    1620   laf = lafProp;
    1621   }
    1622   else if (lafSetting != null)
    1623   {
    1624   laf = lafSetting;
    1625   }
    1626   boolean lafSet = false;
    1627   switch (laf)
    1628   {
    1629   case "crossplatform":
    1630   lafSet = setCrossPlatformLookAndFeel();
    1631   if (!lafSet)
    1632   {
    1633   Console.error("Could not set requested laf=" + laf);
    1634   }
    1635   break;
    1636   case "system":
    1637   lafSet = setSystemLookAndFeel();
    1638   if (!lafSet)
    1639   {
    1640   Console.error("Could not set requested laf=" + laf);
    1641   }
    1642   break;
    1643   case "gtk":
    1644   lafSet = setGtkLookAndFeel();
    1645   if (!lafSet)
    1646   {
    1647   Console.error("Could not set requested laf=" + laf);
    1648   }
    1649   break;
    1650   case "metal":
    1651   lafSet = setMetalLookAndFeel();
    1652   if (!lafSet)
    1653   {
    1654   Console.error("Could not set requested laf=" + laf);
    1655   }
    1656   break;
    1657   case "nimbus":
    1658   lafSet = setNimbusLookAndFeel();
    1659   if (!lafSet)
    1660   {
    1661   Console.error("Could not set requested laf=" + laf);
    1662   }
    1663   break;
    1664   case "flat":
    1665   lafSet = setFlatLookAndFeel();
    1666   if (!lafSet)
    1667   {
    1668   Console.error("Could not set requested laf=" + laf);
    1669   }
    1670   break;
    1671   case "mac":
    1672   lafSet = setMacLookAndFeel();
    1673   if (!lafSet)
    1674   {
    1675   Console.error("Could not set requested laf=" + laf);
    1676   }
    1677   break;
    1678   case "none":
    1679   break;
    1680   default:
    1681   Console.error("Requested laf=" + laf + " not implemented");
    1682   }
    1683   if (!lafSet)
    1684   {
    1685   // Flatlaf default for everyone!
    1686   lafSet = setFlatLookAndFeel();
    1687   if (!lafSet)
    1688   {
    1689   setSystemLookAndFeel();
    1690   }
    1691   if (Platform.isLinux())
    1692   {
    1693   setLinuxLookAndFeel();
    1694   }
    1695   if (Platform.isMac())
    1696   {
    1697   setMacLookAndFeel();
    1698   }
    1699   }
    1700   }
    1701   }
    1702  
    1703   private static boolean setCrossPlatformLookAndFeel()
    1704   {
    1705   boolean set = false;
    1706   try
    1707   {
    1708   UIManager.setLookAndFeel(
    1709   UIManager.getCrossPlatformLookAndFeelClassName());
    1710   set = true;
    1711   } catch (Exception ex)
    1712   {
    1713   Console.error("Unexpected Look and Feel Exception");
    1714   Console.error(ex.getMessage());
    1715   Console.debug(Cache.getStackTraceString(ex));
    1716   }
    1717   return set;
    1718   }
    1719  
    1720   private static boolean setSystemLookAndFeel()
    1721   {
    1722   boolean set = false;
    1723   try
    1724   {
    1725   UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    1726   set = true;
    1727   } catch (Exception ex)
    1728   {
    1729   Console.error("Unexpected Look and Feel Exception");
    1730   Console.error(ex.getMessage());
    1731   Console.debug(Cache.getStackTraceString(ex));
    1732   }
    1733   return set;
    1734   }
    1735  
    1736   private static boolean setSpecificLookAndFeel(String name,
    1737   String className, boolean nameStartsWith)
    1738   {
    1739   boolean set = false;
    1740   try
    1741   {
    1742   for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
    1743   {
    1744   if (info.getName() != null && nameStartsWith
    1745   ? info.getName().toLowerCase(Locale.ROOT)
    1746   .startsWith(name.toLowerCase(Locale.ROOT))
    1747   : info.getName().toLowerCase(Locale.ROOT)
    1748   .equals(name.toLowerCase(Locale.ROOT)))
    1749   {
    1750   className = info.getClassName();
    1751   break;
    1752   }
    1753   }
    1754   UIManager.setLookAndFeel(className);
    1755   set = true;
    1756   } catch (Exception ex)
    1757   {
    1758   Console.error("Unexpected Look and Feel Exception");
    1759   Console.error(ex.getMessage());
    1760   Console.debug(Cache.getStackTraceString(ex));
    1761   }
    1762   return set;
    1763   }
    1764  
    1765   private static boolean setGtkLookAndFeel()
    1766   {
    1767   return setSpecificLookAndFeel("gtk",
    1768   "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true);
    1769   }
    1770  
    1771   private static boolean setMetalLookAndFeel()
    1772   {
    1773   return setSpecificLookAndFeel("metal",
    1774   "javax.swing.plaf.metal.MetalLookAndFeel", false);
    1775   }
    1776  
    1777   private static boolean setNimbusLookAndFeel()
    1778   {
    1779   return setSpecificLookAndFeel("nimbus",
    1780   "javax.swing.plaf.nimbus.NimbusLookAndFeel", false);
    1781   }
    1782  
    1783   private static boolean setFlatLookAndFeel()
    1784   {
    1785   boolean set = false;
    1786   if (SystemInfo.isMacOS)
    1787   {
    1788   try
    1789   {
    1790   UIManager.setLookAndFeel(
    1791   "com.formdev.flatlaf.themes.FlatMacLightLaf");
    1792   set = true;
    1793   Console.debug("Using FlatMacLightLaf");
    1794   } catch (ClassNotFoundException | InstantiationException
    1795   | IllegalAccessException | UnsupportedLookAndFeelException e)
    1796   {
    1797   Console.debug("Exception loading FlatLightLaf", e);
    1798   }
    1799   System.setProperty("apple.laf.useScreenMenuBar", "true");
    1800   System.setProperty("apple.awt.application.name",
    1801   ChannelProperties.getProperty("app_name"));
    1802   System.setProperty("apple.awt.application.appearance", "system");
    1803   if (SystemInfo.isMacFullWindowContentSupported
    1804   && Desktop.getInstance() != null)
    1805   {
    1806   Console.debug("Setting transparent title bar");
    1807   Desktop.getInstance().getRootPane()
    1808   .putClientProperty("apple.awt.fullWindowContent", true);
    1809   Desktop.getInstance().getRootPane()
    1810   .putClientProperty("apple.awt.transparentTitleBar", true);
    1811   Desktop.getInstance().getRootPane()
    1812   .putClientProperty("apple.awt.fullscreenable", true);
    1813   }
    1814   SwingUtilities.invokeLater(() -> {
    1815   FlatMacLightLaf.setup();
    1816   });
    1817   Console.debug("Using FlatMacLightLaf");
    1818   set = true;
    1819   }
    1820   if (!set)
    1821   {
    1822   try
    1823   {
    1824   UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
    1825   set = true;
    1826   Console.debug("Using FlatLightLaf");
    1827   } catch (ClassNotFoundException | InstantiationException
    1828   | IllegalAccessException | UnsupportedLookAndFeelException e)
    1829   {
    1830   Console.debug("Exception loading FlatLightLaf", e);
    1831   }
    1832   // Windows specific properties here
    1833   SwingUtilities.invokeLater(() -> {
    1834   FlatLightLaf.setup();
    1835   });
    1836   Console.debug("Using FlatLightLaf");
    1837   set = true;
    1838   }
    1839   else if (SystemInfo.isLinux)
    1840   {
    1841   try
    1842   {
    1843   UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
    1844   set = true;
    1845   Console.debug("Using FlatLightLaf");
    1846   } catch (ClassNotFoundException | InstantiationException
    1847   | IllegalAccessException | UnsupportedLookAndFeelException e)
    1848   {
    1849   Console.debug("Exception loading FlatLightLaf", e);
    1850   }
    1851   // enable custom window decorations
    1852   JFrame.setDefaultLookAndFeelDecorated(true);
    1853   JDialog.setDefaultLookAndFeelDecorated(true);
    1854   SwingUtilities.invokeLater(() -> {
    1855   FlatLightLaf.setup();
    1856   });
    1857   Console.debug("Using FlatLightLaf");
    1858   set = true;
    1859   }
    1860  
    1861   if (!set)
    1862   {
    1863   try
    1864   {
    1865   UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
    1866   set = true;
    1867   Console.debug("Using FlatLightLaf");
    1868   } catch (ClassNotFoundException | InstantiationException
    1869   | IllegalAccessException | UnsupportedLookAndFeelException e)
    1870   {
    1871   Console.debug("Exception loading FlatLightLaf", e);
    1872   }
    1873   }
    1874  
    1875   if (set)
    1876   {
    1877   UIManager.put("TabbedPane.tabType", "card");
    1878   UIManager.put("TabbedPane.showTabSeparators", true);
    1879   UIManager.put("TabbedPane.showContentSeparator", true);
    1880   // UIManager.put("TabbedPane.tabSeparatorsFullHeight", true);
    1881   UIManager.put("TabbedPane.tabsOverlapBorder", true);
    1882   UIManager.put("TabbedPane.hasFullBorder", true);
    1883   UIManager.put("TabbedPane.tabLayoutPolicy", "scroll");
    1884   UIManager.put("TabbedPane.scrollButtonsPolicy", "asNeeded");
    1885   UIManager.put("TabbedPane.smoothScrolling", true);
    1886   UIManager.put("TabbedPane.tabWidthMode", "compact");
    1887   UIManager.put("TabbedPane.selectedBackground", Color.white);
    1888   UIManager.put("TabbedPane.background", new Color(236, 236, 236));
    1889   UIManager.put("TabbedPane.hoverColor", Color.lightGray);
    1890   }
    1891  
    1892   Desktop.setLiveDragMode(Cache.getDefault("FLAT_LIVE_DRAG_MODE", true));
    1893   return set;
    1894   }
    1895  
    1896   private static boolean setMacLookAndFeel()
    1897   {
    1898   boolean set = false;
    1899   System.setProperty("com.apple.mrj.application.apple.menu.about.name",
    1900   ChannelProperties.getProperty("app_name"));
    1901   System.setProperty("apple.laf.useScreenMenuBar", "true");
    1902   /*
    1903   * broken native LAFs on (ARM?) macbooks
    1904   set = setQuaquaLookAndFeel();
    1905   if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
    1906   .toLowerCase(Locale.ROOT).contains("quaqua"))
    1907   {
    1908   set = setVaquaLookAndFeel();
    1909   }
    1910   */
    1911   set = setFlatLookAndFeel();
    1912   return set;
    1913   }
    1914  
    1915   private static boolean setLinuxLookAndFeel()
    1916   {
    1917   boolean set = false;
    1918   set = setFlatLookAndFeel();
    1919   if (!set)
    1920   set = setMetalLookAndFeel();
    1921   // avoid GtkLookAndFeel -- not good results especially on HiDPI
    1922   if (!set)
    1923   set = setNimbusLookAndFeel();
    1924   return set;
    1925   }
    1926  
    1927   /*
    1928   private static void showUsage()
    1929   {
    1930   jalview.bin.Console.outPrintln(
    1931   "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
    1932   + "-nodisplay\tRun Jalview without User Interface.\n"
    1933   + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
    1934   + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
    1935   + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
    1936   + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
    1937   + "-features FILE\tUse the given file to mark features on the alignment.\n"
    1938   + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
    1939   + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
    1940   + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
    1941   + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
    1942   + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
    1943   + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
    1944   + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
    1945   + "-json FILE\tCreate alignment file FILE in JSON format.\n"
    1946   + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
    1947   + "-png FILE\tCreate PNG image FILE from alignment.\n"
    1948   + "-svg FILE\tCreate SVG image FILE from alignment.\n"
    1949   + "-html FILE\tCreate HTML file from alignment.\n"
    1950   + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
    1951   + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
    1952   + "-eps FILE\tCreate EPS file FILE from alignment.\n"
    1953   + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
    1954   + "-noquestionnaire\tTurn off questionnaire check.\n"
    1955   + "-nonews\tTurn off check for Jalview news.\n"
    1956   + "-nousagestats\tTurn off analytics tracking for this session.\n"
    1957   + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
    1958   // +
    1959   // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
    1960   // after all other properties files have been read\n\t
    1961   // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
    1962   // passed in correctly)"
    1963   + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
    1964   + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
    1965   + "-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"
    1966   + "-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"
    1967   + "-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"
    1968   + "\n~Read documentation in Application or visit https://www.jalview.org for description of Features and Annotations file~\n\n");
    1969   }
    1970   */
    1971  
    1972   private static void startUsageStats(final Desktop desktop)
    1973   {
    1974   /**
    1975   * start a User Config prompt asking if we can log usage statistics.
    1976   */
    1977   PromptUserConfig prompter = new PromptUserConfig(desktop, "USAGESTATS",
    1978   MessageManager.getString("prompt.analytics_title"),
    1979   MessageManager.getString("prompt.analytics"), new Runnable()
    1980   {
    1981   @Override
    1982   public void run()
    1983   {
    1984   Console.debug("Initialising analytics for usage stats.");
    1985   Cache.initAnalytics();
    1986   Console.debug("Tracking enabled.");
    1987   }
    1988   }, new Runnable()
    1989   {
    1990   @Override
    1991   public void run()
    1992   {
    1993   Console.debug("Not enabling analytics.");
    1994   }
    1995   }, null, true);
    1996   desktop.addDialogThread(prompter);
    1997   }
    1998  
    1999   /**
    2000   * Locate the given string as a file and pass it to the groovy interpreter.
    2001   *
    2002   * @param groovyscript
    2003   * the script to execute
    2004   * @param jalviewContext
    2005   * the Jalview Desktop object passed in to the groovy binding as the
    2006   * 'Jalview' object.
    2007   */
    2008   protected void executeGroovyScript(String groovyscript, AlignFrame af)
    2009   {
    2010   /**
    2011   * for scripts contained in files
    2012   */
    2013   File tfile = null;
    2014   /**
    2015   * script's URI
    2016   */
    2017   URL sfile = null;
    2018   if (groovyscript.trim().equals("STDIN"))
    2019   {
    2020   // read from stdin into a tempfile and execute it
    2021   try
    2022   {
    2023   tfile = File.createTempFile("jalview", "groovy");
    2024   PrintWriter outfile = new PrintWriter(
    2025   new OutputStreamWriter(new FileOutputStream(tfile)));
    2026   BufferedReader br = new BufferedReader(
    2027   new InputStreamReader(System.in));
    2028   String line = null;
    2029   while ((line = br.readLine()) != null)
    2030   {
    2031   outfile.write(line + "\n");
    2032   }
    2033   br.close();
    2034   outfile.flush();
    2035   outfile.close();
    2036  
    2037   } catch (Exception ex)
    2038   {
    2039   jalview.bin.Console
    2040   .errPrintln("Failed to read from STDIN into tempfile "
    2041   + ((tfile == null) ? "(tempfile wasn't created)"
    2042   : tfile.toString()));
    2043   ex.printStackTrace();
    2044   return;
    2045   }
    2046   try
    2047   {
    2048   sfile = tfile.toURI().toURL();
    2049   } catch (Exception x)
    2050   {
    2051   jalview.bin.Console.errPrintln(
    2052   "Unexpected Malformed URL Exception for temporary file created from STDIN: "
    2053   + tfile.toURI());
    2054   x.printStackTrace();
    2055   return;
    2056   }
    2057   }
    2058   else
    2059   {
    2060   try
    2061   {
    2062   sfile = new URI(groovyscript).toURL();
    2063   } catch (Exception x)
    2064   {
    2065   tfile = new File(groovyscript);
    2066   if (!tfile.exists())
    2067   {
    2068   jalview.bin.Console.errPrintln(
    2069   "File '" + groovyscript + "' does not exist.");
    2070   return;
    2071   }
    2072   if (!tfile.canRead())
    2073   {
    2074   jalview.bin.Console.errPrintln(
    2075   "File '" + groovyscript + "' cannot be read.");
    2076   return;
    2077   }
    2078   if (tfile.length() < 1)
    2079   {
    2080   jalview.bin.Console
    2081   .errPrintln("File '" + groovyscript + "' is empty.");
    2082   return;
    2083   }
    2084   try
    2085   {
    2086   sfile = tfile.getAbsoluteFile().toURI().toURL();
    2087   } catch (Exception ex)
    2088   {
    2089   jalview.bin.Console.errPrintln("Failed to create a file URL for "
    2090   + tfile.getAbsoluteFile());
    2091   return;
    2092   }
    2093   }
    2094   }
    2095   try
    2096   {
    2097   JalviewObjectI j = new JalviewObject(this);
    2098   Map vbinding = new HashMap<>();
    2099   vbinding.put(JalviewObjectI.jalviewObjectName, j);
    2100   vbinding.put(JalviewObjectI.currentAlFrameName,
    2101   af != null ? af : getCurrentAlignFrame());
    2102   Binding gbinding = new Binding(vbinding);
    2103   GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
    2104   gse.run(sfile.toString(), gbinding);
    2105   if ("STDIN".equals(groovyscript))
    2106   {
    2107   // delete temp file that we made -
    2108   // only if it was successfully executed
    2109   tfile.delete();
    2110   }
    2111   } catch (Exception e)
    2112   {
    2113   jalview.bin.Console
    2114   .errPrintln("Exception Whilst trying to execute file " + sfile
    2115   + " as a groovy script.");
    2116   e.printStackTrace(System.err);
    2117   }
    2118   }
    2119  
    2120   public static boolean isHeadlessMode()
    2121   {
    2122   String isheadless = System.getProperty("java.awt.headless");
    2123   if (isheadless != null && isheadless.equalsIgnoreCase("true"))
    2124   {
    2125   return true;
    2126   }
    2127   return false;
    2128   }
    2129  
    2130   @Override
    2131   public AlignFrame[] getAlignFrames()
    2132   {
    2133   return desktop == null
    2134   ? getCurrentAlignFrame() != null ? new AlignFrame[]
    2135   { getCurrentAlignFrame() } : null
    2136   : Desktop.getDesktopAlignFrames();
    2137   }
    2138  
    2139   /**
    2140   * jalview.bin.Jalview.quit() will just run the non-GUI shutdownHook and exit
    2141   */
    2142   @Override
    2143   public void quit()
    2144   {
    2145   // System.exit will run the shutdownHook first
    2146   Jalview.exit("Quitting now. Bye!", ExitCode.OK);
    2147   }
    2148  
    2149   @Override
    2150   public AlignFrame getCurrentAlignFrame()
    2151   {
    2152   return currentAlignFrame;
    2153   }
    2154  
    2155   public void setCurrentAlignFrame(AlignFrame af)
    2156   {
    2157   this.currentAlignFrame = af;
    2158   }
    2159  
    2160   public Commands getCommands()
    2161   {
    2162   return cmds;
    2163   }
    2164  
    2165   public static void exit(String message, ExitCode ec)
    2166   {
    2167   int exitcode = ec == ExitCode.OK ? 0 : ec.ordinal() + 1;
    2168   if (Console.log == null)
    2169   {
    2170   // Don't start the logger just to exit!
    2171   if (message != null)
    2172   {
    2173   if (exitcode == 0)
    2174   {
    2175   Console.outPrintln(message);
    2176   }
    2177   else
    2178   {
    2179   jalview.bin.Console.errPrintln(message);
    2180   }
    2181   }
    2182   }
    2183   else
    2184   {
    2185   Console.debug("Using Jalview.exit");
    2186   if (message != null)
    2187   {
    2188   if (exitcode == 0)
    2189   {
    2190   Console.info(message);
    2191   }
    2192   else
    2193   {
    2194   Console.error(message);
    2195   }
    2196   }
    2197   }
    2198   if (exitcode > -1)
    2199   {
    2200   System.exit(exitcode);
    2201   }
    2202   }
    2203  
    2204   public enum ExitCode
    2205   {
    2206   // only add new ones to the end of the list (to preserve ordinal values)
    2207   OK, FILE_NOT_FOUND, FILE_NOT_READABLE, NO_FILES, INVALID_FORMAT,
    2208   INVALID_ARGUMENT, INVALID_VALUE, MIXED_CLI_ARGUMENTS,
    2209   ERROR_RUNNING_COMMANDS, NO_LOGGING, GROOVY_ERROR;
    2210   }
    2211  
    2212   /******************************
    2213   *
    2214   * TEST OUTPUT METHODS
    2215   *
    2216   * these operate only when Arg.TESTOUTPUT has been passed, and variously check
    2217   * if an expected value / arg was set and report it to the test framework.
    2218   *
    2219   ******************************/
    2220   /**
    2221   * report string values parsed/processed during tests When the Bootstrap
    2222   * argument Arg.TESTOUTPUT is present - reports on debug if given s1 is not
    2223   * null and not equals s2, warns if given argument is not set, and calls
    2224   * testoutput(true,a,s1,s2) to report processing progress.
    2225   *
    2226   * @param ap
    2227   * - ArgParser handling parsing
    2228   * @param a
    2229   * - Arg currently being processed
    2230   * @param s1
    2231   * - expected
    2232   * @param s2
    2233   */
    2234   protected static void testoutput(ArgParser ap, Arg a, String s1,
    2235   String s2)
    2236   {
    2237   BootstrapArgs bsa = ap.getBootstrapArgs();
    2238   if (!bsa.getBoolean(Arg.TESTOUTPUT))
    2239   return;
    2240   if (!((s1 == null && s2 == null) || (s1 != null && s1.equals(s2))))
    2241   {
    2242   Console.debug("testoutput with unmatching values '" + s1 + "' and '"
    2243   + s2 + "' for arg " + a.argString());
    2244   return;
    2245   }
    2246   boolean isset = a.hasOption(Opt.BOOTSTRAP) ? bsa.contains(a)
    2247   : ap.isSet(a);
    2248   if (!isset)
    2249   {
    2250   Console.warn("Arg '" + a.getName() + "' not set at all");
    2251   return;
    2252   }
    2253   testoutput(true, a, s1, s2);
    2254   }
    2255  
    2256   /**
    2257   * report values passed via bootstrap arguments
    2258   *
    2259   * TODO: significant code duplication with testouput(Argparser...) - move it
    2260   */
    2261  
    2262   protected static void testoutput(BootstrapArgs bsa, Arg a, String s1,
    2263   String s2)
    2264   {
    2265   if (!bsa.getBoolean(Arg.TESTOUTPUT))
    2266   return;
    2267   if (!((s1 == null && s2 == null) || (s1 != null && s1.equals(s2))))
    2268   {
    2269   Console.debug("testoutput with unmatching values '" + s1 + "' and '"
    2270   + s2 + "' for arg " + a.argString());
    2271   return;
    2272   }
    2273   if (!a.hasOption(Opt.BOOTSTRAP))
    2274   {
    2275   Console.error("Non-bootstrap Arg '" + a.getName()
    2276   + "' given to testoutput(BootstrapArgs bsa, Arg a, String s1, String s2) with only BootstrapArgs");
    2277   }
    2278   if (!bsa.contains(a))
    2279   {
    2280   Console.warn("Arg '" + a.getName() + "' not set at all");
    2281   return;
    2282   }
    2283   testoutput(true, a, s1, s2);
    2284   }
    2285  
    2286   /**
    2287   * conditionally (on @param yes) report that expected value s1 was set during
    2288   * CommandsTest tests
    2289   */
    2290   private static void testoutput(boolean yes, Arg a, String s1, String s2)
    2291   {
    2292   if (yes && ((s1 == null && s2 == null)
    2293   || (s1 != null && s1.equals(s2))))
    2294   {
    2295   Console.outPrintln("[TESTOUTPUT] arg " + a.argString() + "='" + s1
    2296   + "' was set");
    2297   }
    2298   }
    2299  
    2300   /*
    2301   * testoutput for boolean and unary values
    2302   */
    2303   protected static void testoutput(ArgParser ap, Arg a)
    2304   {
    2305   if (ap == null)
    2306   return;
    2307   BootstrapArgs bsa = ap.getBootstrapArgs();
    2308   if (bsa == null)
    2309   return;
    2310   if (!bsa.getBoolean(Arg.TESTOUTPUT))
    2311   return;
    2312   boolean val = a.hasOption(Opt.BOOTSTRAP) ? bsa.getBoolean(a)
    2313   : ap.getBoolean(a);
    2314   boolean isset = a.hasOption(Opt.BOOTSTRAP) ? bsa.contains(a)
    2315   : ap.isSet(a);
    2316   if (!isset)
    2317   {
    2318   Console.warn("Arg '" + a.getName() + "' not set at all");
    2319   return;
    2320   }
    2321   testoutput(val, a);
    2322   }
    2323  
    2324   protected static void testoutput(BootstrapArgs bsa, Arg a)
    2325   {
    2326   if (!bsa.getBoolean(Arg.TESTOUTPUT))
    2327   return;
    2328   if (!a.hasOption(Opt.BOOTSTRAP))
    2329   {
    2330   Console.warn("Non-bootstrap Arg '" + a.getName()
    2331   + "' given to testoutput(BootstrapArgs bsa, Arg a) with only BootstrapArgs");
    2332  
    2333   }
    2334   if (!bsa.contains(a))
    2335   {
    2336   Console.warn("Arg '" + a.getName() + "' not set at all");
    2337   return;
    2338   }
    2339   testoutput(bsa.getBoolean(a), a);
    2340   }
    2341  
    2342   private static void testoutput(boolean yes, Arg a)
    2343   {
    2344   String message = null;
    2345   if (a.hasOption(Opt.BOOLEAN))
    2346   {
    2347   message = (yes ? a.argString() : a.negateArgString()) + " was set";
    2348   }
    2349   else if (a.hasOption(Opt.UNARY))
    2350   {
    2351   message = a.argString() + (yes ? " was set" : " was not set");
    2352   }
    2353   Console.outPrintln("[TESTOUTPUT] arg " + message);
    2354   }
    2355  
    2356   public ArgParser getArgParser()
    2357   {
    2358   return argparser;
    2359   }
    2360  
    2361   public BootstrapArgs getBootstrapArgs()
    2362   {
    2363   return bootstrapArgs;
    2364   }
    2365  
    2366   public static boolean isBatchMode()
    2367   {
    2368   return instanceExists() && (getInstance().desktop == null
    2369   || getInstance().desktop.isInBatchMode());
    2370   }
    2371  
    2372   /**
    2373   * Warning about old or mixed command line arguments
    2374   */
    2375   private void mixedCliWarning()
    2376   {
    2377   Jalview j = Jalview.getInstance();
    2378   boolean mixedStyle = j.getArgParser() != null
    2379   && j.getArgParser().isMixedStyle();
    2380   String title = MessageManager.getString("label.command_line_arguments");
    2381   if (mixedStyle)
    2382   {
    2383   String warning = MessageManager.formatMessage(
    2384   "warning.using_mixed_command_line_arguments",
    2385   j.getArgParser().getMixedExamples());
    2386   String quit = MessageManager.getString("action.quit");
    2387  
    2388   Desktop.getInstance().nonBlockingDialog(title, warning, null, quit,
    2389   JvOptionPane.WARNING_MESSAGE, false, false, true, 30000);
    2390  
    2391   Jalview.exit(
    2392   "Exiting due to mixed old and new command line arguments.",
    2393   ExitCode.MIXED_CLI_ARGUMENTS);
    2394   }
    2395   }
    2396  
    2397   private void cliWarning()
    2398   {
    2399   Jalview j = Jalview.getInstance();
    2400   Commands c = j.getCommands();
    2401   boolean oldStyle = j.getArgParser() != null
    2402   && j.getArgParser().isOldStyle();
    2403   String title = MessageManager.getString("label.command_line_arguments");
    2404   if (oldStyle)
    2405   {
    2406   String warning = MessageManager
    2407   .getString("warning.using_old_command_line_arguments");
    2408   String url = "https://www.jalview.org/help/html/features/commandline.html";
    2409   if (Desktop.getInstance() != null)
    2410   {
    2411   String cont = MessageManager.getString("label.continue");
    2412  
    2413   Desktop.getInstance().nonBlockingDialog(title, warning, url, cont,
    2414   JvOptionPane.WARNING_MESSAGE, false, true, true, 30000);
    2415   }
    2416   }
    2417   if (j.getCommands() != null && j.getCommands().getErrors().size() > 0)
    2418   {
    2419   if (Desktop.getInstance() != null)
    2420   {
    2421   String message = MessageManager
    2422   .getString("warning.the_following_errors");
    2423   String ok = MessageManager.getString("action.ok");
    2424   int shortest = 60;
    2425   List errors = j.getCommands().getErrors();
    2426   for (int i = 0; i < errors.size(); i++)
    2427   {
    2428   shortest = Math.min(shortest, errors.get(i).length());
    2429   }
    2430   Desktop.getInstance().nonBlockingDialog(
    2431   Math.max(message.length(), Math.min(60, shortest)),
    2432   Math.min(errors.size(), 20), title, message,
    2433   j.getCommands().errorsToString(), ok,
    2434   JvOptionPane.WARNING_MESSAGE, true, false, true, -1);
    2435   }
    2436   }
    2437   }
    2438  
    2439   public void notifyWorker(AlignCalcWorkerI worker, String status)
    2440   {
    2441   // System.out.println("Jalview worker " + worker.getClass().getSimpleName()
    2442   // + " " + status);
    2443   }
    2444  
    2445   private static boolean isInteractive = true;
    2446  
    2447   public static boolean isInteractive()
    2448   {
    2449   return isInteractive;
    2450   }
    2451  
    2452   public static void setInteractive(boolean tf)
    2453   {
    2454   isInteractive = tf;
    2455   }
    2456   }