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