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