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