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