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