c8e453d29e8797140989d06fff668498332470cf
[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.io.BufferedReader;
24 import java.io.File;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.InputStreamReader;
28 import java.io.OutputStreamWriter;
29 import java.io.PrintWriter;
30 import java.net.MalformedURLException;
31 import java.net.URI;
32 import java.net.URISyntaxException;
33 import java.net.URL;
34 import java.security.AllPermission;
35 import java.security.CodeSource;
36 import java.security.PermissionCollection;
37 import java.security.Permissions;
38 import java.security.Policy;
39 import java.util.HashMap;
40 import java.util.Map;
41 import java.util.Vector;
42 import java.util.logging.ConsoleHandler;
43 import java.util.logging.Level;
44 import java.util.logging.Logger;
45
46 import javax.swing.LookAndFeel;
47 import javax.swing.UIManager;
48
49 import com.threerings.getdown.util.LaunchUtil;
50
51 import groovy.lang.Binding;
52 import groovy.util.GroovyScriptEngine;
53 import jalview.ext.so.SequenceOntology;
54 import jalview.gui.AlignFrame;
55 import jalview.gui.Desktop;
56 import jalview.gui.PromptUserConfig;
57 import jalview.io.AppletFormatAdapter;
58 import jalview.io.BioJsHTMLOutput;
59 import jalview.io.DataSourceType;
60 import jalview.io.FileFormat;
61 import jalview.io.FileFormatException;
62 import jalview.io.FileFormatI;
63 import jalview.io.FileLoader;
64 import jalview.io.HtmlSvgOutput;
65 import jalview.io.IdentifyFile;
66 import jalview.io.NewickFile;
67 import jalview.io.gff.SequenceOntologyFactory;
68 import jalview.schemes.ColourSchemeI;
69 import jalview.schemes.ColourSchemeProperty;
70 import jalview.util.MessageManager;
71 import jalview.util.Platform;
72 import jalview.ws.jws2.Jws2Discoverer;
73
74 /**
75  * Main class for Jalview Application <br>
76  * <br>
77  * start with: java -classpath "$PATH_TO_LIB$/*:$PATH_TO_CLASSES$" \
78  * jalview.bin.Jalview
79  * 
80  * or on Windows: java -classpath "$PATH_TO_LIB$/*;$PATH_TO_CLASSES$" \
81  * jalview.bin.Jalview jalview.bin.Jalview
82  * 
83  * (ensure -classpath arg is quoted to avoid shell expansion of '*' and do not
84  * embellish '*' to e.g. '*.jar')
85  * 
86  * @author $author$
87  * @version $Revision$
88  */
89 public class Jalview
90 {
91   static
92   {
93     Platform.getURLCommandArguments();
94   }
95
96   // singleton instance of this class
97
98   private static Jalview instance;
99
100   private Desktop desktop;
101
102   public static AlignFrame currentAlignFrame;
103
104   static
105   {
106     if (!Platform.isJS())
107     /**
108      * Java only
109      * 
110      * @j2sIgnore
111      */
112     {
113       // grab all the rights we can for the JVM
114       Policy.setPolicy(new Policy()
115       {
116         @Override
117         public PermissionCollection getPermissions(CodeSource codesource)
118         {
119           Permissions perms = new Permissions();
120           perms.add(new AllPermission());
121           return (perms);
122         }
123
124         @Override
125         public void refresh()
126         {
127         }
128       });
129     }
130   }
131
132   /**
133    * keep track of feature fetching tasks.
134    * 
135    * @author JimP
136    * 
137    */
138   class FeatureFetcher
139   {
140     /*
141      * TODO: generalise to track all jalview events to orchestrate batch processing
142      * events.
143      */
144
145     private int queued = 0;
146
147     private int running = 0;
148
149     public FeatureFetcher()
150     {
151
152     }
153
154     public void addFetcher(final AlignFrame af,
155             final Vector<String> dasSources)
156     {
157       final long id = System.currentTimeMillis();
158       queued++;
159       final FeatureFetcher us = this;
160       new Thread(new Runnable()
161       {
162
163         @Override
164         public void run()
165         {
166           synchronized (us)
167           {
168             queued--;
169             running++;
170           }
171
172           af.setProgressBar(MessageManager
173                   .getString("status.das_features_being_retrived"), id);
174           af.featureSettings_actionPerformed(null);
175           af.setProgressBar(null, id);
176           synchronized (us)
177           {
178             running--;
179           }
180         }
181       }).start();
182     }
183
184     public synchronized boolean allFinished()
185     {
186       return queued == 0 && running == 0;
187     }
188
189   }
190
191   public static Jalview getInstance()
192   {
193     return instance;
194   }
195
196   /**
197    * main class for Jalview application
198    * 
199    * @param args
200    *          open <em>filename</em>
201    */
202   public static void main(String[] args)
203   {
204     // setLogging(); // BH - for event debugging in JavaScript
205     instance = new Jalview();
206     instance.doMain(args);
207   }
208
209   private static void logClass(String name)
210   {
211     // BH - for event debugging in JavaScript
212     ConsoleHandler consoleHandler = new ConsoleHandler();
213     consoleHandler.setLevel(Level.ALL);
214     Logger logger = Logger.getLogger(name);
215     logger.setLevel(Level.ALL);
216     logger.addHandler(consoleHandler);
217   }
218
219   @SuppressWarnings("unused")
220   private static void setLogging()
221   {
222
223     /**
224      * @j2sIgnore
225      * 
226      */
227     {
228       System.out.println("not in js");
229     }
230
231     // BH - for event debugging in JavaScript (Java mode only)
232     if (!Platform.isJS())
233     /**
234      * Java only
235      * 
236      * @j2sIgnore
237      */
238     {
239       Logger.getLogger("").setLevel(Level.ALL);
240       logClass("java.awt.EventDispatchThread");
241       logClass("java.awt.EventQueue");
242       logClass("java.awt.Component");
243       logClass("java.awt.focus.Component");
244       logClass("java.awt.focus.DefaultKeyboardFocusManager");
245     }
246
247   }
248
249   /**
250    * @param args
251    */
252   void doMain(String[] args)
253   {
254
255     if (!Platform.isJS())
256     {
257       System.setSecurityManager(null);
258     }
259
260     System.out
261             .println("Java version: " + System.getProperty("java.version"));
262     System.out.println("Java Home: " + System.getProperty("java.home"));
263     System.out.println(System.getProperty("os.arch") + " "
264             + System.getProperty("os.name") + " "
265             + System.getProperty("os.version"));
266     String val = System.getProperty("sys.install4jVersion");
267     if (val != null)
268     {
269       System.out.println("Install4j version: " + val);
270     }
271     val = System.getProperty("installer_template_version");
272     if (val != null)
273     {
274       System.out.println("Install4j template version: " + val);
275     }
276     val = System.getProperty("launcher_version");
277     if (val != null)
278     {
279       System.out.println("Launcher version: " + val);
280     }
281
282     // report Jalview version
283     Cache.loadBuildProperties(true);
284
285     ArgsParser aparser = new ArgsParser(args);
286     boolean headless = false;
287
288     String usrPropsFile = aparser.getValue("props");
289     Cache.loadProperties(usrPropsFile); // must do this before
290     if (usrPropsFile != null)
291     {
292       System.out.println(
293               "CMD [-props " + usrPropsFile + "] executed successfully!");
294     }
295
296     if (!Platform.isJS())
297     /**
298      * Java only
299      * 
300      * @j2sIgnore
301      */
302     {
303       if (aparser.contains("help") || aparser.contains("h"))
304       {
305         showUsage();
306         System.exit(0);
307       }
308       if (aparser.contains("nodisplay") || aparser.contains("nogui")
309               || aparser.contains("headless"))
310       {
311         System.setProperty("java.awt.headless", "true");
312         headless = true;
313       }
314       // anything else!
315
316       final String jabawsUrl = aparser.getValue("jabaws");
317       if (jabawsUrl != null)
318       {
319         try
320         {
321           Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl);
322           System.out.println(
323                   "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
324         } catch (MalformedURLException e)
325         {
326           System.err.println(
327                   "Invalid jabaws parameter: " + jabawsUrl + " ignored");
328         }
329       }
330
331     }
332     String defs = aparser.getValue("setprop");
333     while (defs != null)
334     {
335       int p = defs.indexOf('=');
336       if (p == -1)
337       {
338         System.err.println("Ignoring invalid setprop argument : " + defs);
339       }
340       else
341       {
342         System.out.println("Executing setprop argument: " + defs);
343         if (Platform.isJS())
344         {
345           Cache.setProperty(defs.substring(0, p), defs.substring(p + 1));
346         }
347       }
348       defs = aparser.getValue("setprop");
349     }
350     if (System.getProperty("java.awt.headless") != null
351             && System.getProperty("java.awt.headless").equals("true"))
352     {
353       headless = true;
354     }
355     System.setProperty("http.agent",
356             "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown"));
357     try
358     {
359       Cache.initLogger();
360     } catch (NoClassDefFoundError error)
361     {
362       error.printStackTrace();
363       System.out.println("\nEssential logging libraries not found."
364               + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview");
365       System.exit(0);
366     }
367
368     desktop = null;
369
370     try
371     {
372       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
373     } catch (Exception ex)
374     {
375       System.err.println("Unexpected Look and Feel Exception");
376       ex.printStackTrace();
377     }
378     if (Platform.isAMacAndNotJS())
379     {
380
381       LookAndFeel lookAndFeel = ch.randelshofer.quaqua.QuaquaManager
382               .getLookAndFeel();
383       System.setProperty("com.apple.mrj.application.apple.menu.about.name",
384               "Jalview");
385       System.setProperty("apple.laf.useScreenMenuBar", "true");
386       if (lookAndFeel != null)
387       {
388         try
389         {
390           UIManager.setLookAndFeel(lookAndFeel);
391         } catch (Throwable e)
392         {
393           System.err.println(
394                   "Failed to set QuaQua look and feel: " + e.toString());
395         }
396       }
397       if (lookAndFeel == null
398               || !(lookAndFeel.getClass().isAssignableFrom(
399                       UIManager.getLookAndFeel().getClass()))
400               || !UIManager.getLookAndFeel().getClass().toString()
401                       .toLowerCase().contains("quaqua"))
402       {
403         try
404         {
405           System.err.println(
406                   "Quaqua LaF not available on this plaform. Using VAqua(4).\nSee https://issues.jalview.org/browse/JAL-2976");
407           UIManager.setLookAndFeel("org.violetlib.aqua.AquaLookAndFeel");
408         } catch (Throwable e)
409         {
410           System.err.println(
411                   "Failed to reset look and feel: " + e.toString());
412         }
413       }
414     }
415
416     /*
417      * configure 'full' SO model if preferences say to, else use the default (full SO)
418      * - as JS currently doesn't have OBO parsing, it must use 'Lite' version
419      */
420     boolean soDefault = !Platform.isJS();
421     if (Cache.getDefault("USE_FULL_SO", soDefault))
422     {
423       SequenceOntologyFactory.setInstance(new SequenceOntology());
424     }
425
426     if (!headless)
427     {
428       desktop = new Desktop();
429       desktop.setInBatchMode(true); // indicate we are starting up
430
431       try
432       {
433         JalviewTaskbar.setTaskbar(this);
434       } catch (Throwable t)
435       {
436         System.out.println("Error setting Taskbar: " + t.getMessage());
437       }
438
439       desktop.setVisible(true);
440
441       if (!Platform.isJS())
442       /**
443        * Java only
444        * 
445        * @j2sIgnore
446        */
447       {
448         desktop.startServiceDiscovery();
449         if (!aparser.contains("nousagestats"))
450         {
451           startUsageStats(desktop);
452         }
453         else
454         {
455           System.err.println("CMD [-nousagestats] executed successfully!");
456         }
457
458         if (!aparser.contains("noquestionnaire"))
459         {
460           String url = aparser.getValue("questionnaire");
461           if (url != null)
462           {
463             // Start the desktop questionnaire prompter with the specified
464             // questionnaire
465             Cache.log.debug("Starting questionnaire url at " + url);
466             desktop.checkForQuestionnaire(url);
467             System.out.println("CMD questionnaire[-" + url
468                     + "] executed successfully!");
469           }
470           else
471           {
472             if (Cache.getProperty("NOQUESTIONNAIRES") == null)
473             {
474               // Start the desktop questionnaire prompter with the specified
475               // questionnaire
476               // String defurl =
477               // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
478               // //
479               String defurl = "http://www.jalview.org/cgi-bin/questionnaire.pl";
480               Cache.log.debug(
481                       "Starting questionnaire with default url: " + defurl);
482               desktop.checkForQuestionnaire(defurl);
483             }
484           }
485         }
486         else
487         {
488           System.err
489                   .println("CMD [-noquestionnaire] executed successfully!");
490         }
491
492         if (!aparser.contains("nonews"))
493         {
494           desktop.checkForNews();
495         }
496
497         BioJsHTMLOutput.updateBioJS();
498       }
499     }
500
501     // Move any new getdown-launcher-new.jar into place over old
502     // getdown-launcher.jar
503     String appdirString = System.getProperty("getdownappdir");
504     if (appdirString != null && appdirString.length() > 0)
505     {
506       final File appdir = new File(appdirString);
507       new Thread()
508       {
509         @Override
510         public void run()
511         {
512           LaunchUtil.upgradeGetdown(
513                   new File(appdir, "getdown-launcher-old.jar"),
514                   new File(appdir, "getdown-launcher.jar"),
515                   new File(appdir, "getdown-launcher-new.jar"));
516         }
517       }.start();
518     }
519
520     String file = null, data = null;
521     FileFormatI format = null;
522     DataSourceType protocol = null;
523     FileLoader fileLoader = new FileLoader(!headless);
524
525     String groovyscript = null; // script to execute after all loading is
526     // completed one way or another
527     // extract groovy argument and execute if necessary
528     groovyscript = aparser.getValue("groovy", true);
529     file = aparser.getValue("open", true);
530
531     if (file == null && desktop == null)
532     {
533       System.out.println("No files to open!");
534       System.exit(1);
535     }
536     long progress = -1;
537     // Finally, deal with the remaining input data.
538     if (file != null)
539     {
540       if (!headless)
541       {
542         desktop.setProgressBar(
543                 MessageManager
544                         .getString("status.processing_commandline_args"),
545                 progress = System.currentTimeMillis());
546       }
547       System.out.println("CMD [-open " + file + "] executed successfully!");
548
549       if (!Platform.isJS())
550       /**
551        * ignore in JavaScript -- can't just file existence - could load it?
552        * 
553        * @j2sIgnore
554        */
555       {
556         if (!file.startsWith("http://") && !file.startsWith("https://"))
557         // BH 2019 added https check for Java
558         {
559           if (!(new File(file)).exists())
560           {
561             System.out.println("Can't find " + file);
562             if (headless)
563             {
564               System.exit(1);
565             }
566           }
567         }
568       }
569
570       protocol = AppletFormatAdapter.checkProtocol(file);
571
572       try
573       {
574         format = new IdentifyFile().identify(file, protocol);
575       } catch (FileFormatException e1)
576       {
577         // TODO ?
578       }
579
580       AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
581               format);
582       if (af == null)
583       {
584         System.out.println("error");
585       }
586       else
587       {
588         setCurrentAlignFrame(af);
589         data = aparser.getValue("colour", true);
590         if (data != null)
591         {
592           data.replaceAll("%20", " ");
593
594           ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
595                   af.getViewport(), af.getViewport().getAlignment(), data);
596
597           if (cs != null)
598           {
599             System.out.println(
600                     "CMD [-color " + data + "] executed successfully!");
601           }
602           af.changeColour(cs);
603         }
604
605         // Must maintain ability to use the groups flag
606         data = aparser.getValue("groups", true);
607         if (data != null)
608         {
609           af.parseFeaturesFile(data,
610                   AppletFormatAdapter.checkProtocol(data));
611           // System.out.println("Added " + data);
612           System.out.println(
613                   "CMD groups[-" + data + "]  executed successfully!");
614         }
615         data = aparser.getValue("features", true);
616         if (data != null)
617         {
618           af.parseFeaturesFile(data,
619                   AppletFormatAdapter.checkProtocol(data));
620           // System.out.println("Added " + data);
621           System.out.println(
622                   "CMD [-features " + data + "]  executed successfully!");
623         }
624
625         data = aparser.getValue("annotations", true);
626         if (data != null)
627         {
628           af.loadJalviewDataFile(data, null, null, null);
629           // System.out.println("Added " + data);
630           System.out.println(
631                   "CMD [-annotations " + data + "] executed successfully!");
632         }
633         // set or clear the sortbytree flag.
634         if (aparser.contains("sortbytree"))
635         {
636           af.getViewport().setSortByTree(true);
637           if (af.getViewport().getSortByTree())
638           {
639             System.out.println("CMD [-sortbytree] executed successfully!");
640           }
641         }
642         if (aparser.contains("no-annotation"))
643         {
644           af.getViewport().setShowAnnotation(false);
645           if (!af.getViewport().isShowAnnotation())
646           {
647             System.out.println("CMD no-annotation executed successfully!");
648           }
649         }
650         if (aparser.contains("nosortbytree"))
651         {
652           af.getViewport().setSortByTree(false);
653           if (!af.getViewport().getSortByTree())
654           {
655             System.out
656                     .println("CMD [-nosortbytree] executed successfully!");
657           }
658         }
659         data = aparser.getValue("tree", true);
660         if (data != null)
661         {
662           try
663           {
664             System.out.println(
665                     "CMD [-tree " + data + "] executed successfully!");
666             NewickFile nf = new NewickFile(data,
667                     AppletFormatAdapter.checkProtocol(data));
668             af.getViewport()
669                     .setCurrentTree(af.showNewickTree(nf, data).getTree());
670           } catch (IOException ex)
671           {
672             System.err.println("Couldn't add tree " + data);
673             ex.printStackTrace(System.err);
674           }
675         }
676         // TODO - load PDB structure(s) to alignment JAL-629
677         // (associate with identical sequence in alignment, or a specified
678         // sequence)
679         if (groovyscript != null)
680         {
681           // Execute the groovy script after we've done all the rendering stuff
682           // and before any images or figures are generated.
683           System.out.println("Executing script " + groovyscript);
684           executeGroovyScript(groovyscript, af);
685           System.out.println("CMD groovy[" + groovyscript
686                   + "] executed successfully!");
687           groovyscript = null;
688         }
689         String imageName = "unnamed.png";
690         while (aparser.getSize() > 1)
691         {
692           String outputFormat = aparser.nextValue();
693           file = aparser.nextValue();
694
695           if (outputFormat.equalsIgnoreCase("png"))
696           {
697             af.createPNG(new File(file));
698             imageName = (new File(file)).getName();
699             System.out.println("Creating PNG image: " + file);
700             continue;
701           }
702           else if (outputFormat.equalsIgnoreCase("svg"))
703           {
704             File imageFile = new File(file);
705             imageName = imageFile.getName();
706             af.createSVG(imageFile);
707             System.out.println("Creating SVG image: " + file);
708             continue;
709           }
710           else if (outputFormat.equalsIgnoreCase("html"))
711           {
712             File imageFile = new File(file);
713             imageName = imageFile.getName();
714             HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
715             htmlSVG.exportHTML(file);
716
717             System.out.println("Creating HTML image: " + file);
718             continue;
719           }
720           else if (outputFormat.equalsIgnoreCase("biojsmsa"))
721           {
722             if (file == null)
723             {
724               System.err.println("The output html file must not be null");
725               return;
726             }
727             try
728             {
729               BioJsHTMLOutput.refreshVersionInfo(
730                       BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
731             } catch (URISyntaxException e)
732             {
733               e.printStackTrace();
734             }
735             BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
736             bjs.exportHTML(file);
737             System.out
738                     .println("Creating BioJS MSA Viwer HTML file: " + file);
739             continue;
740           }
741           else if (outputFormat.equalsIgnoreCase("imgMap"))
742           {
743             af.createImageMap(new File(file), imageName);
744             System.out.println("Creating image map: " + file);
745             continue;
746           }
747           else if (outputFormat.equalsIgnoreCase("eps"))
748           {
749             File outputFile = new File(file);
750             System.out.println(
751                     "Creating EPS file: " + outputFile.getAbsolutePath());
752             af.createEPS(outputFile);
753             continue;
754           }
755
756           af.saveAlignment(file, format);
757           if (af.isSaveAlignmentSuccessful())
758           {
759             System.out.println("Written alignment in " + format
760                     + " format to " + file);
761           }
762           else
763           {
764             System.out.println("Error writing file " + file + " in "
765                     + format + " format!!");
766           }
767
768         }
769
770         while (aparser.getSize() > 0)
771         {
772           System.out.println("Unknown arg: " + aparser.nextValue());
773         }
774       }
775     }
776     AlignFrame startUpAlframe = null;
777     // We'll only open the default file if the desktop is visible.
778     // And the user
779     // ////////////////////
780
781     if (!Platform.isJS() && !headless && file == null
782             && Cache.getDefault("SHOW_STARTUP_FILE", true))
783     /**
784      * Java only
785      * 
786      * @j2sIgnore
787      */
788     {
789       file = Cache.getDefault("STARTUP_FILE",
790               Cache.getDefault("www.jalview.org", "http://www.jalview.org")
791                       + "/examples/exampleFile_2_7.jar");
792       if (file.equals(
793               "http://www.jalview.org/examples/exampleFile_2_3.jar"))
794       {
795         // hardwire upgrade of the startup file
796         file.replace("_2_3.jar", "_2_7.jar");
797         // and remove the stale setting
798         Cache.removeProperty("STARTUP_FILE");
799       }
800
801       protocol = DataSourceType.FILE;
802
803       if (file.indexOf("http:") > -1)
804       {
805         protocol = DataSourceType.URL;
806       }
807
808       if (file.endsWith(".jar"))
809       {
810         format = FileFormat.Jalview;
811       }
812       else
813       {
814         try
815         {
816           format = new IdentifyFile().identify(file, protocol);
817         } catch (FileFormatException e)
818         {
819           // TODO what?
820         }
821       }
822
823       startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
824               format);
825       // extract groovy arguments before anything else.
826     }
827
828     // Once all other stuff is done, execute any groovy scripts (in order)
829     if (groovyscript != null)
830     {
831       if (Cache.groovyJarsPresent())
832       {
833         System.out.println("Executing script " + groovyscript);
834         executeGroovyScript(groovyscript, startUpAlframe);
835       }
836       else
837       {
838         System.err.println(
839                 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
840                         + groovyscript);
841       }
842     }
843     // and finally, turn off batch mode indicator - if the desktop still exists
844     if (desktop != null)
845     {
846       if (progress != -1)
847       {
848         desktop.setProgressBar(null, progress);
849       }
850       desktop.setInBatchMode(false);
851     }
852   }
853
854   private static void showUsage()
855   {
856     System.out.println(
857             "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
858                     + "-nodisplay\tRun Jalview without User Interface.\n"
859                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
860                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
861                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
862                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
863                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
864                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
865                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
866                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
867                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
868                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
869                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
870                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
871                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
872                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
873                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
874                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
875                     + "-html FILE\tCreate HTML file from alignment.\n"
876                     + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
877                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
878                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
879                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
880                     + "-noquestionnaire\tTurn off questionnaire check.\n"
881                     + "-nonews\tTurn off check for Jalview news.\n"
882                     + "-nousagestats\tTurn off google analytics tracking for this session.\n"
883                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
884                     // +
885                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
886                     // after all other properties files have been read\n\t
887                     // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
888                     // passed in correctly)"
889                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
890                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
891                     + "-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"
892                     + "-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"
893                     + "-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"
894                     + "\n~Read documentation in Application or visit http://www.jalview.org for description of Features and Annotations file~\n\n");
895   }
896
897   private static void startUsageStats(final Desktop desktop)
898   {
899     /**
900      * start a User Config prompt asking if we can log usage statistics.
901      */
902     PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
903             "USAGESTATS", "Jalview Usage Statistics",
904             "Do you want to help make Jalview better by enabling "
905                     + "the collection of usage statistics with Google Analytics ?"
906                     + "\n\n(you can enable or disable usage tracking in the preferences)",
907             new Runnable()
908             {
909               @Override
910               public void run()
911               {
912                 Cache.log.debug(
913                         "Initialising googletracker for usage stats.");
914                 Cache.initGoogleTracker();
915                 Cache.log.debug("Tracking enabled.");
916               }
917             }, new Runnable()
918             {
919               @Override
920               public void run()
921               {
922                 Cache.log.debug("Not enabling Google Tracking.");
923               }
924             }, null, true);
925     desktop.addDialogThread(prompter);
926   }
927
928   /**
929    * Locate the given string as a file and pass it to the groovy interpreter.
930    * 
931    * @param groovyscript
932    *          the script to execute
933    * @param jalviewContext
934    *          the Jalview Desktop object passed in to the groovy binding as the
935    *          'Jalview' object.
936    */
937   private void executeGroovyScript(String groovyscript, AlignFrame af)
938   {
939     /**
940      * for scripts contained in files
941      */
942     File tfile = null;
943     /**
944      * script's URI
945      */
946     URL sfile = null;
947     if (groovyscript.trim().equals("STDIN"))
948     {
949       // read from stdin into a tempfile and execute it
950       try
951       {
952         tfile = File.createTempFile("jalview", "groovy");
953         PrintWriter outfile = new PrintWriter(
954                 new OutputStreamWriter(new FileOutputStream(tfile)));
955         BufferedReader br = new BufferedReader(
956                 new InputStreamReader(System.in));
957         String line = null;
958         while ((line = br.readLine()) != null)
959         {
960           outfile.write(line + "\n");
961         }
962         br.close();
963         outfile.flush();
964         outfile.close();
965
966       } catch (Exception ex)
967       {
968         System.err.println("Failed to read from STDIN into tempfile "
969                 + ((tfile == null) ? "(tempfile wasn't created)"
970                         : tfile.toString()));
971         ex.printStackTrace();
972         return;
973       }
974       try
975       {
976         sfile = tfile.toURI().toURL();
977       } catch (Exception x)
978       {
979         System.err.println(
980                 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
981                         + tfile.toURI());
982         x.printStackTrace();
983         return;
984       }
985     }
986     else
987     {
988       try
989       {
990         sfile = new URI(groovyscript).toURL();
991       } catch (Exception x)
992       {
993         tfile = new File(groovyscript);
994         if (!tfile.exists())
995         {
996           System.err.println("File '" + groovyscript + "' does not exist.");
997           return;
998         }
999         if (!tfile.canRead())
1000         {
1001           System.err.println("File '" + groovyscript + "' cannot be read.");
1002           return;
1003         }
1004         if (tfile.length() < 1)
1005         {
1006           System.err.println("File '" + groovyscript + "' is empty.");
1007           return;
1008         }
1009         try
1010         {
1011           sfile = tfile.getAbsoluteFile().toURI().toURL();
1012         } catch (Exception ex)
1013         {
1014           System.err.println("Failed to create a file URL for "
1015                   + tfile.getAbsoluteFile());
1016           return;
1017         }
1018       }
1019     }
1020     try
1021     {
1022       Map<String, java.lang.Object> vbinding = new HashMap<>();
1023       vbinding.put("Jalview", this);
1024       if (af != null)
1025       {
1026         vbinding.put("currentAlFrame", af);
1027       }
1028       Binding gbinding = new Binding(vbinding);
1029       GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1030       gse.run(sfile.toString(), gbinding);
1031       if ("STDIN".equals(groovyscript))
1032       {
1033         // delete temp file that we made -
1034         // only if it was successfully executed
1035         tfile.delete();
1036       }
1037     } catch (Exception e)
1038     {
1039       System.err.println("Exception Whilst trying to execute file " + sfile
1040               + " as a groovy script.");
1041       e.printStackTrace(System.err);
1042
1043     }
1044   }
1045
1046   public static boolean isHeadlessMode()
1047   {
1048     String isheadless = System.getProperty("java.awt.headless");
1049     if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1050     {
1051       return true;
1052     }
1053     return false;
1054   }
1055
1056   public AlignFrame[] getAlignFrames()
1057   {
1058     return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1059             : Desktop.getAlignFrames();
1060
1061   }
1062
1063   /**
1064    * Quit method delegates to Desktop.quit - unless running in headless mode
1065    * when it just ends the JVM
1066    */
1067   public void quit()
1068   {
1069     if (desktop != null)
1070     {
1071       desktop.quit();
1072     }
1073     else
1074     {
1075       System.exit(0);
1076     }
1077   }
1078
1079   public static AlignFrame getCurrentAlignFrame()
1080   {
1081     return Jalview.currentAlignFrame;
1082   }
1083
1084   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
1085   {
1086     Jalview.currentAlignFrame = currentAlignFrame;
1087   }
1088 }