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