JAL-629 open initial non-Arg arguments as files in separate alignment frames
[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         {
655           desktop.checkForNews();
656         }
657
658         if (!aparser.contains("nohtmltemplates")
659                 || Cache.getProperty("NOHTMLTEMPLATES") == null)
660         {
661           BioJsHTMLOutput.updateBioJS();
662         }
663       }
664     }
665     // Run Commands from cli
666     cmds = new Commands(argparser, headlessArg);
667     boolean commandsSuccess = cmds.argsWereParsed();
668     if (commandsSuccess)
669     {
670       if (headlessArg)
671       {
672         Jalview.exit("Successfully completed commands in headless mode", 0);
673       }
674       Console.info("Successfully completed commands");
675     }
676     else
677     {
678       if (headlessArg)
679       {
680         Jalview.exit("Error when running Commands in headless mode", 1);
681       }
682       Console.warn("Error when running commands");
683     }
684
685     // Check if JVM and compile version might cause problems and log if it
686     // might.
687     if (headless && !Platform.isJS() && !LaunchUtils.checkJavaVersion())
688     {
689       Console.warn("The Java version being used (Java "
690               + LaunchUtils.getJavaVersion()
691               + ") may lead to problems. This installation of Jalview should be used with Java "
692               + LaunchUtils.getJavaCompileVersion() + ".");
693     }
694
695     String file = null, data = null;
696
697     FileFormatI format = null;
698
699     DataSourceType protocol = null;
700
701     FileLoader fileLoader = new FileLoader(!headless);
702
703     String groovyscript = null; // script to execute after all loading is
704     // completed one way or another
705     // extract groovy argument and execute if necessary
706     groovyscript = aparser.getValue("groovy", true);
707     file = aparser.getValue("open", true);
708
709     if (file == null && desktop == null && !commandsSuccess)
710     {
711       Jalview.exit("No files to open!", 1);
712     }
713
714     long progress = -1;
715     // Finally, deal with the remaining input data.
716     if (file != null)
717     {
718       if (!headless)
719       {
720         desktop.setProgressBar(
721                 MessageManager
722                         .getString("status.processing_commandline_args"),
723                 progress = System.currentTimeMillis());
724       }
725       System.out.println("CMD [-open " + file + "] executed successfully!");
726
727       if (!Platform.isJS())
728       /**
729        * ignore in JavaScript -- can't just file existence - could load it?
730        * 
731        * @j2sIgnore
732        */
733       {
734         if (!HttpUtils.startsWithHttpOrHttps(file))
735         {
736           if (!(new File(file)).exists())
737           {
738             if (headless)
739             {
740               Jalview.exit(
741                       "Can't find file '" + file + "' in headless mode", 1);
742             }
743             Console.warn("Can't find file'" + file + "'");
744           }
745         }
746       }
747
748       protocol = AppletFormatAdapter.checkProtocol(file);
749
750       try
751       {
752         format = new IdentifyFile().identify(file, protocol);
753       } catch (FileFormatException e1)
754       {
755         // TODO ?
756       }
757
758       AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
759               format);
760       if (af == null)
761       {
762         System.out.println("error");
763       }
764       else
765       {
766         setCurrentAlignFrame(af);
767         data = aparser.getValue("colour", true);
768         if (data != null)
769         {
770           data.replaceAll("%20", " ");
771
772           ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
773                   af.getViewport(), af.getViewport().getAlignment(), data);
774
775           if (cs != null)
776           {
777             System.out.println(
778                     "CMD [-colour " + data + "] executed successfully!");
779           }
780           af.changeColour(cs);
781         }
782
783         // Must maintain ability to use the groups flag
784         data = aparser.getValue("groups", true);
785         if (data != null)
786         {
787           af.parseFeaturesFile(data,
788                   AppletFormatAdapter.checkProtocol(data));
789           // System.out.println("Added " + data);
790           System.out.println(
791                   "CMD groups[-" + data + "]  executed successfully!");
792         }
793         data = aparser.getValue("features", true);
794         if (data != null)
795         {
796           af.parseFeaturesFile(data,
797                   AppletFormatAdapter.checkProtocol(data));
798           // System.out.println("Added " + data);
799           System.out.println(
800                   "CMD [-features " + data + "]  executed successfully!");
801         }
802
803         data = aparser.getValue("annotations", true);
804         if (data != null)
805         {
806           af.loadJalviewDataFile(data, null, null, null);
807           // System.out.println("Added " + data);
808           System.out.println(
809                   "CMD [-annotations " + data + "] executed successfully!");
810         }
811         // set or clear the sortbytree flag.
812         if (aparser.contains("sortbytree"))
813         {
814           af.getViewport().setSortByTree(true);
815           if (af.getViewport().getSortByTree())
816           {
817             System.out.println("CMD [-sortbytree] executed successfully!");
818           }
819         }
820         if (aparser.contains("no-annotation"))
821         {
822           af.getViewport().setShowAnnotation(false);
823           if (!af.getViewport().isShowAnnotation())
824           {
825             System.out.println("CMD no-annotation executed successfully!");
826           }
827         }
828         if (aparser.contains("nosortbytree"))
829         {
830           af.getViewport().setSortByTree(false);
831           if (!af.getViewport().getSortByTree())
832           {
833             System.out
834                     .println("CMD [-nosortbytree] executed successfully!");
835           }
836         }
837         data = aparser.getValue("tree", true);
838         if (data != null)
839         {
840           try
841           {
842             System.out.println(
843                     "CMD [-tree " + data + "] executed successfully!");
844             NewickFile nf = new NewickFile(data,
845                     AppletFormatAdapter.checkProtocol(data));
846             af.getViewport()
847                     .setCurrentTree(af.showNewickTree(nf, data).getTree());
848           } catch (IOException ex)
849           {
850             System.err.println("Couldn't add tree " + data);
851             ex.printStackTrace(System.err);
852           }
853         }
854         // TODO - load PDB structure(s) to alignment JAL-629
855         // (associate with identical sequence in alignment, or a specified
856         // sequence)
857         if (groovyscript != null)
858         {
859           // Execute the groovy script after we've done all the rendering stuff
860           // and before any images or figures are generated.
861           System.out.println("Executing script " + groovyscript);
862           executeGroovyScript(groovyscript, af);
863           System.out.println("CMD groovy[" + groovyscript
864                   + "] executed successfully!");
865           groovyscript = null;
866         }
867         String imageName = "unnamed.png";
868         while (aparser.getSize() > 1)
869         {
870           String outputFormat = aparser.nextValue();
871           file = aparser.nextValue();
872
873           if (outputFormat.equalsIgnoreCase("png"))
874           {
875             af.createPNG(new File(file));
876             imageName = (new File(file)).getName();
877             System.out.println("Creating PNG image: " + file);
878             continue;
879           }
880           else if (outputFormat.equalsIgnoreCase("svg"))
881           {
882             File imageFile = new File(file);
883             imageName = imageFile.getName();
884             af.createSVG(imageFile);
885             System.out.println("Creating SVG image: " + file);
886             continue;
887           }
888           else if (outputFormat.equalsIgnoreCase("html"))
889           {
890             File imageFile = new File(file);
891             imageName = imageFile.getName();
892             HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
893             htmlSVG.exportHTML(file);
894
895             System.out.println("Creating HTML image: " + file);
896             continue;
897           }
898           else if (outputFormat.equalsIgnoreCase("biojsmsa"))
899           {
900             if (file == null)
901             {
902               System.err.println("The output html file must not be null");
903               return;
904             }
905             try
906             {
907               BioJsHTMLOutput.refreshVersionInfo(
908                       BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
909             } catch (URISyntaxException e)
910             {
911               e.printStackTrace();
912             }
913             BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
914             bjs.exportHTML(file);
915             System.out
916                     .println("Creating BioJS MSA Viwer HTML file: " + file);
917             continue;
918           }
919           else if (outputFormat.equalsIgnoreCase("imgMap"))
920           {
921             af.createImageMap(new File(file), imageName);
922             System.out.println("Creating image map: " + file);
923             continue;
924           }
925           else if (outputFormat.equalsIgnoreCase("eps"))
926           {
927             File outputFile = new File(file);
928             System.out.println(
929                     "Creating EPS file: " + outputFile.getAbsolutePath());
930             af.createEPS(outputFile);
931             continue;
932           }
933           FileFormatI outFormat = null;
934           try
935           {
936             outFormat = FileFormats.getInstance().forName(outputFormat);
937           } catch (Exception formatP)
938           {
939             System.out.println("Couldn't parse " + outFormat
940                     + " as a valid Jalview format string.");
941           }
942           if (outFormat != null)
943           {
944             if (!outFormat.isWritable())
945             {
946               System.out.println(
947                       "This version of Jalview does not support alignment export as "
948                               + outputFormat);
949             }
950             else
951             {
952               af.saveAlignment(file, outFormat);
953               if (af.isSaveAlignmentSuccessful())
954               {
955                 System.out.println("Written alignment in "
956                         + outFormat.getName() + " format to " + file);
957               }
958               else
959               {
960                 System.out.println("Error writing file " + file + " in "
961                         + outFormat.getName() + " format!!");
962               }
963             }
964           }
965
966         }
967
968         while (aparser.getSize() > 0)
969         {
970           System.out.println("Unknown arg: " + aparser.nextValue());
971         }
972       }
973     }
974
975     AlignFrame startUpAlframe = null;
976     // We'll only open the default file if the desktop is visible.
977     // And the user
978     // ////////////////////
979
980     if (!Platform.isJS() && !headless && file == null
981             && Cache.getDefault("SHOW_STARTUP_FILE", true)
982             && !cmds.commandArgsProvided())
983     // don't open the startup file if command line args have been processed
984     // (&& !Commands.commandArgsProvided())
985     /**
986      * Java only
987      * 
988      * @j2sIgnore
989      */
990     {
991       file = Cache.getDefault("STARTUP_FILE",
992               Cache.getDefault("www.jalview.org", "https://www.jalview.org")
993                       + "/examples/exampleFile_2_7.jvp");
994       if (file.equals("http://www.jalview.org/examples/exampleFile_2_3.jar")
995               || file.equals(
996                       "http://www.jalview.org/examples/exampleFile_2_7.jar"))
997       {
998         file.replace("http:", "https:");
999         // hardwire upgrade of the startup file
1000         file.replace("_2_3", "_2_7");
1001         file.replace("2_7.jar", "2_7.jvp");
1002         // and remove the stale setting
1003         Cache.removeProperty("STARTUP_FILE");
1004       }
1005
1006       protocol = AppletFormatAdapter.checkProtocol(file);
1007
1008       if (file.endsWith(".jar"))
1009       {
1010         format = FileFormat.Jalview;
1011       }
1012       else
1013       {
1014         try
1015         {
1016           format = new IdentifyFile().identify(file, protocol);
1017         } catch (FileFormatException e)
1018         {
1019           // TODO what?
1020         }
1021       }
1022
1023       startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
1024               format);
1025       // don't ask to save when quitting if only the startup file has been
1026       // opened
1027       Console.debug("Resetting up-to-date flag for startup file");
1028       startUpAlframe.getViewport().setSavedUpToDate(true);
1029       // extract groovy arguments before anything else.
1030     }
1031
1032     // Once all other stuff is done, execute any groovy scripts (in order)
1033     if (groovyscript != null)
1034     {
1035       if (Cache.groovyJarsPresent())
1036       {
1037         System.out.println("Executing script " + groovyscript);
1038         executeGroovyScript(groovyscript, startUpAlframe);
1039       }
1040       else
1041       {
1042         System.err.println(
1043                 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
1044                         + groovyscript);
1045       }
1046     }
1047     // and finally, turn off batch mode indicator - if the desktop still exists
1048     if (desktop != null)
1049     {
1050       if (progress != -1)
1051       {
1052         desktop.setProgressBar(null, progress);
1053       }
1054       desktop.setInBatchMode(false);
1055     }
1056   }
1057
1058   private static void setLookAndFeel()
1059   {
1060     // property laf = "crossplatform", "system", "gtk", "metal", "nimbus",
1061     // "mac" or "flat"
1062     // If not set (or chosen laf fails), use the normal SystemLaF and if on Mac,
1063     // try Quaqua/Vaqua.
1064     String lafProp = System.getProperty("laf");
1065     String lafSetting = Cache.getDefault("PREFERRED_LAF", null);
1066     String laf = "none";
1067     if (lafProp != null)
1068     {
1069       laf = lafProp;
1070     }
1071     else if (lafSetting != null)
1072     {
1073       laf = lafSetting;
1074     }
1075     boolean lafSet = false;
1076     switch (laf)
1077     {
1078     case "crossplatform":
1079       lafSet = setCrossPlatformLookAndFeel();
1080       if (!lafSet)
1081       {
1082         Console.error("Could not set requested laf=" + laf);
1083       }
1084       break;
1085     case "system":
1086       lafSet = setSystemLookAndFeel();
1087       if (!lafSet)
1088       {
1089         Console.error("Could not set requested laf=" + laf);
1090       }
1091       break;
1092     case "gtk":
1093       lafSet = setGtkLookAndFeel();
1094       if (!lafSet)
1095       {
1096         Console.error("Could not set requested laf=" + laf);
1097       }
1098       break;
1099     case "metal":
1100       lafSet = setMetalLookAndFeel();
1101       if (!lafSet)
1102       {
1103         Console.error("Could not set requested laf=" + laf);
1104       }
1105       break;
1106     case "nimbus":
1107       lafSet = setNimbusLookAndFeel();
1108       if (!lafSet)
1109       {
1110         Console.error("Could not set requested laf=" + laf);
1111       }
1112       break;
1113     case "flat":
1114       lafSet = setFlatLookAndFeel();
1115       if (!lafSet)
1116       {
1117         Console.error("Could not set requested laf=" + laf);
1118       }
1119       break;
1120     case "mac":
1121       lafSet = setMacLookAndFeel();
1122       if (!lafSet)
1123       {
1124         Console.error("Could not set requested laf=" + laf);
1125       }
1126       break;
1127     case "none":
1128       break;
1129     default:
1130       Console.error("Requested laf=" + laf + " not implemented");
1131     }
1132     if (!lafSet)
1133     {
1134       setSystemLookAndFeel();
1135       if (Platform.isLinux())
1136       {
1137         setLinuxLookAndFeel();
1138       }
1139       if (Platform.isMac())
1140       {
1141         setMacLookAndFeel();
1142       }
1143     }
1144   }
1145
1146   private static boolean setCrossPlatformLookAndFeel()
1147   {
1148     boolean set = false;
1149     try
1150     {
1151       UIManager.setLookAndFeel(
1152               UIManager.getCrossPlatformLookAndFeelClassName());
1153       set = true;
1154     } catch (Exception ex)
1155     {
1156       Console.error("Unexpected Look and Feel Exception");
1157       Console.error(ex.getMessage());
1158       Console.debug(Cache.getStackTraceString(ex));
1159     }
1160     return set;
1161   }
1162
1163   private static boolean setSystemLookAndFeel()
1164   {
1165     boolean set = false;
1166     try
1167     {
1168       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
1169       set = true;
1170     } catch (Exception ex)
1171     {
1172       Console.error("Unexpected Look and Feel Exception");
1173       Console.error(ex.getMessage());
1174       Console.debug(Cache.getStackTraceString(ex));
1175     }
1176     return set;
1177   }
1178
1179   private static boolean setSpecificLookAndFeel(String name,
1180           String className, boolean nameStartsWith)
1181   {
1182     boolean set = false;
1183     try
1184     {
1185       for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
1186       {
1187         if (info.getName() != null && nameStartsWith
1188                 ? info.getName().toLowerCase(Locale.ROOT)
1189                         .startsWith(name.toLowerCase(Locale.ROOT))
1190                 : info.getName().toLowerCase(Locale.ROOT)
1191                         .equals(name.toLowerCase(Locale.ROOT)))
1192         {
1193           className = info.getClassName();
1194           break;
1195         }
1196       }
1197       UIManager.setLookAndFeel(className);
1198       set = true;
1199     } catch (Exception ex)
1200     {
1201       Console.error("Unexpected Look and Feel Exception");
1202       Console.error(ex.getMessage());
1203       Console.debug(Cache.getStackTraceString(ex));
1204     }
1205     return set;
1206   }
1207
1208   private static boolean setGtkLookAndFeel()
1209   {
1210     return setSpecificLookAndFeel("gtk",
1211             "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true);
1212   }
1213
1214   private static boolean setMetalLookAndFeel()
1215   {
1216     return setSpecificLookAndFeel("metal",
1217             "javax.swing.plaf.metal.MetalLookAndFeel", false);
1218   }
1219
1220   private static boolean setNimbusLookAndFeel()
1221   {
1222     return setSpecificLookAndFeel("nimbus",
1223             "javax.swing.plaf.nimbus.NimbusLookAndFeel", false);
1224   }
1225
1226   private static boolean setFlatLookAndFeel()
1227   {
1228     boolean set = false;
1229     if (SystemInfo.isMacOS)
1230     {
1231       try
1232       {
1233         UIManager.setLookAndFeel(
1234                 "com.formdev.flatlaf.themes.FlatMacLightLaf");
1235         set = true;
1236         Console.debug("Using FlatMacLightLaf");
1237       } catch (ClassNotFoundException | InstantiationException
1238               | IllegalAccessException | UnsupportedLookAndFeelException e)
1239       {
1240         Console.debug("Exception loading FlatLightLaf", e);
1241       }
1242       System.setProperty("apple.laf.useScreenMenuBar", "true");
1243       System.setProperty("apple.awt.application.name",
1244               ChannelProperties.getProperty("app_name"));
1245       System.setProperty("apple.awt.application.appearance", "system");
1246       if (SystemInfo.isMacFullWindowContentSupported
1247               && Desktop.desktop != null)
1248       {
1249         Console.debug("Setting transparent title bar");
1250         Desktop.desktop.getRootPane()
1251                 .putClientProperty("apple.awt.fullWindowContent", true);
1252         Desktop.desktop.getRootPane()
1253                 .putClientProperty("apple.awt.transparentTitleBar", true);
1254         Desktop.desktop.getRootPane()
1255                 .putClientProperty("apple.awt.fullscreenable", true);
1256       }
1257       SwingUtilities.invokeLater(() -> {
1258         FlatMacLightLaf.setup();
1259       });
1260       Console.debug("Using FlatMacLightLaf");
1261       set = true;
1262     }
1263     if (!set)
1264     {
1265       try
1266       {
1267         UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1268         set = true;
1269         Console.debug("Using FlatLightLaf");
1270       } catch (ClassNotFoundException | InstantiationException
1271               | IllegalAccessException | UnsupportedLookAndFeelException e)
1272       {
1273         Console.debug("Exception loading FlatLightLaf", e);
1274       }
1275       // Windows specific properties here
1276       SwingUtilities.invokeLater(() -> {
1277         FlatLightLaf.setup();
1278       });
1279       Console.debug("Using FlatLightLaf");
1280       set = true;
1281     }
1282     else if (SystemInfo.isLinux)
1283     {
1284       try
1285       {
1286         UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1287         set = true;
1288         Console.debug("Using FlatLightLaf");
1289       } catch (ClassNotFoundException | InstantiationException
1290               | IllegalAccessException | UnsupportedLookAndFeelException e)
1291       {
1292         Console.debug("Exception loading FlatLightLaf", e);
1293       }
1294       // enable custom window decorations
1295       JFrame.setDefaultLookAndFeelDecorated(true);
1296       JDialog.setDefaultLookAndFeelDecorated(true);
1297       SwingUtilities.invokeLater(() -> {
1298         FlatLightLaf.setup();
1299       });
1300       Console.debug("Using FlatLightLaf");
1301       set = true;
1302     }
1303
1304     if (!set)
1305     {
1306       try
1307       {
1308         UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1309         set = true;
1310         Console.debug("Using FlatLightLaf");
1311       } catch (ClassNotFoundException | InstantiationException
1312               | IllegalAccessException | UnsupportedLookAndFeelException e)
1313       {
1314         Console.debug("Exception loading FlatLightLaf", e);
1315       }
1316     }
1317
1318     if (set)
1319     {
1320       UIManager.put("TabbedPane.tabType", "card");
1321       UIManager.put("TabbedPane.showTabSeparators", true);
1322       UIManager.put("TabbedPane.showContentSeparator", true);
1323       UIManager.put("TabbedPane.tabSeparatorsFullHeight", true);
1324       UIManager.put("TabbedPane.tabsOverlapBorder", true);
1325       UIManager.put("TabbedPane.hasFullBorder", true);
1326       UIManager.put("TabbedPane.tabLayoutPolicy", "scroll");
1327       UIManager.put("TabbedPane.scrollButtonsPolicy", "asNeeded");
1328       UIManager.put("TabbedPane.smoothScrolling", true);
1329       UIManager.put("TabbedPane.tabWidthMode", "compact");
1330       UIManager.put("TabbedPane.selectedBackground", Color.white);
1331     }
1332
1333     Desktop.setLiveDragMode(Cache.getDefault("FLAT_LIVE_DRAG_MODE", true));
1334     return set;
1335   }
1336
1337   private static boolean setMacLookAndFeel()
1338   {
1339     boolean set = false;
1340     System.setProperty("com.apple.mrj.application.apple.menu.about.name",
1341             ChannelProperties.getProperty("app_name"));
1342     System.setProperty("apple.laf.useScreenMenuBar", "true");
1343     /*
1344      * broken native LAFs on (ARM?) macbooks
1345     set = setQuaquaLookAndFeel();
1346     if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
1347             .toLowerCase(Locale.ROOT).contains("quaqua"))
1348     {
1349       set = setVaquaLookAndFeel();
1350     }
1351      */
1352     set = setFlatLookAndFeel();
1353     return set;
1354   }
1355
1356   private static boolean setLinuxLookAndFeel()
1357   {
1358     boolean set = false;
1359     set = setFlatLookAndFeel();
1360     if (!set)
1361       set = setMetalLookAndFeel();
1362     // avoid GtkLookAndFeel -- not good results especially on HiDPI
1363     if (!set)
1364       set = setNimbusLookAndFeel();
1365     return set;
1366   }
1367
1368   private static void showUsage()
1369   {
1370     System.out.println(
1371             "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
1372                     + "-nodisplay\tRun Jalview without User Interface.\n"
1373                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
1374                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
1375                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
1376                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
1377                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
1378                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
1379                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
1380                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
1381                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
1382                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
1383                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
1384                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
1385                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
1386                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
1387                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
1388                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
1389                     + "-html FILE\tCreate HTML file from alignment.\n"
1390                     + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
1391                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
1392                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
1393                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
1394                     + "-noquestionnaire\tTurn off questionnaire check.\n"
1395                     + "-nonews\tTurn off check for Jalview news.\n"
1396                     + "-nousagestats\tTurn off google analytics tracking for this session.\n"
1397                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
1398                     // +
1399                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
1400                     // after all other properties files have been read\n\t
1401                     // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
1402                     // passed in correctly)"
1403                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
1404                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
1405                     + "-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"
1406                     + "-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"
1407                     + "-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"
1408                     + "\n~Read documentation in Application or visit https://www.jalview.org for description of Features and Annotations file~\n\n");
1409   }
1410
1411   private static void startUsageStats(final Desktop desktop)
1412   {
1413     /**
1414      * start a User Config prompt asking if we can log usage statistics.
1415      */
1416     PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
1417             "USAGESTATS", "Jalview Usage Statistics",
1418             "Do you want to help make Jalview better by enabling "
1419                     + "the collection of usage statistics with Google Analytics ?"
1420                     + "\n\n(you can enable or disable usage tracking in the preferences)",
1421             new Runnable()
1422             {
1423               @Override
1424               public void run()
1425               {
1426                 Console.debug(
1427                         "Initialising googletracker for usage stats.");
1428                 Cache.initGoogleTracker();
1429                 Console.debug("Tracking enabled.");
1430               }
1431             }, new Runnable()
1432             {
1433               @Override
1434               public void run()
1435               {
1436                 Console.debug("Not enabling Google Tracking.");
1437               }
1438             }, null, true);
1439     desktop.addDialogThread(prompter);
1440   }
1441
1442   /**
1443    * Locate the given string as a file and pass it to the groovy interpreter.
1444    * 
1445    * @param groovyscript
1446    *          the script to execute
1447    * @param jalviewContext
1448    *          the Jalview Desktop object passed in to the groovy binding as the
1449    *          'Jalview' object.
1450    */
1451   private void executeGroovyScript(String groovyscript, AlignFrame af)
1452   {
1453     /**
1454      * for scripts contained in files
1455      */
1456     File tfile = null;
1457     /**
1458      * script's URI
1459      */
1460     URL sfile = null;
1461     if (groovyscript.trim().equals("STDIN"))
1462     {
1463       // read from stdin into a tempfile and execute it
1464       try
1465       {
1466         tfile = File.createTempFile("jalview", "groovy");
1467         PrintWriter outfile = new PrintWriter(
1468                 new OutputStreamWriter(new FileOutputStream(tfile)));
1469         BufferedReader br = new BufferedReader(
1470                 new InputStreamReader(System.in));
1471         String line = null;
1472         while ((line = br.readLine()) != null)
1473         {
1474           outfile.write(line + "\n");
1475         }
1476         br.close();
1477         outfile.flush();
1478         outfile.close();
1479
1480       } catch (Exception ex)
1481       {
1482         System.err.println("Failed to read from STDIN into tempfile "
1483                 + ((tfile == null) ? "(tempfile wasn't created)"
1484                         : tfile.toString()));
1485         ex.printStackTrace();
1486         return;
1487       }
1488       try
1489       {
1490         sfile = tfile.toURI().toURL();
1491       } catch (Exception x)
1492       {
1493         System.err.println(
1494                 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1495                         + tfile.toURI());
1496         x.printStackTrace();
1497         return;
1498       }
1499     }
1500     else
1501     {
1502       try
1503       {
1504         sfile = new URI(groovyscript).toURL();
1505       } catch (Exception x)
1506       {
1507         tfile = new File(groovyscript);
1508         if (!tfile.exists())
1509         {
1510           System.err.println("File '" + groovyscript + "' does not exist.");
1511           return;
1512         }
1513         if (!tfile.canRead())
1514         {
1515           System.err.println("File '" + groovyscript + "' cannot be read.");
1516           return;
1517         }
1518         if (tfile.length() < 1)
1519         {
1520           System.err.println("File '" + groovyscript + "' is empty.");
1521           return;
1522         }
1523         try
1524         {
1525           sfile = tfile.getAbsoluteFile().toURI().toURL();
1526         } catch (Exception ex)
1527         {
1528           System.err.println("Failed to create a file URL for "
1529                   + tfile.getAbsoluteFile());
1530           return;
1531         }
1532       }
1533     }
1534     try
1535     {
1536       Map<String, java.lang.Object> vbinding = new HashMap<>();
1537       vbinding.put("Jalview", this);
1538       if (af != null)
1539       {
1540         vbinding.put("currentAlFrame", af);
1541       }
1542       Binding gbinding = new Binding(vbinding);
1543       GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1544       gse.run(sfile.toString(), gbinding);
1545       if ("STDIN".equals(groovyscript))
1546       {
1547         // delete temp file that we made -
1548         // only if it was successfully executed
1549         tfile.delete();
1550       }
1551     } catch (Exception e)
1552     {
1553       System.err.println("Exception Whilst trying to execute file " + sfile
1554               + " as a groovy script.");
1555       e.printStackTrace(System.err);
1556
1557     }
1558   }
1559
1560   public static boolean isHeadlessMode()
1561   {
1562     String isheadless = System.getProperty("java.awt.headless");
1563     if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1564     {
1565       return true;
1566     }
1567     return false;
1568   }
1569
1570   public AlignFrame[] getAlignFrames()
1571   {
1572     return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1573             : Desktop.getAlignFrames();
1574
1575   }
1576
1577   /**
1578    * jalview.bin.Jalview.quit() will just run the non-GUI shutdownHook and exit
1579    */
1580   public void quit()
1581   {
1582     // System.exit will run the shutdownHook first
1583     Jalview.exit("Quitting now. Bye!", 0);
1584   }
1585
1586   public static AlignFrame getCurrentAlignFrame()
1587   {
1588     return Jalview.currentAlignFrame;
1589   }
1590
1591   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
1592   {
1593     Jalview.currentAlignFrame = currentAlignFrame;
1594   }
1595
1596   protected Commands getCommands()
1597   {
1598     return cmds;
1599   }
1600
1601   public static void exit(String message, int exitcode)
1602   {
1603     Console.debug("Using Jalview.exit");
1604     if (message != null)
1605       if (exitcode == 0)
1606         Console.info(message);
1607       else
1608         Console.error(message);
1609     if (exitcode > -1)
1610       System.exit(exitcode);
1611   }
1612 }