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