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);
306 args = new String[] {};
308 // get args needed before proper ArgParser
309 bootstrapArgs = BootstrapArgs.getBootstrapArgs(args);
311 if (!Platform.isJS())
313 // are we being --quiet ?
314 if (bootstrapArgs.contains(Arg.QUIET))
317 OutputStream devNull = new OutputStream()
321 public void write(int b)
326 System.setOut(new PrintStream(devNull));
327 // redirecting stderr not working
328 if (bootstrapArgs.getList(Arg.QUIET).size() > 1)
330 System.setErr(new PrintStream(devNull));
334 if (bootstrapArgs.contains(Arg.HELP)
335 || bootstrapArgs.contains(Arg.VERSION))
341 // Move any new getdown-launcher-new.jar into place over old
342 // getdown-launcher.jar
343 String appdirString = System.getProperty("getdownappdir");
344 if (appdirString != null && appdirString.length() > 0)
346 final File appdir = new File(appdirString);
353 LaunchUtil.upgradeGetdown(
354 new File(appdir, "getdown-launcher-old.jar"),
355 new File(appdir, "getdown-launcher.jar"),
356 new File(appdir, "getdown-launcher-new.jar"));
361 if (!quiet() || bootstrapArgs.contains(Arg.VERSION))
364 "Java version: " + System.getProperty("java.version"));
365 System.out.println("Java home: " + System.getProperty("java.home"));
366 System.out.println("Java arch: " + System.getProperty("os.arch") + " "
367 + System.getProperty("os.name") + " "
368 + System.getProperty("os.version"));
370 String val = System.getProperty("sys.install4jVersion");
373 System.out.println("Install4j version: " + val);
375 val = System.getProperty("installer_template_version");
378 System.out.println("Install4j template version: " + val);
380 val = System.getProperty("launcher_version");
383 System.out.println("Launcher version: " + val);
387 if (Platform.isLinux() && LaunchUtils.getJavaVersion() < 11)
389 System.setProperty("flatlaf.uiScale", "1");
392 // get bootstrap properties (mainly for the logger level)
393 Properties bootstrapProperties = Cache
394 .bootstrapProperties(bootstrapArgs.get(Arg.PROPS));
396 // report Jalview version
397 Cache.loadBuildProperties(
398 !quiet() || bootstrapArgs.contains(Arg.VERSION));
400 // stop now if only after --version
401 if (bootstrapArgs.contains(Arg.VERSION))
403 Jalview.exit(null, 0);
407 ArgsParser aparser = new ArgsParser(args);
410 boolean headless = false;
412 boolean headlessArg = false;
416 String logLevel = null;
417 if (bootstrapArgs.contains(Arg.TRACE))
421 else if (bootstrapArgs.contains(Arg.DEBUG))
425 if (logLevel == null && !(bootstrapProperties == null))
427 logLevel = bootstrapProperties.getProperty(Cache.JALVIEWLOGLEVEL);
429 Console.initLogger(logLevel);
430 } catch (NoClassDefFoundError error)
432 error.printStackTrace();
433 String message = "\nEssential logging libraries not found."
434 + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview";
435 Jalview.exit(message, 0);
438 // register SIGTERM listener
439 Runtime.getRuntime().addShutdownHook(new Thread()
443 Console.debug("Running shutdown hook");
444 if (QuitHandler.gotQuitResponse() == QResponse.CANCEL_QUIT)
446 // Got to here by a SIGTERM signal.
447 // Note we will not actually cancel the quit from here -- it's too
448 // late -- but we can wait for saving files.
449 Console.debug("Checking for saving files");
450 QuitHandler.getQuitResponse(false);
454 Console.debug("Nothing more to do");
456 Console.debug("Exiting, bye!");
457 // shutdownHook cannot be cancelled, JVM will now halt
461 String usrPropsFile = bootstrapArgs.contains(Arg.PROPS)
462 ? bootstrapArgs.get(Arg.PROPS)
463 : aparser.getValue("props");
464 // if usrPropsFile == null, loadProperties will use the Channel
466 Cache.loadProperties(usrPropsFile);
467 if (usrPropsFile != null)
470 "CMD [-props " + usrPropsFile + "] executed successfully!");
471 testoutput(bootstrapArgs, Arg.PROPS,
472 "test/jalview/bin/testProps.jvprops", usrPropsFile);
475 // --argfile=... -- OVERRIDES ALL NON-BOOTSTRAP ARGS
476 if (bootstrapArgs.contains(Arg.ARGFILE))
478 argparser = ArgParser.parseArgFiles(
479 bootstrapArgs.getValueList(Arg.ARGFILE),
480 bootstrapArgs.getBoolean(Arg.INITSUBSTITUTIONS),
485 argparser = new ArgParser(args,
486 bootstrapArgs.getBoolean(Arg.INITSUBSTITUTIONS),
490 if (!Platform.isJS())
497 if (bootstrapArgs.contains(Arg.HELP))
499 List<Map.Entry<Type, String>> helpArgs = bootstrapArgs
501 System.out.println(Arg.usage(helpArgs.stream().map(e -> e.getKey())
502 .collect(Collectors.toList())));
503 Jalview.exit(null, 0);
505 if (aparser.contains("help") || aparser.contains("h"))
508 * Now using new usage statement.
511 System.out.println(Arg.usage());
512 Jalview.exit(null, 0);
515 if (bootstrapArgs.contains(Arg.HEADLESS))
517 System.setProperty("java.awt.headless", "true");
519 headlessArg = bootstrapArgs.getBoolean(Arg.HEADLESS);
521 if (aparser.contains("nodisplay") || aparser.contains("nogui")
522 || aparser.contains("headless"))
524 System.setProperty("java.awt.headless", "true");
530 // allow https handshakes to download intermediate certs if necessary
531 System.setProperty("com.sun.security.enableAIAcaIssuers", "true");
533 String jabawsUrl = bootstrapArgs.get(Arg.JABAWS);
534 if (jabawsUrl == null)
535 jabawsUrl = aparser.getValue("jabaws");
536 if (jabawsUrl != null)
540 Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl);
542 "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
543 testoutput(bootstrapArgs, Arg.JABAWS,
544 "http://www.compbio.dundee.ac.uk/jabaws", jabawsUrl);
545 } catch (MalformedURLException e)
548 "Invalid jabaws parameter: " + jabawsUrl + " ignored");
553 List<String> setprops = new ArrayList<>();
554 if (bootstrapArgs.contains(Arg.SETPROP))
556 setprops = bootstrapArgs.getValueList(Arg.SETPROP);
560 String sp = aparser.getValue("setprop");
564 sp = aparser.getValue("setprop");
567 for (String setprop : setprops)
569 int p = setprop.indexOf('=');
573 .println("Ignoring invalid setprop argument : " + setprop);
577 System.out.println("Executing setprop argument: " + setprop);
580 Cache.setProperty(setprop.substring(0, p),
581 setprop.substring(p + 1));
583 // DISABLED FOR SECURITY REASONS
584 // TODO: add a property to allow properties to be overriden by cli args
585 // Cache.setProperty(setprop.substring(0,p), setprop.substring(p+1));
588 if (System.getProperty("java.awt.headless") != null
589 && System.getProperty("java.awt.headless").equals("true"))
593 System.setProperty("http.agent",
594 "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown"));
598 Console.initLogger();
601 NoClassDefFoundError error)
603 error.printStackTrace();
604 String message = "\nEssential logging libraries not found."
605 + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview";
606 Jalview.exit(message, 0);
610 if (!(headless || headlessArg))
614 * configure 'full' SO model if preferences say to, else use the default (full SO)
615 * - as JS currently doesn't have OBO parsing, it must use 'Lite' version
617 boolean soDefault = !Platform.isJS();
618 if (Cache.getDefault("USE_FULL_SO", soDefault))
620 SequenceOntologyFactory.setInstance(new SequenceOntology());
623 if (!(headless || headlessArg))
625 Desktop.nosplash = "false".equals(bootstrapArgs.get(Arg.SPLASH))
626 || aparser.contains("nosplash")
627 || Cache.getDefault("SPLASH", "true").equals("false");
628 desktop = new Desktop();
629 desktop.setInBatchMode(true); // indicate we are starting up
633 JalviewTaskbar.setTaskbar(this);
634 } catch (Exception e)
636 Console.info("Cannot set Taskbar");
637 Console.error(e.getMessage());
638 // e.printStackTrace();
639 } catch (Throwable t)
641 Console.info("Cannot set Taskbar");
642 Console.error(t.getMessage());
643 // t.printStackTrace();
646 // set Proxy settings before all the internet calls
647 Cache.setProxyPropertiesFromPreferences();
649 desktop.setVisible(true);
651 if (!Platform.isJS())
660 * Check to see that the JVM version being run is suitable for the Java
661 * version this Jalview was compiled for. Popup a warning if not.
663 if (!LaunchUtils.checkJavaVersion())
665 Console.warn("The Java version being used (Java "
666 + LaunchUtils.getJavaVersion()
667 + ") may lead to problems. This installation of Jalview should be used with Java "
668 + LaunchUtils.getJavaCompileVersion() + ".");
671 .getBooleanUserPreference("IGNORE_JVM_WARNING_POPUP"))
674 MessageManager.getString("label.continue") };
675 JOptionPane.showOptionDialog(null,
676 MessageManager.formatMessage(
677 "warning.wrong_jvm_version_message",
678 LaunchUtils.getJavaVersion(),
679 LaunchUtils.getJavaCompileVersion()),
681 .getString("warning.wrong_jvm_version_title"),
682 JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
683 null, options, options[0]);
687 boolean webservicediscovery = bootstrapArgs
688 .getBoolean(Arg.WEBSERVICEDISCOVERY);
689 if (aparser.contains("nowebservicediscovery"))
690 webservicediscovery = false;
691 if (webservicediscovery)
693 desktop.startServiceDiscovery();
697 testoutput(argparser, Arg.WEBSERVICEDISCOVERY);
700 boolean usagestats = bootstrapArgs.getBoolean(Arg.USAGESTATS);
701 if (aparser.contains("nousagestats"))
705 startUsageStats(desktop);
706 testoutput(argparser, Arg.USAGESTATS);
710 System.out.println("CMD [-nousagestats] executed successfully!");
711 testoutput(argparser, Arg.USAGESTATS);
714 boolean questionnaire = bootstrapArgs.getBoolean(Arg.QUESTIONNAIRE);
715 if (aparser.contains("noquestionnaire"))
716 questionnaire = false;
719 String url = aparser.getValue("questionnaire");
722 // Start the desktop questionnaire prompter with the specified
724 Console.debug("Starting questionnaire url at " + url);
725 desktop.checkForQuestionnaire(url);
726 System.out.println("CMD questionnaire[-" + url
727 + "] executed successfully!");
731 if (Cache.getProperty("NOQUESTIONNAIRES") == null)
733 // Start the desktop questionnaire prompter with the specified
736 // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
738 String defurl = "https://www.jalview.org/cgi-bin/questionnaire.pl";
740 "Starting questionnaire with default url: " + defurl);
741 desktop.checkForQuestionnaire(defurl);
748 .println("CMD [-noquestionnaire] executed successfully!");
749 testoutput(argparser, Arg.QUESTIONNAIRE);
752 if ((!aparser.contains("nonews")
753 && Cache.getProperty("NONEWS") == null
754 && !"false".equals(bootstrapArgs.get(Arg.NEWS)))
755 || "true".equals(bootstrapArgs.get(Arg.NEWS)))
757 desktop.checkForNews();
760 if (!aparser.contains("nohtmltemplates")
761 && Cache.getProperty("NOHTMLTEMPLATES") == null)
763 BioJsHTMLOutput.updateBioJS();
767 // Run Commands from cli
768 cmds = new Commands(argparser, headlessArg);
769 boolean commandsSuccess = cmds.argsWereParsed();
774 Jalview.exit("Successfully completed commands in headless mode", 0);
776 Console.info("Successfully completed commands");
782 Jalview.exit("Error when running Commands in headless mode", 1);
784 Console.warn("Error when running commands");
787 // Check if JVM and compile version might cause problems and log if it
789 if (headless && !Platform.isJS() && !LaunchUtils.checkJavaVersion())
791 Console.warn("The Java version being used (Java "
792 + LaunchUtils.getJavaVersion()
793 + ") may lead to problems. This installation of Jalview should be used with Java "
794 + LaunchUtils.getJavaCompileVersion() + ".");
797 String file = null, data = null;
799 FileFormatI format = null;
801 DataSourceType protocol = null;
803 FileLoader fileLoader = new FileLoader(!headless);
805 String groovyscript = null; // script to execute after all loading is
806 // completed one way or another
807 // extract groovy argument and execute if necessary
808 groovyscript = aparser.getValue("groovy", true);
809 file = aparser.getValue("open", true);
811 if (file == null && desktop == null && !commandsSuccess)
813 Jalview.exit("No files to open!", 1);
817 // Finally, deal with the remaining input data.
822 desktop.setProgressBar(
824 .getString("status.processing_commandline_args"),
825 progress = System.currentTimeMillis());
827 System.out.println("CMD [-open " + file + "] executed successfully!");
829 if (!Platform.isJS())
831 * ignore in JavaScript -- can't just file existence - could load it?
836 if (!HttpUtils.startsWithHttpOrHttps(file))
838 if (!(new File(file)).exists())
843 "Can't find file '" + file + "' in headless mode", 1);
845 Console.warn("Can't find file'" + file + "'");
850 protocol = AppletFormatAdapter.checkProtocol(file);
854 format = new IdentifyFile().identify(file, protocol);
855 } catch (FileFormatException e1)
860 AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
864 System.out.println("error");
868 setCurrentAlignFrame(af);
869 data = aparser.getValue("colour", true);
872 data.replaceAll("%20", " ");
874 ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
875 af.getViewport(), af.getViewport().getAlignment(), data);
880 "CMD [-colour " + data + "] executed successfully!");
885 // Must maintain ability to use the groups flag
886 data = aparser.getValue("groups", true);
889 af.parseFeaturesFile(data,
890 AppletFormatAdapter.checkProtocol(data));
891 // System.out.println("Added " + data);
893 "CMD groups[-" + data + "] executed successfully!");
895 data = aparser.getValue("features", true);
898 af.parseFeaturesFile(data,
899 AppletFormatAdapter.checkProtocol(data));
900 // System.out.println("Added " + data);
902 "CMD [-features " + data + "] executed successfully!");
905 data = aparser.getValue("annotations", true);
908 af.loadJalviewDataFile(data, null, null, null);
909 // System.out.println("Added " + data);
911 "CMD [-annotations " + data + "] executed successfully!");
913 // set or clear the sortbytree flag.
914 if (aparser.contains("sortbytree"))
916 af.getViewport().setSortByTree(true);
917 if (af.getViewport().getSortByTree())
919 System.out.println("CMD [-sortbytree] executed successfully!");
922 if (aparser.contains("no-annotation"))
924 af.getViewport().setShowAnnotation(false);
925 if (!af.getViewport().isShowAnnotation())
927 System.out.println("CMD no-annotation executed successfully!");
930 if (aparser.contains("nosortbytree"))
932 af.getViewport().setSortByTree(false);
933 if (!af.getViewport().getSortByTree())
936 .println("CMD [-nosortbytree] executed successfully!");
939 data = aparser.getValue("tree", true);
945 "CMD [-tree " + data + "] executed successfully!");
946 NewickFile nf = new NewickFile(data,
947 AppletFormatAdapter.checkProtocol(data));
949 .setCurrentTree(af.showNewickTree(nf, data).getTree());
950 } catch (IOException ex)
952 System.err.println("Couldn't add tree " + data);
953 ex.printStackTrace(System.err);
956 // TODO - load PDB structure(s) to alignment JAL-629
957 // (associate with identical sequence in alignment, or a specified
959 if (groovyscript != null)
961 // Execute the groovy script after we've done all the rendering stuff
962 // and before any images or figures are generated.
963 System.out.println("Executing script " + groovyscript);
964 executeGroovyScript(groovyscript, af);
965 System.out.println("CMD groovy[" + groovyscript
966 + "] executed successfully!");
969 String imageName = "unnamed.png";
970 while (aparser.getSize() > 1)
972 String outputFormat = aparser.nextValue();
973 file = aparser.nextValue();
975 if (outputFormat.equalsIgnoreCase("png"))
977 af.createPNG(new File(file));
978 imageName = (new File(file)).getName();
979 System.out.println("Creating PNG image: " + file);
982 else if (outputFormat.equalsIgnoreCase("svg"))
984 File imageFile = new File(file);
985 imageName = imageFile.getName();
986 af.createSVG(imageFile);
987 System.out.println("Creating SVG image: " + file);
990 else if (outputFormat.equalsIgnoreCase("html"))
992 File imageFile = new File(file);
993 imageName = imageFile.getName();
994 HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
995 htmlSVG.exportHTML(file);
997 System.out.println("Creating HTML image: " + file);
1000 else if (outputFormat.equalsIgnoreCase("biojsmsa"))
1004 System.err.println("The output html file must not be null");
1009 BioJsHTMLOutput.refreshVersionInfo(
1010 BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
1011 } catch (URISyntaxException e)
1013 e.printStackTrace();
1015 BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
1016 bjs.exportHTML(file);
1018 .println("Creating BioJS MSA Viwer HTML file: " + file);
1021 else if (outputFormat.equalsIgnoreCase("imgMap"))
1023 af.createImageMap(new File(file), imageName);
1024 System.out.println("Creating image map: " + file);
1027 else if (outputFormat.equalsIgnoreCase("eps"))
1029 File outputFile = new File(file);
1031 "Creating EPS file: " + outputFile.getAbsolutePath());
1032 af.createEPS(outputFile);
1035 FileFormatI outFormat = null;
1038 outFormat = FileFormats.getInstance().forName(outputFormat);
1039 } catch (Exception formatP)
1041 System.out.println("Couldn't parse " + outFormat
1042 + " as a valid Jalview format string.");
1044 if (outFormat != null)
1046 if (!outFormat.isWritable())
1049 "This version of Jalview does not support alignment export as "
1054 af.saveAlignment(file, outFormat);
1055 if (af.isSaveAlignmentSuccessful())
1057 System.out.println("Written alignment in "
1058 + outFormat.getName() + " format to " + file);
1062 System.out.println("Error writing file " + file + " in "
1063 + outFormat.getName() + " format!!");
1070 while (aparser.getSize() > 0)
1072 System.out.println("Unknown arg: " + aparser.nextValue());
1077 AlignFrame startUpAlframe = null;
1078 // We'll only open the default file if the desktop is visible.
1080 // ////////////////////
1082 if (!Platform.isJS() && !headless && file == null
1083 && Cache.getDefault("SHOW_STARTUP_FILE", true)
1084 && !cmds.commandArgsProvided())
1085 // don't open the startup file if command line args have been processed
1086 // (&& !Commands.commandArgsProvided())
1093 file = Cache.getDefault("STARTUP_FILE",
1094 Cache.getDefault("www.jalview.org", "https://www.jalview.org")
1095 + "/examples/exampleFile_2_7.jvp");
1096 if (file.equals("http://www.jalview.org/examples/exampleFile_2_3.jar")
1098 "http://www.jalview.org/examples/exampleFile_2_7.jar"))
1100 file.replace("http:", "https:");
1101 // hardwire upgrade of the startup file
1102 file.replace("_2_3", "_2_7");
1103 file.replace("2_7.jar", "2_7.jvp");
1104 // and remove the stale setting
1105 Cache.removeProperty("STARTUP_FILE");
1108 protocol = AppletFormatAdapter.checkProtocol(file);
1110 if (file.endsWith(".jar"))
1112 format = FileFormat.Jalview;
1118 format = new IdentifyFile().identify(file, protocol);
1119 } catch (FileFormatException e)
1125 startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
1127 // don't ask to save when quitting if only the startup file has been
1129 Console.debug("Resetting up-to-date flag for startup file");
1130 startUpAlframe.getViewport().setSavedUpToDate(true);
1131 // extract groovy arguments before anything else.
1134 // Once all other stuff is done, execute any groovy scripts (in order)
1135 if (groovyscript != null)
1137 if (Cache.groovyJarsPresent())
1139 System.out.println("Executing script " + groovyscript);
1140 executeGroovyScript(groovyscript, startUpAlframe);
1145 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
1149 // and finally, turn off batch mode indicator - if the desktop still exists
1150 if (desktop != null)
1154 desktop.setProgressBar(null, progress);
1156 desktop.setInBatchMode(false);
1160 private static void setLookAndFeel()
1162 // property laf = "crossplatform", "system", "gtk", "metal", "nimbus",
1164 // If not set (or chosen laf fails), use the normal SystemLaF and if on Mac,
1165 // try Quaqua/Vaqua.
1166 String lafProp = System.getProperty("laf");
1167 String lafSetting = Cache.getDefault("PREFERRED_LAF", null);
1168 String laf = "none";
1169 if (lafProp != null)
1173 else if (lafSetting != null)
1177 boolean lafSet = false;
1180 case "crossplatform":
1181 lafSet = setCrossPlatformLookAndFeel();
1184 Console.error("Could not set requested laf=" + laf);
1188 lafSet = setSystemLookAndFeel();
1191 Console.error("Could not set requested laf=" + laf);
1195 lafSet = setGtkLookAndFeel();
1198 Console.error("Could not set requested laf=" + laf);
1202 lafSet = setMetalLookAndFeel();
1205 Console.error("Could not set requested laf=" + laf);
1209 lafSet = setNimbusLookAndFeel();
1212 Console.error("Could not set requested laf=" + laf);
1216 lafSet = setFlatLookAndFeel();
1219 Console.error("Could not set requested laf=" + laf);
1223 lafSet = setMacLookAndFeel();
1226 Console.error("Could not set requested laf=" + laf);
1232 Console.error("Requested laf=" + laf + " not implemented");
1236 setSystemLookAndFeel();
1237 if (Platform.isLinux())
1239 setLinuxLookAndFeel();
1241 if (Platform.isMac())
1243 setMacLookAndFeel();
1248 private static boolean setCrossPlatformLookAndFeel()
1250 boolean set = false;
1253 UIManager.setLookAndFeel(
1254 UIManager.getCrossPlatformLookAndFeelClassName());
1256 } catch (Exception ex)
1258 Console.error("Unexpected Look and Feel Exception");
1259 Console.error(ex.getMessage());
1260 Console.debug(Cache.getStackTraceString(ex));
1265 private static boolean setSystemLookAndFeel()
1267 boolean set = false;
1270 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
1272 } catch (Exception ex)
1274 Console.error("Unexpected Look and Feel Exception");
1275 Console.error(ex.getMessage());
1276 Console.debug(Cache.getStackTraceString(ex));
1281 private static boolean setSpecificLookAndFeel(String name,
1282 String className, boolean nameStartsWith)
1284 boolean set = false;
1287 for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
1289 if (info.getName() != null && nameStartsWith
1290 ? info.getName().toLowerCase(Locale.ROOT)
1291 .startsWith(name.toLowerCase(Locale.ROOT))
1292 : info.getName().toLowerCase(Locale.ROOT)
1293 .equals(name.toLowerCase(Locale.ROOT)))
1295 className = info.getClassName();
1299 UIManager.setLookAndFeel(className);
1301 } catch (Exception ex)
1303 Console.error("Unexpected Look and Feel Exception");
1304 Console.error(ex.getMessage());
1305 Console.debug(Cache.getStackTraceString(ex));
1310 private static boolean setGtkLookAndFeel()
1312 return setSpecificLookAndFeel("gtk",
1313 "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true);
1316 private static boolean setMetalLookAndFeel()
1318 return setSpecificLookAndFeel("metal",
1319 "javax.swing.plaf.metal.MetalLookAndFeel", false);
1322 private static boolean setNimbusLookAndFeel()
1324 return setSpecificLookAndFeel("nimbus",
1325 "javax.swing.plaf.nimbus.NimbusLookAndFeel", false);
1328 private static boolean setFlatLookAndFeel()
1330 boolean set = false;
1331 if (SystemInfo.isMacOS)
1335 UIManager.setLookAndFeel(
1336 "com.formdev.flatlaf.themes.FlatMacLightLaf");
1338 Console.debug("Using FlatMacLightLaf");
1339 } catch (ClassNotFoundException | InstantiationException
1340 | IllegalAccessException | UnsupportedLookAndFeelException e)
1342 Console.debug("Exception loading FlatLightLaf", e);
1344 System.setProperty("apple.laf.useScreenMenuBar", "true");
1345 System.setProperty("apple.awt.application.name",
1346 ChannelProperties.getProperty("app_name"));
1347 System.setProperty("apple.awt.application.appearance", "system");
1348 if (SystemInfo.isMacFullWindowContentSupported
1349 && Desktop.desktop != null)
1351 Console.debug("Setting transparent title bar");
1352 Desktop.desktop.getRootPane()
1353 .putClientProperty("apple.awt.fullWindowContent", true);
1354 Desktop.desktop.getRootPane()
1355 .putClientProperty("apple.awt.transparentTitleBar", true);
1356 Desktop.desktop.getRootPane()
1357 .putClientProperty("apple.awt.fullscreenable", true);
1359 SwingUtilities.invokeLater(() -> {
1360 FlatMacLightLaf.setup();
1362 Console.debug("Using FlatMacLightLaf");
1369 UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1371 Console.debug("Using FlatLightLaf");
1372 } catch (ClassNotFoundException | InstantiationException
1373 | IllegalAccessException | UnsupportedLookAndFeelException e)
1375 Console.debug("Exception loading FlatLightLaf", e);
1377 // Windows specific properties here
1378 SwingUtilities.invokeLater(() -> {
1379 FlatLightLaf.setup();
1381 Console.debug("Using FlatLightLaf");
1384 else if (SystemInfo.isLinux)
1388 UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1390 Console.debug("Using FlatLightLaf");
1391 } catch (ClassNotFoundException | InstantiationException
1392 | IllegalAccessException | UnsupportedLookAndFeelException e)
1394 Console.debug("Exception loading FlatLightLaf", e);
1396 // enable custom window decorations
1397 JFrame.setDefaultLookAndFeelDecorated(true);
1398 JDialog.setDefaultLookAndFeelDecorated(true);
1399 SwingUtilities.invokeLater(() -> {
1400 FlatLightLaf.setup();
1402 Console.debug("Using FlatLightLaf");
1410 UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1412 Console.debug("Using FlatLightLaf");
1413 } catch (ClassNotFoundException | InstantiationException
1414 | IllegalAccessException | UnsupportedLookAndFeelException e)
1416 Console.debug("Exception loading FlatLightLaf", e);
1422 UIManager.put("TabbedPane.tabType", "card");
1423 UIManager.put("TabbedPane.showTabSeparators", true);
1424 UIManager.put("TabbedPane.showContentSeparator", true);
1425 UIManager.put("TabbedPane.tabSeparatorsFullHeight", true);
1426 UIManager.put("TabbedPane.tabsOverlapBorder", true);
1427 UIManager.put("TabbedPane.hasFullBorder", true);
1428 UIManager.put("TabbedPane.tabLayoutPolicy", "scroll");
1429 UIManager.put("TabbedPane.scrollButtonsPolicy", "asNeeded");
1430 UIManager.put("TabbedPane.smoothScrolling", true);
1431 UIManager.put("TabbedPane.tabWidthMode", "compact");
1432 UIManager.put("TabbedPane.selectedBackground", Color.white);
1435 Desktop.setLiveDragMode(Cache.getDefault("FLAT_LIVE_DRAG_MODE", true));
1439 private static boolean setMacLookAndFeel()
1441 boolean set = false;
1442 System.setProperty("com.apple.mrj.application.apple.menu.about.name",
1443 ChannelProperties.getProperty("app_name"));
1444 System.setProperty("apple.laf.useScreenMenuBar", "true");
1446 * broken native LAFs on (ARM?) macbooks
1447 set = setQuaquaLookAndFeel();
1448 if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
1449 .toLowerCase(Locale.ROOT).contains("quaqua"))
1451 set = setVaquaLookAndFeel();
1454 set = setFlatLookAndFeel();
1458 private static boolean setLinuxLookAndFeel()
1460 boolean set = false;
1461 set = setFlatLookAndFeel();
1463 set = setMetalLookAndFeel();
1464 // avoid GtkLookAndFeel -- not good results especially on HiDPI
1466 set = setNimbusLookAndFeel();
1470 private static void showUsage()
1473 "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
1474 + "-nodisplay\tRun Jalview without User Interface.\n"
1475 + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
1476 + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
1477 + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
1478 + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
1479 + "-features FILE\tUse the given file to mark features on the alignment.\n"
1480 + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
1481 + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
1482 + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
1483 + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
1484 + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
1485 + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
1486 + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
1487 + "-json FILE\tCreate alignment file FILE in JSON format.\n"
1488 + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
1489 + "-png FILE\tCreate PNG image FILE from alignment.\n"
1490 + "-svg FILE\tCreate SVG image FILE from alignment.\n"
1491 + "-html FILE\tCreate HTML file from alignment.\n"
1492 + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
1493 + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
1494 + "-eps FILE\tCreate EPS file FILE from alignment.\n"
1495 + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
1496 + "-noquestionnaire\tTurn off questionnaire check.\n"
1497 + "-nonews\tTurn off check for Jalview news.\n"
1498 + "-nousagestats\tTurn off google analytics tracking for this session.\n"
1499 + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
1501 // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
1502 // after all other properties files have been read\n\t
1503 // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
1504 // passed in correctly)"
1505 + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
1506 + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
1507 + "-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"
1508 + "-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"
1509 + "-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"
1510 + "\n~Read documentation in Application or visit https://www.jalview.org for description of Features and Annotations file~\n\n");
1513 private static void startUsageStats(final Desktop desktop)
1516 * start a User Config prompt asking if we can log usage statistics.
1518 PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
1519 "USAGESTATS", "Jalview Usage Statistics",
1520 "Do you want to help make Jalview better by enabling "
1521 + "the collection of usage statistics with Google Analytics ?"
1522 + "\n\n(you can enable or disable usage tracking in the preferences)",
1529 "Initialising googletracker for usage stats.");
1530 Cache.initGoogleTracker();
1531 Console.debug("Tracking enabled.");
1538 Console.debug("Not enabling Google Tracking.");
1541 desktop.addDialogThread(prompter);
1545 * Locate the given string as a file and pass it to the groovy interpreter.
1547 * @param groovyscript
1548 * the script to execute
1549 * @param jalviewContext
1550 * the Jalview Desktop object passed in to the groovy binding as the
1553 protected void executeGroovyScript(String groovyscript, AlignFrame af)
1556 * for scripts contained in files
1563 if (groovyscript.trim().equals("STDIN"))
1565 // read from stdin into a tempfile and execute it
1568 tfile = File.createTempFile("jalview", "groovy");
1569 PrintWriter outfile = new PrintWriter(
1570 new OutputStreamWriter(new FileOutputStream(tfile)));
1571 BufferedReader br = new BufferedReader(
1572 new InputStreamReader(System.in));
1574 while ((line = br.readLine()) != null)
1576 outfile.write(line + "\n");
1582 } catch (Exception ex)
1584 System.err.println("Failed to read from STDIN into tempfile "
1585 + ((tfile == null) ? "(tempfile wasn't created)"
1586 : tfile.toString()));
1587 ex.printStackTrace();
1592 sfile = tfile.toURI().toURL();
1593 } catch (Exception x)
1596 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1598 x.printStackTrace();
1606 sfile = new URI(groovyscript).toURL();
1607 } catch (Exception x)
1609 tfile = new File(groovyscript);
1610 if (!tfile.exists())
1612 System.err.println("File '" + groovyscript + "' does not exist.");
1615 if (!tfile.canRead())
1617 System.err.println("File '" + groovyscript + "' cannot be read.");
1620 if (tfile.length() < 1)
1622 System.err.println("File '" + groovyscript + "' is empty.");
1627 sfile = tfile.getAbsoluteFile().toURI().toURL();
1628 } catch (Exception ex)
1630 System.err.println("Failed to create a file URL for "
1631 + tfile.getAbsoluteFile());
1638 Map<String, java.lang.Object> vbinding = new HashMap<>();
1639 vbinding.put("Jalview", this);
1642 vbinding.put("currentAlFrame", af);
1644 Binding gbinding = new Binding(vbinding);
1645 GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1646 gse.run(sfile.toString(), gbinding);
1647 if ("STDIN".equals(groovyscript))
1649 // delete temp file that we made -
1650 // only if it was successfully executed
1653 } catch (Exception e)
1655 System.err.println("Exception Whilst trying to execute file " + sfile
1656 + " as a groovy script.");
1657 e.printStackTrace(System.err);
1662 public static boolean isHeadlessMode()
1664 String isheadless = System.getProperty("java.awt.headless");
1665 if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1672 public AlignFrame[] getAlignFrames()
1674 return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1675 : Desktop.getAlignFrames();
1680 * jalview.bin.Jalview.quit() will just run the non-GUI shutdownHook and exit
1684 // System.exit will run the shutdownHook first
1685 Jalview.exit("Quitting now. Bye!", 0);
1688 public static AlignFrame getCurrentAlignFrame()
1690 return Jalview.currentAlignFrame;
1693 public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
1695 Jalview.currentAlignFrame = currentAlignFrame;
1698 protected Commands getCommands()
1703 public static void exit(String message, int exitcode)
1705 if (Console.log == null)
1707 // Don't start the logger just to exit!
1708 if (message != null)
1712 System.out.println(message);
1716 System.err.println(message);
1722 Console.debug("Using Jalview.exit");
1723 if (message != null)
1727 Console.info(message);
1731 Console.error(message);
1737 System.exit(exitcode);
1742 * testoutput for string values
1744 protected static void testoutput(ArgParser ap, Arg a, String s1,
1747 BootstrapArgs bsa = ap.getBootstrapArgs();
1748 if (!bsa.getBoolean(Arg.TESTOUTPUT))
1750 if (!((s1 == null && s2 == null) || (s1 != null && s1.equals(s2))))
1752 Console.debug("testoutput with unmatching values '" + s1 + "' and '"
1753 + s2 + "' for arg " + a.argString());
1756 boolean isset = a.hasOption(Opt.BOOTSTRAP) ? bsa.contains(a)
1760 Console.warn("Arg '" + a.getName() + "' not set at all");
1763 testoutput(true, a, s1, s2);
1766 protected static void testoutput(BootstrapArgs bsa, Arg a, String s1,
1769 if (!bsa.getBoolean(Arg.TESTOUTPUT))
1771 if (!((s1 == null && s2 == null) || (s1 != null && s1.equals(s2))))
1773 Console.debug("testoutput with unmatching values '" + s1 + "' and '"
1774 + s2 + "' for arg " + a.argString());
1777 if (!a.hasOption(Opt.BOOTSTRAP))
1779 Console.error("Non-bootstrap Arg '" + a.getName()
1780 + "' given to testoutput(BootstrapArgs bsa, Arg a, String s1, String s2) with only BootstrapArgs");
1782 if (!bsa.contains(a))
1784 Console.warn("Arg '" + a.getName() + "' not set at all");
1787 testoutput(true, a, s1, s2);
1790 private static void testoutput(boolean yes, Arg a, String s1, String s2)
1792 if (yes && ((s1 == null && s2 == null)
1793 || (s1 != null && s1.equals(s2))))
1795 System.out.println("[TESTOUTPUT] arg " + a.argString() + "='" + s1
1801 * testoutput for boolean values
1803 protected static void testoutput(ArgParser ap, Arg a)
1807 BootstrapArgs bsa = ap.getBootstrapArgs();
1810 if (!bsa.getBoolean(Arg.TESTOUTPUT))
1812 boolean val = a.hasOption(Opt.BOOTSTRAP) ? bsa.getBoolean(a)
1814 boolean isset = a.hasOption(Opt.BOOTSTRAP) ? bsa.contains(a)
1818 Console.warn("Arg '" + a.getName() + "' not set at all");
1824 protected static void testoutput(BootstrapArgs bsa, Arg a)
1826 if (!bsa.getBoolean(Arg.TESTOUTPUT))
1828 if (!a.hasOption(Opt.BOOTSTRAP))
1830 Console.warn("Non-bootstrap Arg '" + a.getName()
1831 + "' given to testoutput(BootstrapArgs bsa, Arg a) with only BootstrapArgs");
1834 if (!bsa.contains(a))
1836 Console.warn("Arg '" + a.getName() + "' not set at all");
1839 testoutput(bsa.getBoolean(a), a);
1842 private static void testoutput(boolean yes, Arg a)
1844 System.out.println("[TESTOUTPUT] arg "
1845 + (yes ? a.argString() : a.negateArgString()) + " was set");