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