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