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