JAL-3660 Fix for specified output format when using command line arg to convert a...
[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           FileFormatI outFormat = null;
731           try
732           {
733             outFormat = FileFormats.getInstance().forName(outputFormat);
734           } catch (Exception formatP)
735           {
736             System.out.println("Couldn't parse " + outFormat
737                     + " as a valid Jalview format string.");
738           }
739           if (outFormat != null)
740           {
741             if (!outFormat.isWritable())
742             {
743               System.out.println(
744                       "This version of Jalview does not support alignment export as "
745                               + outputFormat);
746             }
747             else
748             {
749               af.saveAlignment(file, outFormat);
750               if (af.isSaveAlignmentSuccessful())
751               {
752                 System.out.println("Written alignment in "
753                         + outFormat.getName() + " format to " + file);
754               }
755               else
756               {
757                 System.out.println("Error writing file " + file + " in "
758                         + outFormat.getName() + " format!!");
759               }
760             }
761           }
762
763         }
764
765         while (aparser.getSize() > 0)
766         {
767           System.out.println("Unknown arg: " + aparser.nextValue());
768         }
769       }
770     }
771     AlignFrame startUpAlframe = null;
772     // We'll only open the default file if the desktop is visible.
773     // And the user
774     // ////////////////////
775
776     if (!Platform.isJS() && !headless && file == null
777             && Cache.getDefault("SHOW_STARTUP_FILE", true))
778     /**
779      * Java only
780      * 
781      * @j2sIgnore
782      */
783     {
784       file = Cache.getDefault("STARTUP_FILE",
785               Cache.getDefault("www.jalview.org", "http://www.jalview.org")
786                       + "/examples/exampleFile_2_7.jar");
787       if (file.equals(
788               "http://www.jalview.org/examples/exampleFile_2_3.jar"))
789       {
790         // hardwire upgrade of the startup file
791         file.replace("_2_3.jar", "_2_7.jar");
792         // and remove the stale setting
793         Cache.removeProperty("STARTUP_FILE");
794       }
795
796       protocol = AppletFormatAdapter.checkProtocol(file);
797
798       if (file.endsWith(".jar"))
799       {
800         format = FileFormat.Jalview;
801       }
802       else
803       {
804         try
805         {
806           format = new IdentifyFile().identify(file, protocol);
807         } catch (FileFormatException e)
808         {
809           // TODO what?
810         }
811       }
812
813       startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
814               format);
815       // extract groovy arguments before anything else.
816     }
817
818     // Once all other stuff is done, execute any groovy scripts (in order)
819     if (groovyscript != null)
820     {
821       if (Cache.groovyJarsPresent())
822       {
823         System.out.println("Executing script " + groovyscript);
824         executeGroovyScript(groovyscript, startUpAlframe);
825       }
826       else
827       {
828         System.err.println(
829                 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
830                         + groovyscript);
831       }
832     }
833     // and finally, turn off batch mode indicator - if the desktop still exists
834     if (desktop != null)
835     {
836       if (progress != -1)
837       {
838         desktop.setProgressBar(null, progress);
839       }
840       desktop.setInBatchMode(false);
841     }
842   }
843
844   private static void setLookAndFeel()
845   {
846     // property laf = "crossplatform", "system", "gtk", "metal", "nimbus" or
847     // "mac"
848     // If not set (or chosen laf fails), use the normal SystemLaF and if on Mac,
849     // try Quaqua/Vaqua.
850     String lafProp = System.getProperty("laf");
851     String lafSetting = Cache.getDefault("PREFERRED_LAF", null);
852     String laf = "none";
853     if (lafProp != null)
854     {
855       laf = lafProp;
856     }
857     else if (lafSetting != null)
858     {
859       laf = lafSetting;
860     }
861     boolean lafSet = false;
862     switch (laf)
863     {
864     case "crossplatform":
865       lafSet = setCrossPlatformLookAndFeel();
866       if (!lafSet)
867       {
868         Cache.log.error("Could not set requested laf=" + laf);
869       }
870       break;
871     case "system":
872       lafSet = setSystemLookAndFeel();
873       if (!lafSet)
874       {
875         Cache.log.error("Could not set requested laf=" + laf);
876       }
877       break;
878     case "gtk":
879       lafSet = setGtkLookAndFeel();
880       if (!lafSet)
881       {
882         Cache.log.error("Could not set requested laf=" + laf);
883       }
884       break;
885     case "metal":
886       lafSet = setMetalLookAndFeel();
887       if (!lafSet)
888       {
889         Cache.log.error("Could not set requested laf=" + laf);
890       }
891       break;
892     case "nimbus":
893       lafSet = setNimbusLookAndFeel();
894       if (!lafSet)
895       {
896         Cache.log.error("Could not set requested laf=" + laf);
897       }
898       break;
899     case "quaqua":
900       lafSet = setQuaquaLookAndFeel();
901       if (!lafSet)
902       {
903         Cache.log.error("Could not set requested laf=" + laf);
904       }
905       break;
906     case "vaqua":
907       lafSet = setVaquaLookAndFeel();
908       if (!lafSet)
909       {
910         Cache.log.error("Could not set requested laf=" + laf);
911       }
912       break;
913     case "mac":
914       lafSet = setMacLookAndFeel();
915       if (!lafSet)
916       {
917         Cache.log.error("Could not set requested laf=" + laf);
918       }
919       break;
920     case "none":
921       break;
922     default:
923       Cache.log.error("Requested laf=" + laf + " not implemented");
924     }
925     if (!lafSet)
926     {
927       setSystemLookAndFeel();
928       if (Platform.isLinux())
929       {
930         setMetalLookAndFeel();
931       }
932       if (Platform.isMac())
933       {
934         setMacLookAndFeel();
935       }
936     }
937   }
938
939   private static boolean setCrossPlatformLookAndFeel()
940   {
941     boolean set = false;
942     try
943     {
944       UIManager.setLookAndFeel(
945               UIManager.getCrossPlatformLookAndFeelClassName());
946       set = true;
947     } catch (Exception ex)
948     {
949       Cache.log.error("Unexpected Look and Feel Exception");
950       Cache.log.error(ex.getMessage());
951       Cache.log.debug(Cache.getStackTraceString(ex));
952     }
953     return set;
954   }
955
956   private static boolean setSystemLookAndFeel()
957   {
958     boolean set = false;
959     try
960     {
961       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
962       set = true;
963     } catch (Exception ex)
964     {
965       Cache.log.error("Unexpected Look and Feel Exception");
966       Cache.log.error(ex.getMessage());
967       Cache.log.debug(Cache.getStackTraceString(ex));
968     }
969     return set;
970   }
971
972   private static boolean setSpecificLookAndFeel(String name,
973           String className, boolean nameStartsWith)
974   {
975     boolean set = false;
976     try
977     {
978       for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
979       {
980         if (info.getName() != null && nameStartsWith
981                 ? info.getName().toLowerCase()
982                         .startsWith(name.toLowerCase())
983                 : info.getName().toLowerCase().equals(name.toLowerCase()))
984         {
985           className = info.getClassName();
986           break;
987         }
988       }
989       UIManager.setLookAndFeel(className);
990       set = true;
991     } catch (Exception ex)
992     {
993       Cache.log.error("Unexpected Look and Feel Exception");
994       Cache.log.error(ex.getMessage());
995       Cache.log.debug(Cache.getStackTraceString(ex));
996     }
997     return set;
998   }
999
1000   private static boolean setGtkLookAndFeel()
1001   {
1002     return setSpecificLookAndFeel("gtk",
1003             "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true);
1004   }
1005
1006   private static boolean setMetalLookAndFeel()
1007   {
1008     return setSpecificLookAndFeel("metal",
1009             "javax.swing.plaf.metal.MetalLookAndFeel", false);
1010   }
1011
1012   private static boolean setNimbusLookAndFeel()
1013   {
1014     return setSpecificLookAndFeel("nimbus",
1015             "javax.swing.plaf.nimbus.NimbusLookAndFeel", false);
1016   }
1017
1018   private static boolean setQuaquaLookAndFeel()
1019   {
1020     return setSpecificLookAndFeel("quaqua",
1021             ch.randelshofer.quaqua.QuaquaManager.getLookAndFeel().getClass()
1022                     .getName(),
1023             false);
1024   }
1025
1026   private static boolean setVaquaLookAndFeel()
1027   {
1028     return setSpecificLookAndFeel("vaqua",
1029             "org.violetlib.aqua.AquaLookAndFeel", false);
1030   }
1031
1032   private static boolean setMacLookAndFeel()
1033   {
1034     boolean set = false;
1035     System.setProperty("com.apple.mrj.application.apple.menu.about.name",
1036             "Jalview");
1037     System.setProperty("apple.laf.useScreenMenuBar", "true");
1038     set = setQuaquaLookAndFeel();
1039     if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
1040             .toLowerCase().contains("quaqua"))
1041     {
1042       set = setVaquaLookAndFeel();
1043     }
1044     return set;
1045   }
1046
1047   private static void showUsage()
1048   {
1049     System.out.println(
1050             "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
1051                     + "-nodisplay\tRun Jalview without User Interface.\n"
1052                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
1053                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
1054                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
1055                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
1056                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
1057                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
1058                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
1059                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
1060                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
1061                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
1062                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
1063                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
1064                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
1065                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
1066                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
1067                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
1068                     + "-html FILE\tCreate HTML file from alignment.\n"
1069                     + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
1070                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
1071                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
1072                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
1073                     + "-noquestionnaire\tTurn off questionnaire check.\n"
1074                     + "-nonews\tTurn off check for Jalview news.\n"
1075                     + "-nousagestats\tTurn off google analytics tracking for this session.\n"
1076                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
1077                     // +
1078                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
1079                     // after all other properties files have been read\n\t
1080                     // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
1081                     // passed in correctly)"
1082                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
1083                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
1084                     + "-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"
1085                     + "\n~Read documentation in Application or visit http://www.jalview.org for description of Features and Annotations file~\n\n");
1086   }
1087
1088   private static void startUsageStats(final Desktop desktop)
1089   {
1090     /**
1091      * start a User Config prompt asking if we can log usage statistics.
1092      */
1093     PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
1094             "USAGESTATS", "Jalview Usage Statistics",
1095             "Do you want to help make Jalview better by enabling "
1096                     + "the collection of usage statistics with Google Analytics ?"
1097                     + "\n\n(you can enable or disable usage tracking in the preferences)",
1098             new Runnable()
1099             {
1100               @Override
1101               public void run()
1102               {
1103                 Cache.log.debug(
1104                         "Initialising googletracker for usage stats.");
1105                 Cache.initGoogleTracker();
1106                 Cache.log.debug("Tracking enabled.");
1107               }
1108             }, new Runnable()
1109             {
1110               @Override
1111               public void run()
1112               {
1113                 Cache.log.debug("Not enabling Google Tracking.");
1114               }
1115             }, null, true);
1116     desktop.addDialogThread(prompter);
1117   }
1118
1119   /**
1120    * Locate the given string as a file and pass it to the groovy interpreter.
1121    * 
1122    * @param groovyscript
1123    *          the script to execute
1124    * @param jalviewContext
1125    *          the Jalview Desktop object passed in to the groovy binding as the
1126    *          'Jalview' object.
1127    */
1128   private void executeGroovyScript(String groovyscript, AlignFrame af)
1129   {
1130     /**
1131      * for scripts contained in files
1132      */
1133     File tfile = null;
1134     /**
1135      * script's URI
1136      */
1137     URL sfile = null;
1138     if (groovyscript.trim().equals("STDIN"))
1139     {
1140       // read from stdin into a tempfile and execute it
1141       try
1142       {
1143         tfile = File.createTempFile("jalview", "groovy");
1144         PrintWriter outfile = new PrintWriter(
1145                 new OutputStreamWriter(new FileOutputStream(tfile)));
1146         BufferedReader br = new BufferedReader(
1147                 new InputStreamReader(System.in));
1148         String line = null;
1149         while ((line = br.readLine()) != null)
1150         {
1151           outfile.write(line + "\n");
1152         }
1153         br.close();
1154         outfile.flush();
1155         outfile.close();
1156
1157       } catch (Exception ex)
1158       {
1159         System.err.println("Failed to read from STDIN into tempfile "
1160                 + ((tfile == null) ? "(tempfile wasn't created)"
1161                         : tfile.toString()));
1162         ex.printStackTrace();
1163         return;
1164       }
1165       try
1166       {
1167         sfile = tfile.toURI().toURL();
1168       } catch (Exception x)
1169       {
1170         System.err.println(
1171                 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1172                         + tfile.toURI());
1173         x.printStackTrace();
1174         return;
1175       }
1176     }
1177     else
1178     {
1179       try
1180       {
1181         sfile = new URI(groovyscript).toURL();
1182       } catch (Exception x)
1183       {
1184         tfile = new File(groovyscript);
1185         if (!tfile.exists())
1186         {
1187           System.err.println("File '" + groovyscript + "' does not exist.");
1188           return;
1189         }
1190         if (!tfile.canRead())
1191         {
1192           System.err.println("File '" + groovyscript + "' cannot be read.");
1193           return;
1194         }
1195         if (tfile.length() < 1)
1196         {
1197           System.err.println("File '" + groovyscript + "' is empty.");
1198           return;
1199         }
1200         try
1201         {
1202           sfile = tfile.getAbsoluteFile().toURI().toURL();
1203         } catch (Exception ex)
1204         {
1205           System.err.println("Failed to create a file URL for "
1206                   + tfile.getAbsoluteFile());
1207           return;
1208         }
1209       }
1210     }
1211     try
1212     {
1213       Map<String, java.lang.Object> vbinding = new HashMap<>();
1214       vbinding.put("Jalview", this);
1215       if (af != null)
1216       {
1217         vbinding.put("currentAlFrame", af);
1218       }
1219       Binding gbinding = new Binding(vbinding);
1220       GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1221       gse.run(sfile.toString(), gbinding);
1222       if ("STDIN".equals(groovyscript))
1223       {
1224         // delete temp file that we made -
1225         // only if it was successfully executed
1226         tfile.delete();
1227       }
1228     } catch (Exception e)
1229     {
1230       System.err.println("Exception Whilst trying to execute file " + sfile
1231               + " as a groovy script.");
1232       e.printStackTrace(System.err);
1233
1234     }
1235   }
1236
1237   public static boolean isHeadlessMode()
1238   {
1239     String isheadless = System.getProperty("java.awt.headless");
1240     if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1241     {
1242       return true;
1243     }
1244     return false;
1245   }
1246
1247   public AlignFrame[] getAlignFrames()
1248   {
1249     return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1250             : Desktop.getAlignFrames();
1251
1252   }
1253
1254   /**
1255    * Quit method delegates to Desktop.quit - unless running in headless mode
1256    * when it just ends the JVM
1257    */
1258   public void quit()
1259   {
1260     if (desktop != null)
1261     {
1262       desktop.quit();
1263     }
1264     else
1265     {
1266       System.exit(0);
1267     }
1268   }
1269
1270   public static AlignFrame getCurrentAlignFrame()
1271   {
1272     return Jalview.currentAlignFrame;
1273   }
1274
1275   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
1276   {
1277     Jalview.currentAlignFrame = currentAlignFrame;
1278   }
1279 }