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