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