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