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