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