JAL-3608 Changes from code review CR-JAL-236
[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     return setGenericLookAndFeel(false);
821   }
822
823   private static boolean setSystemLookAndFeel()
824   {
825     return setGenericLookAndFeel(true);
826   }
827
828   private static boolean setGenericLookAndFeel(boolean system)
829   {
830     boolean set = false;
831     try
832     {
833       UIManager.setLookAndFeel(
834               system ? UIManager.getSystemLookAndFeelClassName()
835                       : UIManager.getCrossPlatformLookAndFeelClassName());
836       set = true;
837     } catch (Exception ex)
838     {
839       Cache.log.error("Unexpected Look and Feel Exception");
840       Cache.log.error(ex.getMessage());
841       Cache.log.debug(Cache.getStackTraceString(ex));
842     }
843     return set;
844   }
845
846   private static boolean setSpecificLookAndFeel(String name,
847           String className, boolean nameStartsWith)
848   {
849     boolean set = false;
850     try
851     {
852       for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
853       {
854         if (info.getName() != null && nameStartsWith
855                 ? info.getName().toLowerCase()
856                         .startsWith(name.toLowerCase())
857                 : info.getName().toLowerCase().equals(name.toLowerCase()))
858         {
859           className = info.getClassName();
860           break;
861         }
862       }
863       UIManager.setLookAndFeel(className);
864       set = true;
865     } catch (Exception ex)
866     {
867       Cache.log.error("Unexpected Look and Feel Exception");
868       Cache.log.error(ex.getMessage());
869       Cache.log.debug(Cache.getStackTraceString(ex));
870     }
871     return set;
872   }
873
874   private static boolean setGtkLookAndFeel()
875   {
876     return setSpecificLookAndFeel("gtk",
877             "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true);
878   }
879
880   private static boolean setMetalLookAndFeel()
881   {
882     return setSpecificLookAndFeel("metal",
883             "javax.swing.plaf.metal.MetalLookAndFeel", false);
884   }
885
886   private static boolean setNimbusLookAndFeel()
887   {
888     return setSpecificLookAndFeel("nimbus",
889             "javax.swing.plaf.nimbus.NimbusLookAndFeel", false);
890   }
891
892   private static boolean setQuaquaLookAndFeel()
893   {
894     return setSpecificLookAndFeel("quaqua",
895             ch.randelshofer.quaqua.QuaquaManager.getLookAndFeel().getClass()
896                     .getName(),
897             false);
898   }
899
900   private static boolean setVaquaLookAndFeel()
901   {
902     return setSpecificLookAndFeel("vaqua",
903             "org.violetlib.aqua.AquaLookAndFeel", false);
904   }
905
906   private static boolean setMacLookAndFeel()
907   {
908     boolean set = false;
909     System.setProperty("com.apple.mrj.application.apple.menu.about.name",
910             "Jalview");
911     System.setProperty("apple.laf.useScreenMenuBar", "true");
912     set = setQuaquaLookAndFeel();
913     if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
914             .toLowerCase().contains("quaqua"))
915     {
916       set = setVaquaLookAndFeel();
917     }
918     return set;
919   }
920
921   private static void showUsage()
922   {
923     System.out.println(
924             "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
925                     + "-nodisplay\tRun Jalview without User Interface.\n"
926                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
927                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
928                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
929                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
930                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
931                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
932                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
933                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
934                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
935                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
936                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
937                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
938                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
939                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
940                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
941                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
942                     + "-html FILE\tCreate HTML file from alignment.\n"
943                     + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
944                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
945                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
946                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
947                     + "-noquestionnaire\tTurn off questionnaire check.\n"
948                     + "-nonews\tTurn off check for Jalview news.\n"
949                     + "-nousagestats\tTurn off google analytics tracking for this session.\n"
950                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
951                     // +
952                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
953                     // after all other properties files have been read\n\t
954                     // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
955                     // passed in correctly)"
956                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
957                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
958                     + "-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"
959                     + "-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"
960                     + "-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"
961                     + "\n~Read documentation in Application or visit http://www.jalview.org for description of Features and Annotations file~\n\n");
962   }
963
964   private static void startUsageStats(final Desktop desktop)
965   {
966     /**
967      * start a User Config prompt asking if we can log usage statistics.
968      */
969     PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
970             "USAGESTATS", "Jalview Usage Statistics",
971             "Do you want to help make Jalview better by enabling "
972                     + "the collection of usage statistics with Google Analytics ?"
973                     + "\n\n(you can enable or disable usage tracking in the preferences)",
974             new Runnable()
975             {
976               @Override
977               public void run()
978               {
979                 Cache.log.debug(
980                         "Initialising googletracker for usage stats.");
981                 Cache.initGoogleTracker();
982                 Cache.log.debug("Tracking enabled.");
983               }
984             }, new Runnable()
985             {
986               @Override
987               public void run()
988               {
989                 Cache.log.debug("Not enabling Google Tracking.");
990               }
991             }, null, true);
992     desktop.addDialogThread(prompter);
993   }
994
995   /**
996    * Locate the given string as a file and pass it to the groovy interpreter.
997    * 
998    * @param groovyscript
999    *          the script to execute
1000    * @param jalviewContext
1001    *          the Jalview Desktop object passed in to the groovy binding as the
1002    *          'Jalview' object.
1003    */
1004   private void executeGroovyScript(String groovyscript, AlignFrame af)
1005   {
1006     /**
1007      * for scripts contained in files
1008      */
1009     File tfile = null;
1010     /**
1011      * script's URI
1012      */
1013     URL sfile = null;
1014     if (groovyscript.trim().equals("STDIN"))
1015     {
1016       // read from stdin into a tempfile and execute it
1017       try
1018       {
1019         tfile = File.createTempFile("jalview", "groovy");
1020         PrintWriter outfile = new PrintWriter(
1021                 new OutputStreamWriter(new FileOutputStream(tfile)));
1022         BufferedReader br = new BufferedReader(
1023                 new InputStreamReader(System.in));
1024         String line = null;
1025         while ((line = br.readLine()) != null)
1026         {
1027           outfile.write(line + "\n");
1028         }
1029         br.close();
1030         outfile.flush();
1031         outfile.close();
1032
1033       } catch (Exception ex)
1034       {
1035         System.err.println("Failed to read from STDIN into tempfile "
1036                 + ((tfile == null) ? "(tempfile wasn't created)"
1037                         : tfile.toString()));
1038         ex.printStackTrace();
1039         return;
1040       }
1041       try
1042       {
1043         sfile = tfile.toURI().toURL();
1044       } catch (Exception x)
1045       {
1046         System.err.println(
1047                 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1048                         + tfile.toURI());
1049         x.printStackTrace();
1050         return;
1051       }
1052     }
1053     else
1054     {
1055       try
1056       {
1057         sfile = new URI(groovyscript).toURL();
1058       } catch (Exception x)
1059       {
1060         tfile = new File(groovyscript);
1061         if (!tfile.exists())
1062         {
1063           System.err.println("File '" + groovyscript + "' does not exist.");
1064           return;
1065         }
1066         if (!tfile.canRead())
1067         {
1068           System.err.println("File '" + groovyscript + "' cannot be read.");
1069           return;
1070         }
1071         if (tfile.length() < 1)
1072         {
1073           System.err.println("File '" + groovyscript + "' is empty.");
1074           return;
1075         }
1076         try
1077         {
1078           sfile = tfile.getAbsoluteFile().toURI().toURL();
1079         } catch (Exception ex)
1080         {
1081           System.err.println("Failed to create a file URL for "
1082                   + tfile.getAbsoluteFile());
1083           return;
1084         }
1085       }
1086     }
1087     try
1088     {
1089       Map<String, java.lang.Object> vbinding = new HashMap<>();
1090       vbinding.put("Jalview", this);
1091       if (af != null)
1092       {
1093         vbinding.put("currentAlFrame", af);
1094       }
1095       Binding gbinding = new Binding(vbinding);
1096       GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1097       gse.run(sfile.toString(), gbinding);
1098       if ("STDIN".equals(groovyscript))
1099       {
1100         // delete temp file that we made -
1101         // only if it was successfully executed
1102         tfile.delete();
1103       }
1104     } catch (Exception e)
1105     {
1106       System.err.println("Exception Whilst trying to execute file " + sfile
1107               + " as a groovy script.");
1108       e.printStackTrace(System.err);
1109
1110     }
1111   }
1112
1113   public static boolean isHeadlessMode()
1114   {
1115     String isheadless = System.getProperty("java.awt.headless");
1116     if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1117     {
1118       return true;
1119     }
1120     return false;
1121   }
1122
1123   public AlignFrame[] getAlignFrames()
1124   {
1125     return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1126             : Desktop.getAlignFrames();
1127
1128   }
1129
1130   /**
1131    * Quit method delegates to Desktop.quit - unless running in headless mode
1132    * when it just ends the JVM
1133    */
1134   public void quit()
1135   {
1136     if (desktop != null)
1137     {
1138       desktop.quit();
1139     }
1140     else
1141     {
1142       System.exit(0);
1143     }
1144   }
1145
1146   public static AlignFrame getCurrentAlignFrame()
1147   {
1148     return Jalview.currentAlignFrame;
1149   }
1150
1151   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
1152   {
1153     Jalview.currentAlignFrame = currentAlignFrame;
1154   }
1155 }