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