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