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