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