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