2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
23 import java.awt.Color;
24 import java.io.BufferedReader;
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;
35 import java.net.URISyntaxException;
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;
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;
54 import javax.swing.JDialog;
55 import javax.swing.JFrame;
56 import javax.swing.JOptionPane;
57 import javax.swing.SwingUtilities;
58 import javax.swing.UIManager;
59 import javax.swing.UIManager.LookAndFeelInfo;
60 import javax.swing.UnsupportedLookAndFeelException;
62 import com.formdev.flatlaf.FlatLightLaf;
63 import com.formdev.flatlaf.themes.FlatMacLightLaf;
64 import com.formdev.flatlaf.util.SystemInfo;
65 import com.threerings.getdown.util.LaunchUtil;
67 //import edu.stanford.ejalbert.launching.IBrowserLaunching;
68 import groovy.lang.Binding;
69 import groovy.util.GroovyScriptEngine;
70 import jalview.bin.argparser.Arg;
71 import jalview.bin.argparser.Arg.Opt;
72 import jalview.bin.argparser.Arg.Type;
73 import jalview.bin.argparser.ArgParser;
74 import jalview.bin.argparser.BootstrapArgs;
75 import jalview.ext.so.SequenceOntology;
76 import jalview.gui.AlignFrame;
77 import jalview.gui.Desktop;
78 import jalview.gui.PromptUserConfig;
79 import jalview.gui.QuitHandler;
80 import jalview.gui.QuitHandler.QResponse;
81 import jalview.io.AppletFormatAdapter;
82 import jalview.io.BioJsHTMLOutput;
83 import jalview.io.DataSourceType;
84 import jalview.io.FileFormat;
85 import jalview.io.FileFormatException;
86 import jalview.io.FileFormatI;
87 import jalview.io.FileFormats;
88 import jalview.io.FileLoader;
89 import jalview.io.HtmlSvgOutput;
90 import jalview.io.IdentifyFile;
91 import jalview.io.NewickFile;
92 import jalview.io.gff.SequenceOntologyFactory;
93 import jalview.schemes.ColourSchemeI;
94 import jalview.schemes.ColourSchemeProperty;
95 import jalview.util.ChannelProperties;
96 import jalview.util.HttpUtils;
97 import jalview.util.LaunchUtils;
98 import jalview.util.MessageManager;
99 import jalview.util.Platform;
100 import jalview.ws.jws2.Jws2Discoverer;
103 * Main class for Jalview Application <br>
105 * start with: java -classpath "$PATH_TO_LIB$/*:$PATH_TO_CLASSES$" \
106 * jalview.bin.Jalview
108 * or on Windows: java -classpath "$PATH_TO_LIB$/*;$PATH_TO_CLASSES$" \
109 * jalview.bin.Jalview jalview.bin.Jalview
111 * (ensure -classpath arg is quoted to avoid shell expansion of '*' and do not
112 * embellish '*' to e.g. '*.jar')
115 * @version $Revision$
121 Platform.getURLCommandArguments();
122 Platform.addJ2SDirectDatabaseCall("https://www.jalview.org");
123 Platform.addJ2SDirectDatabaseCall("http://www.jalview.org");
124 Platform.addJ2SDirectDatabaseCall("http://www.compbio.dundee.ac.uk");
125 Platform.addJ2SDirectDatabaseCall("https://www.compbio.dundee.ac.uk");
129 * singleton instance of this class
131 private static Jalview instance;
133 private Desktop desktop;
135 protected Commands cmds;
137 public static AlignFrame currentAlignFrame;
139 public ArgParser argparser = null;
141 public BootstrapArgs bootstrapArgs = null;
143 private boolean QUIET = false;
145 public static boolean quiet()
147 return Jalview.getInstance() != null && Jalview.getInstance().QUIET;
152 if (!Platform.isJS())
159 // grab all the rights we can for the JVM
160 Policy.setPolicy(new Policy()
163 public PermissionCollection getPermissions(CodeSource codesource)
165 Permissions perms = new Permissions();
166 perms.add(new AllPermission());
171 public void refresh()
179 * keep track of feature fetching tasks.
187 * TODO: generalise to track all jalview events to orchestrate batch processing
191 private int queued = 0;
193 private int running = 0;
195 public FeatureFetcher()
200 public void addFetcher(final AlignFrame af,
201 final Vector<String> dasSources)
203 final long id = System.currentTimeMillis();
205 final FeatureFetcher us = this;
206 new Thread(new Runnable()
218 af.setProgressBar(MessageManager
219 .getString("status.das_features_being_retrived"), id);
220 af.featureSettings_actionPerformed(null);
221 af.setProgressBar(null, id);
230 public synchronized boolean allFinished()
232 return queued == 0 && running == 0;
237 public static Jalview getInstance()
243 * main class for Jalview application
246 * open <em>filename</em>
248 public static void main(String[] args)
250 // setLogging(); // BH - for event debugging in JavaScript
251 instance = new Jalview();
252 instance.doMain(args);
255 private static void logClass(String name)
257 // BH - for event debugging in JavaScript
258 ConsoleHandler consoleHandler = new ConsoleHandler();
259 consoleHandler.setLevel(Level.ALL);
260 Logger logger = Logger.getLogger(name);
261 logger.setLevel(Level.ALL);
262 logger.addHandler(consoleHandler);
265 @SuppressWarnings("unused")
266 private static void setLogging()
274 System.out.println("not in js");
277 // BH - for event debugging in JavaScript (Java mode only)
278 if (!Platform.isJS())
285 Logger.getLogger("").setLevel(Level.ALL);
286 logClass("java.awt.EventDispatchThread");
287 logClass("java.awt.EventQueue");
288 logClass("java.awt.Component");
289 logClass("java.awt.focus.Component");
290 logClass("java.awt.focus.DefaultKeyboardFocusManager");
298 void doMain(String[] args)
300 if (!Platform.isJS())
302 System.setSecurityManager(null);
305 if (args == null || args.length == 0 || (args.length == 1
306 && (args[0] == null || args[0].length() == 0)))
308 args = new String[] {};
311 // get args needed before proper ArgParser
312 bootstrapArgs = BootstrapArgs.getBootstrapArgs(args);
314 if (!Platform.isJS())
316 // are we being --quiet ?
317 if (bootstrapArgs.contains(Arg.QUIET))
320 OutputStream devNull = new OutputStream()
324 public void write(int b)
329 System.setOut(new PrintStream(devNull));
330 // redirecting stderr not working
331 if (bootstrapArgs.getList(Arg.QUIET).size() > 1)
333 System.setErr(new PrintStream(devNull));
337 if (bootstrapArgs.contains(Arg.HELP)
338 || bootstrapArgs.contains(Arg.VERSION))
344 // Move any new getdown-launcher-new.jar into place over old
345 // getdown-launcher.jar
346 String appdirString = System.getProperty("getdownappdir");
347 if (appdirString != null && appdirString.length() > 0)
349 final File appdir = new File(appdirString);
356 LaunchUtil.upgradeGetdown(
357 new File(appdir, "getdown-launcher-old.jar"),
358 new File(appdir, "getdown-launcher.jar"),
359 new File(appdir, "getdown-launcher-new.jar"));
364 if (!quiet() || bootstrapArgs.contains(Arg.VERSION))
367 "Java version: " + System.getProperty("java.version"));
368 System.out.println("Java home: " + System.getProperty("java.home"));
369 System.out.println("Java arch: " + System.getProperty("os.arch") + " "
370 + System.getProperty("os.name") + " "
371 + System.getProperty("os.version"));
373 String val = System.getProperty("sys.install4jVersion");
376 System.out.println("Install4j version: " + val);
378 val = System.getProperty("installer_template_version");
381 System.out.println("Install4j template version: " + val);
383 val = System.getProperty("launcher_version");
386 System.out.println("Launcher version: " + val);
390 if (Platform.isLinux() && LaunchUtils.getJavaVersion() < 11)
392 System.setProperty("flatlaf.uiScale", "1");
395 // get bootstrap properties (mainly for the logger level)
396 Properties bootstrapProperties = Cache
397 .bootstrapProperties(bootstrapArgs.get(Arg.PROPS));
399 // report Jalview version
400 Cache.loadBuildProperties(
401 !quiet() || bootstrapArgs.contains(Arg.VERSION));
403 // stop now if only after --version
404 if (bootstrapArgs.contains(Arg.VERSION))
406 Jalview.exit(null, 0);
410 ArgsParser aparser = new ArgsParser(args);
413 boolean headless = false;
415 boolean headlessArg = false;
419 String logLevel = null;
420 if (bootstrapArgs.contains(Arg.TRACE))
424 else if (bootstrapArgs.contains(Arg.DEBUG))
428 if (logLevel == null && !(bootstrapProperties == null))
430 logLevel = bootstrapProperties.getProperty(Cache.JALVIEWLOGLEVEL);
432 Console.initLogger(logLevel);
433 } catch (NoClassDefFoundError error)
435 error.printStackTrace();
436 String message = "\nEssential logging libraries not found."
437 + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview";
438 Jalview.exit(message, 0);
441 // register SIGTERM listener
442 Runtime.getRuntime().addShutdownHook(new Thread()
446 Console.debug("Running shutdown hook");
447 if (QuitHandler.gotQuitResponse() == QResponse.CANCEL_QUIT)
449 // Got to here by a SIGTERM signal.
450 // Note we will not actually cancel the quit from here -- it's too
451 // late -- but we can wait for saving files.
452 Console.debug("Checking for saving files");
453 QuitHandler.getQuitResponse(false);
457 Console.debug("Nothing more to do");
459 Console.debug("Exiting, bye!");
460 // shutdownHook cannot be cancelled, JVM will now halt
464 String usrPropsFile = bootstrapArgs.contains(Arg.PROPS)
465 ? bootstrapArgs.get(Arg.PROPS)
466 : aparser.getValue("props");
467 // if usrPropsFile == null, loadProperties will use the Channel
469 Cache.loadProperties(usrPropsFile);
470 if (usrPropsFile != null)
473 "CMD [-props " + usrPropsFile + "] executed successfully!");
474 testoutput(bootstrapArgs, Arg.PROPS,
475 "test/jalview/bin/testProps.jvprops", usrPropsFile);
478 // --argfile=... -- OVERRIDES ALL NON-BOOTSTRAP ARGS
479 if (bootstrapArgs.contains(Arg.ARGFILE))
481 argparser = ArgParser.parseArgFiles(
482 bootstrapArgs.getValueList(Arg.ARGFILE),
483 bootstrapArgs.getBoolean(Arg.INITSUBSTITUTIONS),
488 argparser = new ArgParser(args,
489 bootstrapArgs.getBoolean(Arg.INITSUBSTITUTIONS),
493 if (!Platform.isJS())
500 if (bootstrapArgs.contains(Arg.HELP))
502 List<Map.Entry<Type, String>> helpArgs = bootstrapArgs
504 System.out.println(Arg.usage(helpArgs.stream().map(e -> e.getKey())
505 .collect(Collectors.toList())));
506 Jalview.exit(null, 0);
508 if (aparser.contains("help") || aparser.contains("h"))
511 * Now using new usage statement.
514 System.out.println(Arg.usage());
515 Jalview.exit(null, 0);
518 if (bootstrapArgs.contains(Arg.HEADLESS))
520 System.setProperty("java.awt.headless", "true");
522 headlessArg = bootstrapArgs.getBoolean(Arg.HEADLESS);
524 if (aparser.contains("nodisplay") || aparser.contains("nogui")
525 || aparser.contains("headless"))
527 System.setProperty("java.awt.headless", "true");
533 // allow https handshakes to download intermediate certs if necessary
534 System.setProperty("com.sun.security.enableAIAcaIssuers", "true");
536 String jabawsUrl = bootstrapArgs.get(Arg.JABAWS);
537 if (jabawsUrl == null)
538 jabawsUrl = aparser.getValue("jabaws");
539 if (jabawsUrl != null)
543 Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl);
545 "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
546 testoutput(bootstrapArgs, Arg.JABAWS,
547 "http://www.compbio.dundee.ac.uk/jabaws", jabawsUrl);
548 } catch (MalformedURLException e)
551 "Invalid jabaws parameter: " + jabawsUrl + " ignored");
556 List<String> setprops = new ArrayList<>();
557 if (bootstrapArgs.contains(Arg.SETPROP))
559 setprops = bootstrapArgs.getValueList(Arg.SETPROP);
563 String sp = aparser.getValue("setprop");
567 sp = aparser.getValue("setprop");
570 for (String setprop : setprops)
572 int p = setprop.indexOf('=');
576 .println("Ignoring invalid setprop argument : " + setprop);
580 System.out.println("Executing setprop argument: " + setprop);
583 Cache.setProperty(setprop.substring(0, p),
584 setprop.substring(p + 1));
586 // DISABLED FOR SECURITY REASONS
587 // TODO: add a property to allow properties to be overriden by cli args
588 // Cache.setProperty(setprop.substring(0,p), setprop.substring(p+1));
591 if (System.getProperty("java.awt.headless") != null
592 && System.getProperty("java.awt.headless").equals("true"))
596 System.setProperty("http.agent",
597 "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown"));
601 Console.initLogger();
604 NoClassDefFoundError error)
606 error.printStackTrace();
607 String message = "\nEssential logging libraries not found."
608 + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview";
609 Jalview.exit(message, 0);
613 if (!(headless || headlessArg))
617 * configure 'full' SO model if preferences say to, else use the default (full SO)
618 * - as JS currently doesn't have OBO parsing, it must use 'Lite' version
620 boolean soDefault = !Platform.isJS();
621 if (Cache.getDefault("USE_FULL_SO", soDefault))
623 SequenceOntologyFactory.setInstance(new SequenceOntology());
626 if (!(headless || headlessArg))
628 Desktop.nosplash = "false".equals(bootstrapArgs.get(Arg.SPLASH))
629 || aparser.contains("nosplash")
630 || Cache.getDefault("SPLASH", "true").equals("false");
631 desktop = new Desktop();
632 desktop.setInBatchMode(true); // indicate we are starting up
636 JalviewTaskbar.setTaskbar(this);
637 } catch (Exception e)
639 Console.info("Cannot set Taskbar");
640 Console.error(e.getMessage());
641 // e.printStackTrace();
642 } catch (Throwable t)
644 Console.info("Cannot set Taskbar");
645 Console.error(t.getMessage());
646 // t.printStackTrace();
649 // set Proxy settings before all the internet calls
650 Cache.setProxyPropertiesFromPreferences();
652 desktop.setVisible(true);
654 if (!Platform.isJS())
663 * Check to see that the JVM version being run is suitable for the Java
664 * version this Jalview was compiled for. Popup a warning if not.
666 if (!LaunchUtils.checkJavaVersion())
668 Console.warn("The Java version being used (Java "
669 + LaunchUtils.getJavaVersion()
670 + ") may lead to problems. This installation of Jalview should be used with Java "
671 + LaunchUtils.getJavaCompileVersion() + ".");
674 .getBooleanUserPreference("IGNORE_JVM_WARNING_POPUP"))
677 MessageManager.getString("label.continue") };
678 JOptionPane.showOptionDialog(null,
679 MessageManager.formatMessage(
680 "warning.wrong_jvm_version_message",
681 LaunchUtils.getJavaVersion(),
682 LaunchUtils.getJavaCompileVersion()),
684 .getString("warning.wrong_jvm_version_title"),
685 JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
686 null, options, options[0]);
690 boolean webservicediscovery = bootstrapArgs
691 .getBoolean(Arg.WEBSERVICEDISCOVERY);
692 if (aparser.contains("nowebservicediscovery"))
693 webservicediscovery = false;
694 if (webservicediscovery)
696 desktop.startServiceDiscovery();
700 testoutput(argparser, Arg.WEBSERVICEDISCOVERY);
703 boolean usagestats = bootstrapArgs.getBoolean(Arg.USAGESTATS);
704 if (aparser.contains("nousagestats"))
708 startUsageStats(desktop);
709 testoutput(argparser, Arg.USAGESTATS);
713 System.out.println("CMD [-nousagestats] executed successfully!");
714 testoutput(argparser, Arg.USAGESTATS);
717 boolean questionnaire = bootstrapArgs.getBoolean(Arg.QUESTIONNAIRE);
718 if (aparser.contains("noquestionnaire"))
719 questionnaire = false;
722 String url = aparser.getValue("questionnaire");
725 // Start the desktop questionnaire prompter with the specified
727 Console.debug("Starting questionnaire url at " + url);
728 desktop.checkForQuestionnaire(url);
729 System.out.println("CMD questionnaire[-" + url
730 + "] executed successfully!");
734 if (Cache.getProperty("NOQUESTIONNAIRES") == null)
736 // Start the desktop questionnaire prompter with the specified
739 // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
741 String defurl = "https://www.jalview.org/cgi-bin/questionnaire.pl";
743 "Starting questionnaire with default url: " + defurl);
744 desktop.checkForQuestionnaire(defurl);
751 .println("CMD [-noquestionnaire] executed successfully!");
752 testoutput(argparser, Arg.QUESTIONNAIRE);
755 if ((!aparser.contains("nonews")
756 && Cache.getProperty("NONEWS") == null
757 && !"false".equals(bootstrapArgs.get(Arg.NEWS)))
758 || "true".equals(bootstrapArgs.get(Arg.NEWS)))
760 desktop.checkForNews();
763 if (!aparser.contains("nohtmltemplates")
764 && Cache.getProperty("NOHTMLTEMPLATES") == null)
766 BioJsHTMLOutput.updateBioJS();
770 // Run Commands from cli
771 cmds = new Commands(argparser, headlessArg);
772 boolean commandsSuccess = cmds.argsWereParsed();
777 if (argparser.getBoolean(Arg.NOQUIT))
780 "Completed " + Arg.HEADLESS.getName() + " commands, but "
781 + Arg.NOQUIT + " is set so not quitting!");
785 Jalview.exit("Successfully completed commands in headless mode",
789 Console.info("Successfully completed commands");
795 Jalview.exit("Error when running Commands in headless mode", 1);
797 Console.warn("Error when running commands");
800 // Check if JVM and compile version might cause problems and log if it
802 if (headless && !Platform.isJS() && !LaunchUtils.checkJavaVersion())
804 Console.warn("The Java version being used (Java "
805 + LaunchUtils.getJavaVersion()
806 + ") may lead to problems. This installation of Jalview should be used with Java "
807 + LaunchUtils.getJavaCompileVersion() + ".");
810 String file = null, data = null;
812 FileFormatI format = null;
814 DataSourceType protocol = null;
816 FileLoader fileLoader = new FileLoader(!headless);
818 String groovyscript = null; // script to execute after all loading is
819 // completed one way or another
820 // extract groovy argument and execute if necessary
821 groovyscript = aparser.getValue("groovy", true);
822 file = aparser.getValue("open", true);
824 if (file == null && desktop == null && !commandsSuccess)
826 Jalview.exit("No files to open!", 1);
830 // Finally, deal with the remaining input data.
835 desktop.setProgressBar(
837 .getString("status.processing_commandline_args"),
838 progress = System.currentTimeMillis());
840 System.out.println("CMD [-open " + file + "] executed successfully!");
842 if (!Platform.isJS())
844 * ignore in JavaScript -- can't just file existence - could load it?
849 if (!HttpUtils.startsWithHttpOrHttps(file))
851 if (!(new File(file)).exists())
856 "Can't find file '" + file + "' in headless mode", 1);
858 Console.warn("Can't find file'" + file + "'");
863 protocol = AppletFormatAdapter.checkProtocol(file);
867 format = new IdentifyFile().identify(file, protocol);
868 } catch (FileFormatException e1)
873 AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
877 System.out.println("error");
881 setCurrentAlignFrame(af);
882 data = aparser.getValue("colour", true);
885 data.replaceAll("%20", " ");
887 ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
888 af.getViewport(), af.getViewport().getAlignment(), data);
893 "CMD [-colour " + data + "] executed successfully!");
898 // Must maintain ability to use the groups flag
899 data = aparser.getValue("groups", true);
902 af.parseFeaturesFile(data,
903 AppletFormatAdapter.checkProtocol(data));
904 // System.out.println("Added " + data);
906 "CMD groups[-" + data + "] executed successfully!");
908 data = aparser.getValue("features", true);
911 af.parseFeaturesFile(data,
912 AppletFormatAdapter.checkProtocol(data));
913 // System.out.println("Added " + data);
915 "CMD [-features " + data + "] executed successfully!");
918 data = aparser.getValue("annotations", true);
921 af.loadJalviewDataFile(data, null, null, null);
922 // System.out.println("Added " + data);
924 "CMD [-annotations " + data + "] executed successfully!");
926 // set or clear the sortbytree flag.
927 if (aparser.contains("sortbytree"))
929 af.getViewport().setSortByTree(true);
930 if (af.getViewport().getSortByTree())
932 System.out.println("CMD [-sortbytree] executed successfully!");
935 if (aparser.contains("no-annotation"))
937 af.getViewport().setShowAnnotation(false);
938 if (!af.getViewport().isShowAnnotation())
940 System.out.println("CMD no-annotation executed successfully!");
943 if (aparser.contains("nosortbytree"))
945 af.getViewport().setSortByTree(false);
946 if (!af.getViewport().getSortByTree())
949 .println("CMD [-nosortbytree] executed successfully!");
952 data = aparser.getValue("tree", true);
958 "CMD [-tree " + data + "] executed successfully!");
959 NewickFile nf = new NewickFile(data,
960 AppletFormatAdapter.checkProtocol(data));
962 .setCurrentTree(af.showNewickTree(nf, data).getTree());
963 } catch (IOException ex)
965 System.err.println("Couldn't add tree " + data);
966 ex.printStackTrace(System.err);
969 // TODO - load PDB structure(s) to alignment JAL-629
970 // (associate with identical sequence in alignment, or a specified
972 if (groovyscript != null)
974 // Execute the groovy script after we've done all the rendering stuff
975 // and before any images or figures are generated.
976 System.out.println("Executing script " + groovyscript);
977 executeGroovyScript(groovyscript, af);
978 System.out.println("CMD groovy[" + groovyscript
979 + "] executed successfully!");
982 String imageName = "unnamed.png";
983 while (aparser.getSize() > 1)
985 String outputFormat = aparser.nextValue();
986 file = aparser.nextValue();
988 if (outputFormat.equalsIgnoreCase("png"))
990 af.createPNG(new File(file));
991 imageName = (new File(file)).getName();
992 System.out.println("Creating PNG image: " + file);
995 else if (outputFormat.equalsIgnoreCase("svg"))
997 File imageFile = new File(file);
998 imageName = imageFile.getName();
999 af.createSVG(imageFile);
1000 System.out.println("Creating SVG image: " + file);
1003 else if (outputFormat.equalsIgnoreCase("html"))
1005 File imageFile = new File(file);
1006 imageName = imageFile.getName();
1007 HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
1008 htmlSVG.exportHTML(file);
1010 System.out.println("Creating HTML image: " + file);
1013 else if (outputFormat.equalsIgnoreCase("biojsmsa"))
1017 System.err.println("The output html file must not be null");
1022 BioJsHTMLOutput.refreshVersionInfo(
1023 BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
1024 } catch (URISyntaxException e)
1026 e.printStackTrace();
1028 BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
1029 bjs.exportHTML(file);
1031 .println("Creating BioJS MSA Viwer HTML file: " + file);
1034 else if (outputFormat.equalsIgnoreCase("imgMap"))
1036 af.createImageMap(new File(file), imageName);
1037 System.out.println("Creating image map: " + file);
1040 else if (outputFormat.equalsIgnoreCase("eps"))
1042 File outputFile = new File(file);
1044 "Creating EPS file: " + outputFile.getAbsolutePath());
1045 af.createEPS(outputFile);
1048 FileFormatI outFormat = null;
1051 outFormat = FileFormats.getInstance().forName(outputFormat);
1052 } catch (Exception formatP)
1054 System.out.println("Couldn't parse " + outFormat
1055 + " as a valid Jalview format string.");
1057 if (outFormat != null)
1059 if (!outFormat.isWritable())
1062 "This version of Jalview does not support alignment export as "
1067 af.saveAlignment(file, outFormat);
1068 if (af.isSaveAlignmentSuccessful())
1070 System.out.println("Written alignment in "
1071 + outFormat.getName() + " format to " + file);
1075 System.out.println("Error writing file " + file + " in "
1076 + outFormat.getName() + " format!!");
1083 while (aparser.getSize() > 0)
1085 System.out.println("Unknown arg: " + aparser.nextValue());
1090 AlignFrame startUpAlframe = null;
1091 // We'll only open the default file if the desktop is visible.
1093 // ////////////////////
1095 if (!Platform.isJS() && !headless && file == null
1096 && Cache.getDefault("SHOW_STARTUP_FILE", true)
1097 && !cmds.commandArgsProvided())
1098 // don't open the startup file if command line args have been processed
1099 // (&& !Commands.commandArgsProvided())
1106 file = Cache.getDefault("STARTUP_FILE",
1107 Cache.getDefault("www.jalview.org", "https://www.jalview.org")
1108 + "/examples/exampleFile_2_7.jvp");
1109 if (file.equals("http://www.jalview.org/examples/exampleFile_2_3.jar")
1111 "http://www.jalview.org/examples/exampleFile_2_7.jar"))
1113 file.replace("http:", "https:");
1114 // hardwire upgrade of the startup file
1115 file.replace("_2_3", "_2_7");
1116 file.replace("2_7.jar", "2_7.jvp");
1117 // and remove the stale setting
1118 Cache.removeProperty("STARTUP_FILE");
1121 protocol = AppletFormatAdapter.checkProtocol(file);
1123 if (file.endsWith(".jar"))
1125 format = FileFormat.Jalview;
1131 format = new IdentifyFile().identify(file, protocol);
1132 } catch (FileFormatException e)
1138 startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
1140 // don't ask to save when quitting if only the startup file has been
1142 Console.debug("Resetting up-to-date flag for startup file");
1143 startUpAlframe.getViewport().setSavedUpToDate(true);
1144 // extract groovy arguments before anything else.
1147 // Once all other stuff is done, execute any groovy scripts (in order)
1148 if (groovyscript != null)
1150 if (Cache.groovyJarsPresent())
1152 System.out.println("Executing script " + groovyscript);
1153 executeGroovyScript(groovyscript, startUpAlframe);
1158 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
1162 // and finally, turn off batch mode indicator - if the desktop still exists
1163 if (desktop != null)
1167 desktop.setProgressBar(null, progress);
1169 desktop.setInBatchMode(false);
1173 private static void setLookAndFeel()
1175 // property laf = "crossplatform", "system", "gtk", "metal", "nimbus",
1177 // If not set (or chosen laf fails), use the normal SystemLaF and if on Mac,
1178 // try Quaqua/Vaqua.
1179 String lafProp = System.getProperty("laf");
1180 String lafSetting = Cache.getDefault("PREFERRED_LAF", null);
1181 String laf = "none";
1182 if (lafProp != null)
1186 else if (lafSetting != null)
1190 boolean lafSet = false;
1193 case "crossplatform":
1194 lafSet = setCrossPlatformLookAndFeel();
1197 Console.error("Could not set requested laf=" + laf);
1201 lafSet = setSystemLookAndFeel();
1204 Console.error("Could not set requested laf=" + laf);
1208 lafSet = setGtkLookAndFeel();
1211 Console.error("Could not set requested laf=" + laf);
1215 lafSet = setMetalLookAndFeel();
1218 Console.error("Could not set requested laf=" + laf);
1222 lafSet = setNimbusLookAndFeel();
1225 Console.error("Could not set requested laf=" + laf);
1229 lafSet = setFlatLookAndFeel();
1232 Console.error("Could not set requested laf=" + laf);
1236 lafSet = setMacLookAndFeel();
1239 Console.error("Could not set requested laf=" + laf);
1245 Console.error("Requested laf=" + laf + " not implemented");
1249 setSystemLookAndFeel();
1250 if (Platform.isLinux())
1252 setLinuxLookAndFeel();
1254 if (Platform.isMac())
1256 setMacLookAndFeel();
1261 private static boolean setCrossPlatformLookAndFeel()
1263 boolean set = false;
1266 UIManager.setLookAndFeel(
1267 UIManager.getCrossPlatformLookAndFeelClassName());
1269 } catch (Exception ex)
1271 Console.error("Unexpected Look and Feel Exception");
1272 Console.error(ex.getMessage());
1273 Console.debug(Cache.getStackTraceString(ex));
1278 private static boolean setSystemLookAndFeel()
1280 boolean set = false;
1283 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
1285 } catch (Exception ex)
1287 Console.error("Unexpected Look and Feel Exception");
1288 Console.error(ex.getMessage());
1289 Console.debug(Cache.getStackTraceString(ex));
1294 private static boolean setSpecificLookAndFeel(String name,
1295 String className, boolean nameStartsWith)
1297 boolean set = false;
1300 for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
1302 if (info.getName() != null && nameStartsWith
1303 ? info.getName().toLowerCase(Locale.ROOT)
1304 .startsWith(name.toLowerCase(Locale.ROOT))
1305 : info.getName().toLowerCase(Locale.ROOT)
1306 .equals(name.toLowerCase(Locale.ROOT)))
1308 className = info.getClassName();
1312 UIManager.setLookAndFeel(className);
1314 } catch (Exception ex)
1316 Console.error("Unexpected Look and Feel Exception");
1317 Console.error(ex.getMessage());
1318 Console.debug(Cache.getStackTraceString(ex));
1323 private static boolean setGtkLookAndFeel()
1325 return setSpecificLookAndFeel("gtk",
1326 "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true);
1329 private static boolean setMetalLookAndFeel()
1331 return setSpecificLookAndFeel("metal",
1332 "javax.swing.plaf.metal.MetalLookAndFeel", false);
1335 private static boolean setNimbusLookAndFeel()
1337 return setSpecificLookAndFeel("nimbus",
1338 "javax.swing.plaf.nimbus.NimbusLookAndFeel", false);
1341 private static boolean setFlatLookAndFeel()
1343 boolean set = false;
1344 if (SystemInfo.isMacOS)
1348 UIManager.setLookAndFeel(
1349 "com.formdev.flatlaf.themes.FlatMacLightLaf");
1351 Console.debug("Using FlatMacLightLaf");
1352 } catch (ClassNotFoundException | InstantiationException
1353 | IllegalAccessException | UnsupportedLookAndFeelException e)
1355 Console.debug("Exception loading FlatLightLaf", e);
1357 System.setProperty("apple.laf.useScreenMenuBar", "true");
1358 System.setProperty("apple.awt.application.name",
1359 ChannelProperties.getProperty("app_name"));
1360 System.setProperty("apple.awt.application.appearance", "system");
1361 if (SystemInfo.isMacFullWindowContentSupported
1362 && Desktop.desktop != null)
1364 Console.debug("Setting transparent title bar");
1365 Desktop.desktop.getRootPane()
1366 .putClientProperty("apple.awt.fullWindowContent", true);
1367 Desktop.desktop.getRootPane()
1368 .putClientProperty("apple.awt.transparentTitleBar", true);
1369 Desktop.desktop.getRootPane()
1370 .putClientProperty("apple.awt.fullscreenable", true);
1372 SwingUtilities.invokeLater(() -> {
1373 FlatMacLightLaf.setup();
1375 Console.debug("Using FlatMacLightLaf");
1382 UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1384 Console.debug("Using FlatLightLaf");
1385 } catch (ClassNotFoundException | InstantiationException
1386 | IllegalAccessException | UnsupportedLookAndFeelException e)
1388 Console.debug("Exception loading FlatLightLaf", e);
1390 // Windows specific properties here
1391 SwingUtilities.invokeLater(() -> {
1392 FlatLightLaf.setup();
1394 Console.debug("Using FlatLightLaf");
1397 else if (SystemInfo.isLinux)
1401 UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1403 Console.debug("Using FlatLightLaf");
1404 } catch (ClassNotFoundException | InstantiationException
1405 | IllegalAccessException | UnsupportedLookAndFeelException e)
1407 Console.debug("Exception loading FlatLightLaf", e);
1409 // enable custom window decorations
1410 JFrame.setDefaultLookAndFeelDecorated(true);
1411 JDialog.setDefaultLookAndFeelDecorated(true);
1412 SwingUtilities.invokeLater(() -> {
1413 FlatLightLaf.setup();
1415 Console.debug("Using FlatLightLaf");
1423 UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1425 Console.debug("Using FlatLightLaf");
1426 } catch (ClassNotFoundException | InstantiationException
1427 | IllegalAccessException | UnsupportedLookAndFeelException e)
1429 Console.debug("Exception loading FlatLightLaf", e);
1435 UIManager.put("TabbedPane.tabType", "card");
1436 UIManager.put("TabbedPane.showTabSeparators", true);
1437 UIManager.put("TabbedPane.showContentSeparator", true);
1438 UIManager.put("TabbedPane.tabSeparatorsFullHeight", true);
1439 UIManager.put("TabbedPane.tabsOverlapBorder", true);
1440 UIManager.put("TabbedPane.hasFullBorder", true);
1441 UIManager.put("TabbedPane.tabLayoutPolicy", "scroll");
1442 UIManager.put("TabbedPane.scrollButtonsPolicy", "asNeeded");
1443 UIManager.put("TabbedPane.smoothScrolling", true);
1444 UIManager.put("TabbedPane.tabWidthMode", "compact");
1445 UIManager.put("TabbedPane.selectedBackground", Color.white);
1446 UIManager.put("TabbedPane.background", new Color(236, 236, 236));
1447 UIManager.put("TabbedPane.hoverColor", Color.lightGray);
1450 Desktop.setLiveDragMode(Cache.getDefault("FLAT_LIVE_DRAG_MODE", true));
1454 private static boolean setMacLookAndFeel()
1456 boolean set = false;
1457 System.setProperty("com.apple.mrj.application.apple.menu.about.name",
1458 ChannelProperties.getProperty("app_name"));
1459 System.setProperty("apple.laf.useScreenMenuBar", "true");
1461 * broken native LAFs on (ARM?) macbooks
1462 set = setQuaquaLookAndFeel();
1463 if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
1464 .toLowerCase(Locale.ROOT).contains("quaqua"))
1466 set = setVaquaLookAndFeel();
1469 set = setFlatLookAndFeel();
1473 private static boolean setLinuxLookAndFeel()
1475 boolean set = false;
1476 set = setFlatLookAndFeel();
1478 set = setMetalLookAndFeel();
1479 // avoid GtkLookAndFeel -- not good results especially on HiDPI
1481 set = setNimbusLookAndFeel();
1486 private static void showUsage()
1489 "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
1490 + "-nodisplay\tRun Jalview without User Interface.\n"
1491 + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
1492 + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
1493 + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
1494 + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
1495 + "-features FILE\tUse the given file to mark features on the alignment.\n"
1496 + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
1497 + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
1498 + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
1499 + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
1500 + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
1501 + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
1502 + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
1503 + "-json FILE\tCreate alignment file FILE in JSON format.\n"
1504 + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
1505 + "-png FILE\tCreate PNG image FILE from alignment.\n"
1506 + "-svg FILE\tCreate SVG image FILE from alignment.\n"
1507 + "-html FILE\tCreate HTML file from alignment.\n"
1508 + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
1509 + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
1510 + "-eps FILE\tCreate EPS file FILE from alignment.\n"
1511 + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
1512 + "-noquestionnaire\tTurn off questionnaire check.\n"
1513 + "-nonews\tTurn off check for Jalview news.\n"
1514 + "-nousagestats\tTurn off google analytics tracking for this session.\n"
1515 + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
1517 // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
1518 // after all other properties files have been read\n\t
1519 // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
1520 // passed in correctly)"
1521 + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
1522 + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
1523 + "-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"
1524 + "-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"
1525 + "-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"
1526 + "\n~Read documentation in Application or visit https://www.jalview.org for description of Features and Annotations file~\n\n");
1530 private static void startUsageStats(final Desktop desktop)
1533 * start a User Config prompt asking if we can log usage statistics.
1535 PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
1536 "USAGESTATS", "Jalview Usage Statistics",
1537 "Do you want to help make Jalview better by enabling "
1538 + "the collection of usage statistics with Google Analytics ?"
1539 + "\n\n(you can enable or disable usage tracking in the preferences)",
1546 "Initialising googletracker for usage stats.");
1547 Cache.initGoogleTracker();
1548 Console.debug("Tracking enabled.");
1555 Console.debug("Not enabling Google Tracking.");
1558 desktop.addDialogThread(prompter);
1562 * Locate the given string as a file and pass it to the groovy interpreter.
1564 * @param groovyscript
1565 * the script to execute
1566 * @param jalviewContext
1567 * the Jalview Desktop object passed in to the groovy binding as the
1570 protected void executeGroovyScript(String groovyscript, AlignFrame af)
1573 * for scripts contained in files
1580 if (groovyscript.trim().equals("STDIN"))
1582 // read from stdin into a tempfile and execute it
1585 tfile = File.createTempFile("jalview", "groovy");
1586 PrintWriter outfile = new PrintWriter(
1587 new OutputStreamWriter(new FileOutputStream(tfile)));
1588 BufferedReader br = new BufferedReader(
1589 new InputStreamReader(System.in));
1591 while ((line = br.readLine()) != null)
1593 outfile.write(line + "\n");
1599 } catch (Exception ex)
1601 System.err.println("Failed to read from STDIN into tempfile "
1602 + ((tfile == null) ? "(tempfile wasn't created)"
1603 : tfile.toString()));
1604 ex.printStackTrace();
1609 sfile = tfile.toURI().toURL();
1610 } catch (Exception x)
1613 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1615 x.printStackTrace();
1623 sfile = new URI(groovyscript).toURL();
1624 } catch (Exception x)
1626 tfile = new File(groovyscript);
1627 if (!tfile.exists())
1629 System.err.println("File '" + groovyscript + "' does not exist.");
1632 if (!tfile.canRead())
1634 System.err.println("File '" + groovyscript + "' cannot be read.");
1637 if (tfile.length() < 1)
1639 System.err.println("File '" + groovyscript + "' is empty.");
1644 sfile = tfile.getAbsoluteFile().toURI().toURL();
1645 } catch (Exception ex)
1647 System.err.println("Failed to create a file URL for "
1648 + tfile.getAbsoluteFile());
1655 Map<String, java.lang.Object> vbinding = new HashMap<>();
1656 vbinding.put("Jalview", this);
1659 vbinding.put("currentAlFrame", af);
1661 Binding gbinding = new Binding(vbinding);
1662 GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1663 gse.run(sfile.toString(), gbinding);
1664 if ("STDIN".equals(groovyscript))
1666 // delete temp file that we made -
1667 // only if it was successfully executed
1670 } catch (Exception e)
1672 System.err.println("Exception Whilst trying to execute file " + sfile
1673 + " as a groovy script.");
1674 e.printStackTrace(System.err);
1679 public static boolean isHeadlessMode()
1681 String isheadless = System.getProperty("java.awt.headless");
1682 if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1689 public AlignFrame[] getAlignFrames()
1691 return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1692 : Desktop.getAlignFrames();
1697 * jalview.bin.Jalview.quit() will just run the non-GUI shutdownHook and exit
1701 // System.exit will run the shutdownHook first
1702 Jalview.exit("Quitting now. Bye!", 0);
1705 public static AlignFrame getCurrentAlignFrame()
1707 return Jalview.currentAlignFrame;
1710 public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
1712 Jalview.currentAlignFrame = currentAlignFrame;
1715 protected Commands getCommands()
1720 public static void exit(String message, int exitcode)
1722 if (Console.log == null)
1724 // Don't start the logger just to exit!
1725 if (message != null)
1729 System.out.println(message);
1733 System.err.println(message);
1739 Console.debug("Using Jalview.exit");
1740 if (message != null)
1744 Console.info(message);
1748 Console.error(message);
1754 System.exit(exitcode);
1759 * testoutput for string values
1761 protected static void testoutput(ArgParser ap, Arg a, String s1,
1764 BootstrapArgs bsa = ap.getBootstrapArgs();
1765 if (!bsa.getBoolean(Arg.TESTOUTPUT))
1767 if (!((s1 == null && s2 == null) || (s1 != null && s1.equals(s2))))
1769 Console.debug("testoutput with unmatching values '" + s1 + "' and '"
1770 + s2 + "' for arg " + a.argString());
1773 boolean isset = a.hasOption(Opt.BOOTSTRAP) ? bsa.contains(a)
1777 Console.warn("Arg '" + a.getName() + "' not set at all");
1780 testoutput(true, a, s1, s2);
1783 protected static void testoutput(BootstrapArgs bsa, Arg a, String s1,
1786 if (!bsa.getBoolean(Arg.TESTOUTPUT))
1788 if (!((s1 == null && s2 == null) || (s1 != null && s1.equals(s2))))
1790 Console.debug("testoutput with unmatching values '" + s1 + "' and '"
1791 + s2 + "' for arg " + a.argString());
1794 if (!a.hasOption(Opt.BOOTSTRAP))
1796 Console.error("Non-bootstrap Arg '" + a.getName()
1797 + "' given to testoutput(BootstrapArgs bsa, Arg a, String s1, String s2) with only BootstrapArgs");
1799 if (!bsa.contains(a))
1801 Console.warn("Arg '" + a.getName() + "' not set at all");
1804 testoutput(true, a, s1, s2);
1807 private static void testoutput(boolean yes, Arg a, String s1, String s2)
1809 if (yes && ((s1 == null && s2 == null)
1810 || (s1 != null && s1.equals(s2))))
1812 System.out.println("[TESTOUTPUT] arg " + a.argString() + "='" + s1
1818 * testoutput for boolean values
1820 protected static void testoutput(ArgParser ap, Arg a)
1824 BootstrapArgs bsa = ap.getBootstrapArgs();
1827 if (!bsa.getBoolean(Arg.TESTOUTPUT))
1829 boolean val = a.hasOption(Opt.BOOTSTRAP) ? bsa.getBoolean(a)
1831 boolean isset = a.hasOption(Opt.BOOTSTRAP) ? bsa.contains(a)
1835 Console.warn("Arg '" + a.getName() + "' not set at all");
1841 protected static void testoutput(BootstrapArgs bsa, Arg a)
1843 if (!bsa.getBoolean(Arg.TESTOUTPUT))
1845 if (!a.hasOption(Opt.BOOTSTRAP))
1847 Console.warn("Non-bootstrap Arg '" + a.getName()
1848 + "' given to testoutput(BootstrapArgs bsa, Arg a) with only BootstrapArgs");
1851 if (!bsa.contains(a))
1853 Console.warn("Arg '" + a.getName() + "' not set at all");
1856 testoutput(bsa.getBoolean(a), a);
1859 private static void testoutput(boolean yes, Arg a)
1861 System.out.println("[TESTOUTPUT] arg "
1862 + (yes ? a.argString() : a.negateArgString()) + " was set");