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