3a238bb0e3f90fc90c201dd2ad7fdcf31172f2b7
[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       {
380         desktop.checkForNews();
381       }
382
383       BioJsHTMLOutput.updateBioJS();
384     }
385
386     // Move any new getdown-launcher-new.jar into place over old
387     // getdown-launcher.jar
388     String appdirString = System.getProperty("getdownappdir");
389     if (appdirString != null && appdirString.length() > 0)
390     {
391       final File appdir = new File(appdirString);
392       new Thread()
393       {
394         @Override
395         public void run()
396         {
397           LaunchUtil.upgradeGetdown(
398                   new File(appdir, "getdown-launcher-old.jar"),
399                   new File(appdir, "getdown-launcher.jar"),
400                   new File(appdir, "getdown-launcher-new.jar"));
401         }
402       }.start();
403     }
404
405     String file = null, data = null;
406     FileFormatI format = null;
407     DataSourceType protocol = null;
408     FileLoader fileLoader = new FileLoader(!headless);
409
410     String groovyscript = null; // script to execute after all loading is
411     // completed one way or another
412     // extract groovy argument and execute if necessary
413     groovyscript = aparser.getValue("groovy", true);
414     file = aparser.getValue("open", true);
415
416     if (file == null && desktop == null)
417     {
418       System.out.println("No files to open!");
419       System.exit(1);
420     }
421     long progress = -1;
422     // Finally, deal with the remaining input data.
423     if (file != null)
424     {
425       if (!headless)
426       {
427         desktop.setProgressBar(
428                 MessageManager
429                         .getString("status.processing_commandline_args"),
430                 progress = System.currentTimeMillis());
431       }
432       System.out.println("CMD [-open " + file + "] executed successfully!");
433
434       protocol = AppletFormatAdapter.checkProtocol(file);
435
436       if (protocol == DataSourceType.FILE)
437       {
438         if (!(new File(file)).exists())
439         {
440           System.out.println("Can't find " + file);
441           if (headless)
442           {
443             System.exit(1);
444           }
445         }
446       }
447
448       try
449       {
450         format = new IdentifyFile().identify(file, protocol);
451       } catch (FileFormatException e1)
452       {
453         // TODO ?
454       }
455
456       AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
457               format);
458       if (af == null)
459       {
460         System.out.println("error");
461       }
462       else
463       {
464         setCurrentAlignFrame(af);
465         data = aparser.getValue("colour", true);
466         if (data != null)
467         {
468           data.replaceAll("%20", " ");
469
470           ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
471                   af.getViewport(), af.getViewport().getAlignment(), data);
472
473           if (cs != null)
474           {
475             System.out.println(
476                     "CMD [-color " + data + "] executed successfully!");
477           }
478           af.changeColour(cs);
479         }
480
481         // Must maintain ability to use the groups flag
482         data = aparser.getValue("groups", true);
483         if (data != null)
484         {
485           af.parseFeaturesFile(data,
486                   AppletFormatAdapter.checkProtocol(data));
487           // System.out.println("Added " + data);
488           System.out.println(
489                   "CMD groups[-" + data + "]  executed successfully!");
490         }
491         data = aparser.getValue("features", true);
492         if (data != null)
493         {
494           af.parseFeaturesFile(data,
495                   AppletFormatAdapter.checkProtocol(data));
496           // System.out.println("Added " + data);
497           System.out.println(
498                   "CMD [-features " + data + "]  executed successfully!");
499         }
500
501         data = aparser.getValue("annotations", true);
502         if (data != null)
503         {
504           af.loadJalviewDataFile(data, null, null, null);
505           // System.out.println("Added " + data);
506           System.out.println(
507                   "CMD [-annotations " + data + "] executed successfully!");
508         }
509         // set or clear the sortbytree flag.
510         if (aparser.contains("sortbytree"))
511         {
512           af.getViewport().setSortByTree(true);
513           if (af.getViewport().getSortByTree())
514           {
515             System.out.println("CMD [-sortbytree] executed successfully!");
516           }
517         }
518         if (aparser.contains("no-annotation"))
519         {
520           af.getViewport().setShowAnnotation(false);
521           if (!af.getViewport().isShowAnnotation())
522           {
523             System.out.println("CMD no-annotation executed successfully!");
524           }
525         }
526         if (aparser.contains("nosortbytree"))
527         {
528           af.getViewport().setSortByTree(false);
529           if (!af.getViewport().getSortByTree())
530           {
531             System.out
532                     .println("CMD [-nosortbytree] executed successfully!");
533           }
534         }
535         data = aparser.getValue("tree", true);
536         if (data != null)
537         {
538           try
539           {
540             System.out.println(
541                     "CMD [-tree " + data + "] executed successfully!");
542             NewickFile nf = new NewickFile(data,
543                     AppletFormatAdapter.checkProtocol(data));
544             af.getViewport()
545                     .setCurrentTree(af.showNewickTree(nf, data).getTree());
546           } catch (IOException ex)
547           {
548             System.err.println("Couldn't add tree " + data);
549             ex.printStackTrace(System.err);
550           }
551         }
552         // TODO - load PDB structure(s) to alignment JAL-629
553         // (associate with identical sequence in alignment, or a specified
554         // sequence)
555         if (groovyscript != null)
556         {
557           // Execute the groovy script after we've done all the rendering stuff
558           // and before any images or figures are generated.
559           System.out.println("Executing script " + groovyscript);
560           executeGroovyScript(groovyscript, af);
561           System.out.println("CMD groovy[" + groovyscript
562                   + "] executed successfully!");
563           groovyscript = null;
564         }
565         String imageName = "unnamed.png";
566         while (aparser.getSize() > 1)
567         {
568           String outputFormat = aparser.nextValue();
569           file = aparser.nextValue();
570
571           if (outputFormat.equalsIgnoreCase("png"))
572           {
573             af.createPNG(new File(file));
574             imageName = (new File(file)).getName();
575             System.out.println("Creating PNG image: " + file);
576             continue;
577           }
578           else if (outputFormat.equalsIgnoreCase("svg"))
579           {
580             File imageFile = new File(file);
581             imageName = imageFile.getName();
582             af.createSVG(imageFile);
583             System.out.println("Creating SVG image: " + file);
584             continue;
585           }
586           else if (outputFormat.equalsIgnoreCase("html"))
587           {
588             File imageFile = new File(file);
589             imageName = imageFile.getName();
590             HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
591             htmlSVG.exportHTML(file);
592
593             System.out.println("Creating HTML image: " + file);
594             continue;
595           }
596           else if (outputFormat.equalsIgnoreCase("biojsmsa"))
597           {
598             if (file == null)
599             {
600               System.err.println("The output html file must not be null");
601               return;
602             }
603             try
604             {
605               BioJsHTMLOutput.refreshVersionInfo(
606                       BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
607             } catch (URISyntaxException e)
608             {
609               e.printStackTrace();
610             }
611             BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
612             bjs.exportHTML(file);
613             System.out
614                     .println("Creating BioJS MSA Viwer HTML file: " + file);
615             continue;
616           }
617           else if (outputFormat.equalsIgnoreCase("imgMap"))
618           {
619             af.createImageMap(new File(file), imageName);
620             System.out.println("Creating image map: " + file);
621             continue;
622           }
623           else if (outputFormat.equalsIgnoreCase("eps"))
624           {
625             File outputFile = new File(file);
626             System.out.println(
627                     "Creating EPS file: " + outputFile.getAbsolutePath());
628             af.createEPS(outputFile);
629             continue;
630           }
631           FileFormatI outFormat = null;
632           try
633           {
634             outFormat = FileFormats.getInstance().forName(outputFormat);
635           } catch (Exception formatP)
636           {
637             System.out.println("Couldn't parse " + outFormat
638                     + " as a valid Jalview format string.");
639           }
640           if (outFormat != null)
641           {
642             if (!outFormat.isWritable())
643             {
644               System.out.println(
645                       "This version of Jalview does not support alignment export as "
646                               + outputFormat);
647             }
648             else
649             {
650               if (af.saveAlignment(file, outFormat))
651               {
652                 System.out.println("Written alignment in " + format
653                         + " format to " + file);
654               }
655               else
656               {
657                 System.out.println("Error writing file " + file + " in "
658                         + format + " format!!");
659               }
660             }
661           }
662         }
663
664         while (aparser.getSize() > 0)
665         {
666           System.out.println("Unknown arg: " + aparser.nextValue());
667         }
668       }
669     }
670     AlignFrame startUpAlframe = null;
671     // We'll only open the default file if the desktop is visible.
672     // And the user
673     // ////////////////////
674
675     if (!headless && file == null
676             && jalview.bin.Cache.getDefault("SHOW_STARTUP_FILE", true))
677     {
678       file = jalview.bin.Cache.getDefault("STARTUP_FILE",
679               jalview.bin.Cache.getDefault("www.jalview.org",
680                       "https://www.jalview.org")
681                       + "/examples/exampleFile_2_7.jvp");
682       if (file.equals("http://www.jalview.org/examples/exampleFile_2_3.jar")
683               || file.equals(
684                       "http://www.jalview.org/examples/exampleFile_2_7.jar"))
685       {
686         file.replace("http:", "https:");
687         // hardwire upgrade of the startup file
688         file.replace("_2_3", "_2_7");
689         file.replace("2_7.jar", "2_7.jvp");
690         // and remove the stale setting
691         jalview.bin.Cache.removeProperty("STARTUP_FILE");
692       }
693
694       protocol = AppletFormatAdapter.checkProtocol(file);
695
696       if (file.endsWith(".jar"))
697       {
698         format = FileFormat.Jalview;
699       }
700       else
701       {
702         try
703         {
704           format = new IdentifyFile().identify(file, protocol);
705         } catch (FileFormatException e)
706         {
707           // TODO what?
708         }
709       }
710
711       startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
712               format);
713       // extract groovy arguments before anything else.
714     }
715
716     // Once all other stuff is done, execute any groovy scripts (in order)
717     if (groovyscript != null)
718     {
719       if (Cache.groovyJarsPresent())
720       {
721         System.out.println("Executing script " + groovyscript);
722         executeGroovyScript(groovyscript, startUpAlframe);
723       }
724       else
725       {
726         System.err.println(
727                 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
728                         + groovyscript);
729       }
730     }
731     // and finally, turn off batch mode indicator - if the desktop still exists
732     if (desktop != null)
733     {
734       if (progress != -1)
735       {
736         desktop.setProgressBar(null, progress);
737       }
738       desktop.setInBatchMode(false);
739     }
740   }
741
742   private static void setLookAndFeel()
743   {
744     // property laf = "crossplatform", "system", "gtk", "metal", "nimbus" or
745     // "mac"
746     // If not set (or chosen laf fails), use the normal SystemLaF and if on Mac,
747     // try Quaqua/Vaqua.
748     String lafProp = System.getProperty("laf");
749     String lafSetting = Cache.getDefault("PREFERRED_LAF", null);
750     String laf = "none";
751     if (lafProp != null)
752     {
753       laf = lafProp;
754     }
755     else if (lafSetting != null)
756     {
757       laf = lafSetting;
758     }
759     boolean lafSet = false;
760     switch (laf)
761     {
762     case "crossplatform":
763       lafSet = setCrossPlatformLookAndFeel();
764       if (!lafSet)
765       {
766         Cache.log.error("Could not set requested laf=" + laf);
767       }
768       break;
769     case "system":
770       lafSet = setSystemLookAndFeel();
771       if (!lafSet)
772       {
773         Cache.log.error("Could not set requested laf=" + laf);
774       }
775       break;
776     case "gtk":
777       lafSet = setGtkLookAndFeel();
778       if (!lafSet)
779       {
780         Cache.log.error("Could not set requested laf=" + laf);
781       }
782       break;
783     case "metal":
784       lafSet = setMetalLookAndFeel();
785       if (!lafSet)
786       {
787         Cache.log.error("Could not set requested laf=" + laf);
788       }
789       break;
790     case "nimbus":
791       lafSet = setNimbusLookAndFeel();
792       if (!lafSet)
793       {
794         Cache.log.error("Could not set requested laf=" + laf);
795       }
796       break;
797     case "quaqua":
798       lafSet = setQuaquaLookAndFeel();
799       if (!lafSet)
800       {
801         Cache.log.error("Could not set requested laf=" + laf);
802       }
803       break;
804     case "vaqua":
805       lafSet = setVaquaLookAndFeel();
806       if (!lafSet)
807       {
808         Cache.log.error("Could not set requested laf=" + laf);
809       }
810       break;
811     case "mac":
812       lafSet = setMacLookAndFeel();
813       if (!lafSet)
814       {
815         Cache.log.error("Could not set requested laf=" + laf);
816       }
817       break;
818     case "none":
819       break;
820     default:
821       Cache.log.error("Requested laf=" + laf + " not implemented");
822     }
823     if (!lafSet)
824     {
825       setSystemLookAndFeel();
826       if (Platform.isLinux())
827       {
828         setMetalLookAndFeel();
829       }
830       if (Platform.isAMac())
831       {
832         setMacLookAndFeel();
833       }
834     }
835   }
836
837   private static boolean setCrossPlatformLookAndFeel()
838   {
839     return setGenericLookAndFeel(false);
840   }
841
842   private static boolean setSystemLookAndFeel()
843   {
844     return setGenericLookAndFeel(true);
845   }
846
847   private static boolean setGenericLookAndFeel(boolean system)
848   {
849     boolean set = false;
850     try
851     {
852       UIManager.setLookAndFeel(
853               system ? UIManager.getSystemLookAndFeelClassName()
854                       : UIManager.getCrossPlatformLookAndFeelClassName());
855       set = true;
856     } catch (Exception ex)
857     {
858       Cache.log.error("Unexpected Look and Feel Exception");
859       Cache.log.error(ex.getMessage());
860       Cache.log.debug(Cache.getStackTraceString(ex));
861     }
862     return set;
863   }
864
865   private static boolean setSpecificLookAndFeel(String name,
866           String className, boolean nameStartsWith)
867   {
868     boolean set = false;
869     try
870     {
871       for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
872       {
873         if (info.getName() != null && nameStartsWith
874                 ? info.getName().toLowerCase()
875                         .startsWith(name.toLowerCase())
876                 : info.getName().toLowerCase().equals(name.toLowerCase()))
877         {
878           className = info.getClassName();
879           break;
880         }
881       }
882       UIManager.setLookAndFeel(className);
883       set = true;
884     } catch (Exception ex)
885     {
886       Cache.log.error("Unexpected Look and Feel Exception");
887       Cache.log.error(ex.getMessage());
888       Cache.log.debug(Cache.getStackTraceString(ex));
889     }
890     return set;
891   }
892
893   private static boolean setGtkLookAndFeel()
894   {
895     return setSpecificLookAndFeel("gtk",
896             "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true);
897   }
898
899   private static boolean setMetalLookAndFeel()
900   {
901     return setSpecificLookAndFeel("metal",
902             "javax.swing.plaf.metal.MetalLookAndFeel", false);
903   }
904
905   private static boolean setNimbusLookAndFeel()
906   {
907     return setSpecificLookAndFeel("nimbus",
908             "javax.swing.plaf.nimbus.NimbusLookAndFeel", false);
909   }
910
911   private static boolean setQuaquaLookAndFeel()
912   {
913     return setSpecificLookAndFeel("quaqua",
914             ch.randelshofer.quaqua.QuaquaManager.getLookAndFeel().getClass()
915                     .getName(),
916             false);
917   }
918
919   private static boolean setVaquaLookAndFeel()
920   {
921     return setSpecificLookAndFeel("vaqua",
922             "org.violetlib.aqua.AquaLookAndFeel", false);
923   }
924
925   private static boolean setMacLookAndFeel()
926   {
927     boolean set = false;
928     System.setProperty("com.apple.mrj.application.apple.menu.about.name",
929             "Jalview");
930     System.setProperty("apple.laf.useScreenMenuBar", "true");
931     set = setQuaquaLookAndFeel();
932     if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
933             .toLowerCase().contains("quaqua"))
934     {
935       set = setVaquaLookAndFeel();
936     }
937     return set;
938   }
939
940   private static void showUsage()
941   {
942     System.out.println(
943             "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
944                     + "-nodisplay\tRun Jalview without User Interface.\n"
945                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
946                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
947                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
948                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
949                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
950                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
951                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
952                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
953                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
954                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
955                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
956                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
957                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
958                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
959                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
960                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
961                     + "-html FILE\tCreate HTML file from alignment.\n"
962                     + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
963                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
964                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
965                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
966                     + "-noquestionnaire\tTurn off questionnaire check.\n"
967                     + "-nonews\tTurn off check for Jalview news.\n"
968                     + "-nousagestats\tTurn off google analytics tracking for this session.\n"
969                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
970                     // +
971                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
972                     // after all other properties files have been read\n\t
973                     // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
974                     // passed in correctly)"
975                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
976                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
977                     + "-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"
978                     + "-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"
979                     + "-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"
980                     + "\n~Read documentation in Application or visit https://www.jalview.org for description of Features and Annotations file~\n\n");
981   }
982
983   private static void startUsageStats(final Desktop desktop)
984   {
985     /**
986      * start a User Config prompt asking if we can log usage statistics.
987      */
988     PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
989             "USAGESTATS", "Jalview Usage Statistics",
990             "Do you want to help make Jalview better by enabling "
991                     + "the collection of usage statistics with Google Analytics ?"
992                     + "\n\n(you can enable or disable usage tracking in the preferences)",
993             new Runnable()
994             {
995               @Override
996               public void run()
997               {
998                 Cache.log.debug(
999                         "Initialising googletracker for usage stats.");
1000                 Cache.initGoogleTracker();
1001                 Cache.log.debug("Tracking enabled.");
1002               }
1003             }, new Runnable()
1004             {
1005               @Override
1006               public void run()
1007               {
1008                 Cache.log.debug("Not enabling Google Tracking.");
1009               }
1010             }, null, true);
1011     desktop.addDialogThread(prompter);
1012   }
1013
1014   /**
1015    * Locate the given string as a file and pass it to the groovy interpreter.
1016    * 
1017    * @param groovyscript
1018    *          the script to execute
1019    * @param jalviewContext
1020    *          the Jalview Desktop object passed in to the groovy binding as the
1021    *          'Jalview' object.
1022    */
1023   private void executeGroovyScript(String groovyscript, AlignFrame af)
1024   {
1025     /**
1026      * for scripts contained in files
1027      */
1028     File tfile = null;
1029     /**
1030      * script's URI
1031      */
1032     URL sfile = null;
1033     if (groovyscript.trim().equals("STDIN"))
1034     {
1035       // read from stdin into a tempfile and execute it
1036       try
1037       {
1038         tfile = File.createTempFile("jalview", "groovy");
1039         PrintWriter outfile = new PrintWriter(
1040                 new OutputStreamWriter(new FileOutputStream(tfile)));
1041         BufferedReader br = new BufferedReader(
1042                 new InputStreamReader(System.in));
1043         String line = null;
1044         while ((line = br.readLine()) != null)
1045         {
1046           outfile.write(line + "\n");
1047         }
1048         br.close();
1049         outfile.flush();
1050         outfile.close();
1051
1052       } catch (Exception ex)
1053       {
1054         System.err.println("Failed to read from STDIN into tempfile "
1055                 + ((tfile == null) ? "(tempfile wasn't created)"
1056                         : tfile.toString()));
1057         ex.printStackTrace();
1058         return;
1059       }
1060       try
1061       {
1062         sfile = tfile.toURI().toURL();
1063       } catch (Exception x)
1064       {
1065         System.err.println(
1066                 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1067                         + tfile.toURI());
1068         x.printStackTrace();
1069         return;
1070       }
1071     }
1072     else
1073     {
1074       try
1075       {
1076         sfile = new URI(groovyscript).toURL();
1077       } catch (Exception x)
1078       {
1079         tfile = new File(groovyscript);
1080         if (!tfile.exists())
1081         {
1082           System.err.println("File '" + groovyscript + "' does not exist.");
1083           return;
1084         }
1085         if (!tfile.canRead())
1086         {
1087           System.err.println("File '" + groovyscript + "' cannot be read.");
1088           return;
1089         }
1090         if (tfile.length() < 1)
1091         {
1092           System.err.println("File '" + groovyscript + "' is empty.");
1093           return;
1094         }
1095         try
1096         {
1097           sfile = tfile.getAbsoluteFile().toURI().toURL();
1098         } catch (Exception ex)
1099         {
1100           System.err.println("Failed to create a file URL for "
1101                   + tfile.getAbsoluteFile());
1102           return;
1103         }
1104       }
1105     }
1106     try
1107     {
1108       Map<String, java.lang.Object> vbinding = new HashMap<>();
1109       vbinding.put("Jalview", this);
1110       if (af != null)
1111       {
1112         vbinding.put("currentAlFrame", af);
1113       }
1114       Binding gbinding = new Binding(vbinding);
1115       GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1116       gse.run(sfile.toString(), gbinding);
1117       if ("STDIN".equals(groovyscript))
1118       {
1119         // delete temp file that we made -
1120         // only if it was successfully executed
1121         tfile.delete();
1122       }
1123     } catch (Exception e)
1124     {
1125       System.err.println("Exception Whilst trying to execute file " + sfile
1126               + " as a groovy script.");
1127       e.printStackTrace(System.err);
1128
1129     }
1130   }
1131
1132   public static boolean isHeadlessMode()
1133   {
1134     String isheadless = System.getProperty("java.awt.headless");
1135     if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1136     {
1137       return true;
1138     }
1139     return false;
1140   }
1141
1142   public AlignFrame[] getAlignFrames()
1143   {
1144     return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1145             : Desktop.getAlignFrames();
1146
1147   }
1148
1149   /**
1150    * Quit method delegates to Desktop.quit - unless running in headless mode
1151    * when it just ends the JVM
1152    */
1153   public void quit()
1154   {
1155     if (desktop != null)
1156     {
1157       desktop.quit();
1158     }
1159     else
1160     {
1161       System.exit(0);
1162     }
1163   }
1164
1165   public static AlignFrame getCurrentAlignFrame()
1166   {
1167     return Jalview.currentAlignFrame;
1168   }
1169
1170   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
1171   {
1172     Jalview.currentAlignFrame = currentAlignFrame;
1173   }
1174 }