b1499118349ab72136665997bc1150c6ef8f2d89
[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.AlignCalcWorkerI;
24 import jalview.api.AlignFrameI;
25 import jalview.api.AlignViewportI;
26 import jalview.api.JalviewApp;
27 import jalview.api.StructureSelectionManagerProvider;
28 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
29 import jalview.datamodel.ColumnSelection;
30 import jalview.datamodel.HiddenColumns;
31 import jalview.datamodel.PDBEntry;
32 import jalview.datamodel.SequenceGroup;
33 import jalview.datamodel.SequenceI;
34 import jalview.ext.so.SequenceOntology;
35 import jalview.gui.AlignFrame;
36 import jalview.gui.AlignViewport;
37 import jalview.gui.AlignmentPanel;
38 import jalview.gui.CalculationChooser;
39 import jalview.gui.Desktop;
40 import jalview.gui.Preferences;
41 import jalview.gui.PromptUserConfig;
42 import jalview.gui.StructureViewer;
43 import jalview.io.AppletFormatAdapter;
44 import jalview.io.BioJsHTMLOutput;
45 import jalview.io.DataSourceType;
46 import jalview.io.FileFormat;
47 import jalview.io.FileFormatException;
48 import jalview.io.FileFormatI;
49 import jalview.io.FileFormats;
50 import jalview.io.FileLoader;
51 import jalview.io.HtmlSvgOutput;
52 import jalview.io.IdentifyFile;
53 import jalview.io.NewickFile;
54 import jalview.io.gff.SequenceOntologyFactory;
55 import jalview.javascript.JSFunctionExec;
56 import jalview.javascript.MouseOverStructureListener;
57 import jalview.renderer.seqfeatures.FeatureRenderer;
58 import jalview.schemes.ColourSchemeI;
59 import jalview.schemes.ColourSchemeProperty;
60 import jalview.structure.SelectionSource;
61 import jalview.structure.VamsasSource;
62 import jalview.util.MessageManager;
63 import jalview.util.Platform;
64 import jalview.ws.jws2.Jws2Discoverer;
65
66 import java.applet.AppletContext;
67 import java.io.BufferedReader;
68 import java.io.File;
69 import java.io.FileOutputStream;
70 import java.io.IOException;
71 import java.io.InputStreamReader;
72 import java.io.OutputStreamWriter;
73 import java.io.PrintWriter;
74 import java.net.MalformedURLException;
75 import java.net.URI;
76 import java.net.URISyntaxException;
77 import java.net.URL;
78 import java.security.AllPermission;
79 import java.security.CodeSource;
80 import java.security.PermissionCollection;
81 import java.security.Permissions;
82 import java.security.Policy;
83 import java.util.HashMap;
84 import java.util.Hashtable;
85 import java.util.Map;
86 import java.util.Vector;
87
88 import javax.swing.LookAndFeel;
89 import javax.swing.UIManager;
90
91 import groovy.lang.Binding;
92 import groovy.util.GroovyScriptEngine;
93 import netscape.javascript.JSObject;
94
95 /**
96  * Main class for Jalview Application <br>
97  * <br>
98  * start with: java -classpath "$PATH_TO_LIB$/*:$PATH_TO_CLASSES$" \
99  * jalview.bin.Jalview
100  * 
101  * or on Windows: java -classpath "$PATH_TO_LIB$/*;$PATH_TO_CLASSES$" \
102  * jalview.bin.Jalview jalview.bin.Jalview
103  * 
104  * (ensure -classpath arg is quoted to avoid shell expansion of '*' and do not
105  * embellish '*' to e.g. '*.jar')
106  * 
107  * @author $author$
108  * @version $Revision$
109  */
110 public class Jalview implements ApplicationSingletonI, JalviewJSApi
111 {
112
113   public static Jalview getInstance()
114   {
115     return (Jalview) ApplicationSingletonProvider
116             .getInstance(Jalview.class);
117   }
118
119   private Jalview()
120   {
121   }
122
123   static
124   {
125     Platform.getURLCommandArguments();
126   }
127
128   private boolean headless;
129
130   public static boolean isHeadlessMode()
131   {
132     return getInstance().headless;
133   }
134
135   private Desktop desktop;
136
137   private AlignFrame currentAlignFrame;
138
139   public boolean isJavaAppletTag;
140
141   public String appletResourcePath;
142
143   JalviewAppLoader appLoader;
144
145   protected JSFunctionExec jsFunctionExec;
146
147   private boolean noCalculation, noMenuBar, noStatus;
148
149   private boolean noAnnotation;
150
151   public boolean getStartCalculations()
152   {
153     return !noCalculation;
154   }
155
156   public boolean getAllowMenuBar()
157   {
158     return !noMenuBar;
159   }
160
161   public boolean getShowStatus()
162   {
163     return !noStatus;
164   }
165
166   public boolean getShowAnnotation()
167   {
168     return !noAnnotation;
169   }
170
171   public static AlignFrame getCurrentAlignFrame()
172   {
173     return getInstance().currentAlignFrame;
174   }
175
176   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
177   {
178     getInstance().currentAlignFrame = currentAlignFrame;
179   }
180
181   static
182   {
183     if (!Platform.isJS())
184     /**
185      * Java only
186      * 
187      * @j2sIgnore
188      */
189     {
190       // grab all the rights we can for the JVM
191       Policy.setPolicy(new Policy()
192       {
193         @Override
194         public PermissionCollection getPermissions(CodeSource codesource)
195         {
196           Permissions perms = new Permissions();
197           perms.add(new AllPermission());
198           return (perms);
199         }
200
201         @Override
202         public void refresh()
203         {
204         }
205       });
206     }
207   }
208
209   /**
210    * keep track of feature fetching tasks.
211    * 
212    * @author JimP
213    * 
214    */
215   class FeatureFetcher
216   {
217     /*
218      * TODO: generalise to track all jalview events to orchestrate batch
219      * processing events.
220      */
221
222     private int queued = 0;
223
224     private int running = 0;
225
226     public FeatureFetcher()
227     {
228
229     }
230
231     public void addFetcher(final AlignFrame af,
232             final Vector<String> dasSources)
233     {
234       final long id = System.currentTimeMillis();
235       queued++;
236       final FeatureFetcher us = this;
237       new Thread(new Runnable()
238       {
239
240         @Override
241         public void run()
242         {
243           synchronized (us)
244           {
245             queued--;
246             running++;
247           }
248
249           af.setProgressBar(MessageManager
250                   .getString("status.das_features_being_retrived"), id);
251           af.featureSettings_actionPerformed(null);
252           af.setProgressBar(null, id);
253           synchronized (us)
254           {
255             running--;
256           }
257         }
258       }).start();
259     }
260
261     public synchronized boolean allFinished()
262     {
263       return queued == 0 && running == 0;
264     }
265
266   }
267
268   /**
269    * main class for Jalview application
270    * 
271    * @param args
272    *          open <em>filename</em>
273    */
274   public static void main(String[] args)
275   {
276     // Platform.startJavaLogging();
277     getInstance().doMain(args);
278   }
279
280
281   @SuppressWarnings("unused")
282   /**
283    * @param args
284    */
285   void doMain(String[] args)
286   {
287
288     boolean isJS = Platform.isJS();
289     if (isJS)
290     {
291       Platform.setAppClass(this);
292     }
293     else
294     {
295       System.setSecurityManager(null);
296     }
297
298     System.out
299             .println("Java version: " + System.getProperty("java.version"));
300     System.out.println(System.getProperty("os.arch") + " "
301             + System.getProperty("os.name") + " "
302             + System.getProperty("os.version"));
303
304     ArgsParser aparser = new ArgsParser(args);
305
306     String usrPropsFile = aparser.getValue(ArgsParser.PROPS);
307     Cache.loadProperties(usrPropsFile);
308
309     if (aparser.contains(ArgsParser.NODISPLAY)
310             || aparser.contains(ArgsParser.NOGUI)
311             || aparser.contains(ArgsParser.HEADLESS)
312             || "true".equals(System.getProperty("java.awt.headless")))
313     {
314       headless = true;
315     }
316
317     if (isJS)
318     {
319       isJavaAppletTag = aparser.isApplet();
320       if (isJavaAppletTag)
321       {
322         Preferences.setAppletDefaults();
323         Cache.loadProperties(usrPropsFile); // again, because we
324         // might be changing defaults here?
325       }
326       System.out.println(
327               "<Applet> found: " + aparser.getValue("Info.j2sAppletID"));
328       appletResourcePath = aparser.getValue("Info.resourcePath");
329     }
330     else
331     /**
332      * Java only
333      * 
334      * @j2sIgnore
335      */
336     {
337       if (usrPropsFile != null)
338       {
339         System.out.println(
340                 "CMD [-props " + usrPropsFile + "] executed successfully!");
341       }
342
343       if (aparser.contains("help") || aparser.contains("h"))
344       {
345         showUsage();
346         System.exit(0);
347       }
348
349
350       // anything else!
351
352       final String jabawsUrl = aparser.getValue(ArgsParser.JABAWS);
353       if (jabawsUrl != null)
354       {
355         try
356         {
357           Jws2Discoverer.getInstance().setPreferredUrl(jabawsUrl);
358           System.out.println(
359                   "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
360         } catch (MalformedURLException e)
361         {
362           System.err.println(
363                   "Invalid jabaws parameter: " + jabawsUrl + " ignored");
364         }
365       }
366
367     }
368     // check for property setting
369     String defs = aparser.getValue(ArgsParser.SETPROP);
370     while (defs != null)
371     {
372       int p = defs.indexOf('=');
373       if (p == -1)
374       {
375         System.err.println("Ignoring invalid setprop argument : " + defs);
376       }
377       else
378       {
379         System.out.println("Executing setprop argument: " + defs);
380         if (isJS)
381         {
382           Cache.setProperty(defs.substring(0, p), defs.substring(p + 1));
383         }
384       }
385       defs = aparser.getValue(ArgsParser.SETPROP);
386     }
387     System.setProperty("http.agent",
388             "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown"));
389     try
390     {
391       Cache.initLogger();
392     } catch (NoClassDefFoundError error)
393     {
394       error.printStackTrace();
395       System.out.println("\nEssential logging libraries not found."
396               + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview");
397       System.exit(0);
398     }
399
400     desktop = null;
401
402     try
403     {
404       if (!isJS && Platform.isWin())
405        {
406         UIManager.setLookAndFeel(
407                 headless ? "javax.swing.plaf.metal.MetalLookAndFeel"
408                         : UIManager.getSystemLookAndFeelClassName());
409 //      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
410       }
411     } catch (Exception ex)
412     {
413       System.err.println("Unexpected Look and Feel Exception");
414       ex.printStackTrace();
415     }
416     if (Platform.isAMacAndNotJS())
417     {
418
419       LookAndFeel lookAndFeel = ch.randelshofer.quaqua.QuaquaManager
420               .getLookAndFeel();
421       System.setProperty("com.apple.mrj.application.apple.menu.about.name",
422               "Jalview");
423       System.setProperty("apple.laf.useScreenMenuBar", "true");
424       if (lookAndFeel != null)
425       {
426         try
427         {
428           UIManager.setLookAndFeel(lookAndFeel);
429         } catch (Throwable e)
430         {
431           System.err.println(
432                   "Failed to set QuaQua look and feel: " + e.toString());
433         }
434       }
435       if (lookAndFeel == null
436               || !(lookAndFeel.getClass().isAssignableFrom(
437                       UIManager.getLookAndFeel().getClass()))
438               || !UIManager.getLookAndFeel().getClass().toString()
439                       .toLowerCase().contains("quaqua"))
440       {
441         try
442         {
443           System.err.println(
444                   "Quaqua LaF not available on this plaform. Using VAqua(4).\nSee https://issues.jalview.org/browse/JAL-2976");
445           UIManager.setLookAndFeel("org.violetlib.aqua.AquaLookAndFeel");
446         } catch (Throwable e)
447         {
448           System.err.println(
449                   "Failed to reset look and feel: " + e.toString());
450         }
451       }
452     }
453
454     /*
455      * configure 'full' SO model if preferences say to, 
456      * else use the default (SO Lite)
457      */
458     if (Cache.getDefault(Preferences.USE_FULL_SO, false))
459     {
460       SequenceOntologyFactory.setSequenceOntology(new SequenceOntology());
461     }
462
463     if (!headless)
464     {
465       desktop = Desktop.getInstance();
466       desktop.setInBatchMode(true); // indicate we are starting up
467       desktop.setVisible(true);
468
469       if (!isJS)
470       /**
471        * Java only
472        * 
473        * @j2sIgnore
474        */
475       {
476         desktop.startServiceDiscovery();
477         if (!aparser.contains(ArgsParser.NOUSAGESTATS))
478         {
479           startUsageStats(desktop);
480         }
481         else
482         {
483           System.err.println("CMD [-nousagestats] executed successfully!");
484         }
485
486         if (!aparser.contains(ArgsParser.NOQUESTIONNAIRE))
487         {
488           String url = aparser.getValue(ArgsParser.QUESTIONNAIRE);
489           if (url != null)
490           {
491             // Start the desktop questionnaire prompter with the specified
492             // questionnaire
493             Cache.log.debug("Starting questionnaire url at " + url);
494             desktop.checkForQuestionnaire(url);
495             System.out.println("CMD questionnaire[-" + url
496                     + "] executed successfully!");
497           }
498           else
499           {
500             if (Cache.getProperty(Preferences.NOQUESTIONNAIRES) == null)
501             {
502               // Start the desktop questionnaire prompter with the specified
503               // questionnaire
504               // String defurl =
505               // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
506               // //
507               String defurl = "http://www.jalview.org/cgi-bin/questionnaire.pl";
508               Cache.log.debug(
509                       "Starting questionnaire with default url: " + defurl);
510               desktop.checkForQuestionnaire(defurl);
511             }
512           }
513         }
514         else
515         {
516           System.err
517                   .println("CMD [-noquestionnaire] executed successfully!");
518         }
519
520         if (!aparser.contains(ArgsParser.NONEWS))
521         {
522           desktop.checkForNews();
523         }
524
525         BioJsHTMLOutput.updateBioJS();
526       }
527     }
528
529     parseArguments(aparser, true);
530   }
531
532   /**
533    * Allow an outside entity to initiate the second half of argument parsing
534    * (only).
535    * 
536    * @param args
537    * @return null is good
538    */
539   @Override
540   public Object parseArguments(String[] args)
541   {
542
543     try
544     {
545       ArgsParser aparser = new ArgsParser(args);
546       return parseArguments(aparser, false);
547     } catch (Throwable t)
548     {
549       return t;
550     }
551   }
552
553   /**
554    * 
555    * @param aparser
556    * @param isStartup
557    * @return
558    */
559   private Object parseArguments(ArgsParser aparser, boolean isStartup)
560   {
561     boolean isJS = Platform.isJS();
562
563     Desktop desktop = (headless ? null : Desktop.getInstance());
564     // script to execute after all loading is
565     // completed one way or another
566     // extract groovy argument and execute if necessary
567     String groovyscript = (isJS ? null
568             : aparser.getValue(ArgsParser.GROOVY, true));
569     String file = aparser.getValue(ArgsParser.OPEN, true);
570     // BH this here to allow split frame; not working as of 5/17/2019
571     String file2 = aparser.getValue(ArgsParser.OPEN2, true);
572     String fileFormat = (isJavaAppletTag
573             ? aparser.getAppletValue("format", null)
574             : null);
575     FileFormatI format = null;
576     DataSourceType protocol = null;
577
578     if (file == null && desktop == null)
579     {
580       System.out.println("No files to open!");
581       System.exit(1);
582     }
583     boolean haveImport = checkStartVamas(aparser);
584     // Finally, deal with the remaining input data.
585     long progress = -1;
586     if (file == null && isJavaAppletTag)
587     {
588       // Maybe the sequences are added as parameters
589       StringBuffer data = new StringBuffer("PASTE");
590       int i = 1;
591       while ((file = aparser.getAppletValue("sequence" + i, null)) != null)
592       {
593         data.append(file.toString() + "\n");
594         i++;
595       }
596       if (data.length() > 5)
597       {
598         file = data.toString();
599       }
600     }
601
602     String data;
603
604     if (file != null)
605     {
606
607       if (!headless)
608       {
609         desktop.setProgressBar(
610                 MessageManager
611                         .getString("status.processing_commandline_args"),
612                 progress = System.currentTimeMillis());
613       }
614
615       if (!isJS)
616       /**
617        * ignore in JavaScript -- can't just check file existence - could load
618        * it?
619        * 
620        * @j2sIgnore
621        */
622       {
623         if (!file.startsWith("http://") && !file.startsWith("https://"))
624         // BH 2019 added https check for Java
625         {
626           if (!(new File(file)).exists())
627           {
628             System.out.println("Can't find " + file);
629             if (headless)
630             {
631               System.exit(1);
632             }
633           }
634         }
635       }
636
637       protocol = AppletFormatAdapter.checkProtocol(file);
638
639       try
640       {
641         format = (isJavaAppletTag && fileFormat != null
642                 ? FileFormats.getInstance().forName(fileFormat)
643                 : null);
644         if (format == null)
645         {
646           format = new IdentifyFile().identify(file, protocol);
647         }
648       } catch (FileFormatException e1)
649       {
650         // TODO ?
651       }
652
653       if (aparser.contains(ArgsParser.NOMENUBAR))
654       {
655         noMenuBar = true;
656         System.out.println("CMD [nomenu] executed successfully!");
657       }
658
659       if (aparser.contains(ArgsParser.NOSTATUS))
660       {
661         noStatus = true;
662         System.out.println("CMD [nostatus] executed successfully!");
663       }
664
665       if (aparser.contains(ArgsParser.NOANNOTATION)
666               || aparser.contains(ArgsParser.NOANNOTATION2))
667       {
668         noAnnotation = true;
669         System.out.println("CMD no-annotation executed successfully!");
670       }
671       if (aparser.contains(ArgsParser.NOCALCULATION))
672       {
673         noCalculation = true;
674         System.out.println("CMD [nocalculation] executed successfully!");
675       }
676
677       AlignFrame af = new FileLoader(!headless).loadFileWaitTillLoaded(file,
678               protocol, format);
679       if (af == null)
680       {
681         System.out.println("error");
682       }
683       else
684       {
685         System.out
686                 .println("CMD [-open " + file + "] executed successfully!");
687         if (file2 != null)
688         {
689           protocol = AppletFormatAdapter.checkProtocol(file2);
690           try
691           {
692             format = new IdentifyFile().identify(file2, protocol);
693           } catch (FileFormatException e1)
694           {
695             // TODO ?
696           }
697           AlignFrame af2 = new FileLoader(!headless)
698                   .loadFileWaitTillLoaded(file2, protocol, format);
699           if (af2 == null)
700           {
701             System.out.println("error");
702           }
703           else
704           {
705             AlignViewport.openLinkedAlignmentAs(af,
706                     af.getViewport().getAlignment(),
707                     af2.getViewport().getAlignment(), "",
708                     AlignViewport.SPLIT_FRAME);
709             System.out.println(
710                     "CMD [-open2 " + file2 + "] executed successfully!");
711           }
712         }
713
714         setCurrentAlignFrame(af);
715
716         // TODO: file2 How to implement file2 for the applet spit screen?
717
718         data = aparser.getValue(ArgsParser.COLOUR, true);
719         if (data != null)
720         {
721           data.replaceAll("%20", " ");
722
723           ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
724                   af.getViewport(), af.getViewport().getAlignment(), data);
725
726           if (cs != null)
727           {
728             System.out.println(
729                     "CMD [-color " + data + "] executed successfully!");
730           }
731           af.changeColour(cs);
732         }
733
734         // Must maintain ability to use the groups flag
735         data = aparser.getValue(ArgsParser.GROUPS, true);
736         if (data != null)
737         {
738           af.parseFeaturesFile(data,
739                   AppletFormatAdapter.checkProtocol(data));
740           // System.out.println("Added " + data);
741           System.out.println(
742                   "CMD groups[-" + data + "]  executed successfully!");
743         }
744         data = aparser.getValue(ArgsParser.FEATURES, true);
745         if (data != null)
746         {
747           af.parseFeaturesFile(data,
748                   AppletFormatAdapter.checkProtocol(data));
749           // System.out.println("Added " + data);
750           System.out.println(
751                   "CMD [-features " + data + "]  executed successfully!");
752         }
753
754         data = aparser.getValue(ArgsParser.ANNOTATIONS, true);
755         if (data != null)
756         {
757           af.loadJalviewDataFile(data, null, null, null);
758           // System.out.println("Added " + data);
759           System.out.println(
760                   "CMD [-annotations " + data + "] executed successfully!");
761         }
762
763         if (aparser.contains(ArgsParser.SHOWOVERVIEW))
764         {
765           af.overviewMenuItem_actionPerformed(null);
766           System.out.println("CMD [showoverview] executed successfully!");
767         }
768
769         // set or clear the sortbytree flag.
770         if (aparser.contains(ArgsParser.SORTBYTREE))
771         {
772           af.getViewport().setSortByTree(true);
773           if (af.getViewport().getSortByTree())
774           {
775             System.out.println("CMD [-sortbytree] executed successfully!");
776           }
777         }
778
779         boolean doUpdateAnnotation = false;
780
781         /**
782          * we do this earlier in JalviewJS because of a complication with
783          * SHOWOVERVIEW
784          * 
785          * For now, just fixing this in JalviewJS.
786          *
787          * 
788          * @j2sIgnore
789          * 
790          */
791         {
792           if (aparser.contains(ArgsParser.NOANNOTATION)
793                   || aparser.contains(ArgsParser.NOANNOTATION2))
794           {
795             af.getViewport().setShowAnnotation(false);
796             if (!af.getViewport().isShowAnnotation())
797             {
798               doUpdateAnnotation = true;
799               System.out
800                       .println("CMD no-annotation executed successfully!");
801             }
802           }
803         }
804         if (aparser.contains(ArgsParser.NOSORTBYTREE))
805         {
806           af.getViewport().setSortByTree(false);
807           if (!af.getViewport().getSortByTree())
808           {
809             doUpdateAnnotation = true;
810             System.out
811                     .println("CMD [-nosortbytree] executed successfully!");
812           }
813         }
814         if (doUpdateAnnotation)
815         { // BH 2019.07.24
816           af.setMenusForViewport();
817           af.alignPanel.updateLayout();
818         }
819         data = aparser.getValue(ArgsParser.TREE, true);
820         if (data != null)
821         {
822           try
823           {
824             System.out.println(
825                     "CMD [-tree " + data + "] executed successfully!");
826             NewickFile nf = new NewickFile(data,
827                     AppletFormatAdapter.checkProtocol(data));
828             af.getViewport()
829                     .setCurrentTree(af.showNewickTree(nf, data).getTree());
830           } catch (IOException ex)
831           {
832             System.err.println("Couldn't add tree " + data);
833             ex.printStackTrace(System.err);
834           }
835         }
836         // TODO - load PDB structure(s) to alignment JAL-629
837         // (associate with identical sequence in alignment, or a specified
838         // sequence)
839         if (isJavaAppletTag)
840         {
841           loadAppletParams(aparser, af);
842         }
843         else if (!isJS)
844         /**
845          * Java only
846          * 
847          * @j2sIgnore
848          */
849         {
850           if (groovyscript != null)
851           {
852             // Execute the groovy script after we've done all the rendering
853             // stuff
854             // and before any images or figures are generated.
855             System.out.println("Executing script " + groovyscript);
856             executeGroovyScript(groovyscript, af);
857             System.out.println("CMD groovy[" + groovyscript
858                     + "] executed successfully!");
859             groovyscript = null;
860           }
861           while (aparser.getSize() > 0)
862           {
863             System.out.println("Unknown arg: " + aparser.nextValue());
864           }
865         }
866         checkOutputFile(aparser, af, format);
867       }
868     }
869     AlignFrame startUpAlframe = null;
870     // We'll only open the default file if the desktop is visible.
871     // And the user
872     // ////////////////////
873
874     if (!isJS && !headless && file == null && !haveImport
875             && jalview.bin.Cache.getDefault("SHOW_STARTUP_FILE", true))
876     /**
877      * Java only
878      * 
879      * @j2sIgnore
880      */
881     {
882       file = jalview.bin.Cache.getDefault("STARTUP_FILE",
883               jalview.bin.Cache.getDefault("www.jalview.org",
884                       "http://www.jalview.org")
885                       + "/examples/exampleFile_2_7.jar");
886       if (file.equals(
887               "http://www.jalview.org/examples/exampleFile_2_3.jar"))
888       {
889         // hardwire upgrade of the startup file
890         file.replace("_2_3.jar", "_2_7.jar");
891         // and remove the stale setting
892         jalview.bin.Cache.removeProperty("STARTUP_FILE");
893       }
894
895       protocol = DataSourceType.FILE;
896
897       if (file.indexOf("http:") > -1)
898       {
899         protocol = DataSourceType.URL;
900       }
901
902       if (file.endsWith(".jar"))
903       {
904         format = FileFormat.Jalview;
905       }
906       else
907       {
908         try
909         {
910           format = new IdentifyFile().identify(file, protocol);
911         } catch (FileFormatException e)
912         {
913           // TODO what?
914         }
915       }
916
917       startUpAlframe = new FileLoader(!headless)
918               .loadFileWaitTillLoaded(file, protocol, format);
919       // extract groovy arguments before anything else.
920     }
921
922     // Once all other stuff is done, execute any groovy scripts (in order)
923     if (groovyscript != null)
924     {
925       if (Cache.groovyJarsPresent())
926       {
927         System.out.println("Executing script " + groovyscript);
928         executeGroovyScript(groovyscript, startUpAlframe);
929       }
930       else
931       {
932         System.err.println(
933                 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
934                         + groovyscript);
935       }
936     }
937     // and finally, turn off batch mode indicator - if the desktop still exists
938     if (desktop != null)
939     {
940       if (progress != -1)
941       {
942         desktop.setProgressBar(null, progress);
943       }
944       desktop.setInBatchMode(false);
945     }
946
947     return null;
948   }
949
950   private boolean checkStartVamas(ArgsParser aparser)
951   {
952     String vamsasImport = aparser.getValue(ArgsParser.VDOC);
953     String vamsasSession = aparser.getValue(ArgsParser.VSESS);
954     if (vamsasImport == null && vamsasSession == null)
955     {
956       return false;
957     }
958     if (desktop == null || headless)
959     {
960       System.out.println(
961               "Headless vamsas sessions not yet supported. Sorry.");
962       System.exit(1);
963     }
964     boolean haveImport = (vamsasImport != null);
965     if (haveImport)
966     {
967       // if we have a file, start a new session and import it.
968       boolean inSession = false;
969       try
970       {
971         DataSourceType viprotocol = AppletFormatAdapter
972                 .checkProtocol(vamsasImport);
973         if (viprotocol == DataSourceType.FILE)
974         {
975           inSession = desktop.vamsasImport(new File(vamsasImport));
976         }
977         else if (viprotocol == DataSourceType.URL)
978         {
979           inSession = desktop.vamsasImport(new URL(vamsasImport));
980         }
981
982       } catch (Exception e)
983       {
984         System.err.println("Exeption when importing " + vamsasImport
985                 + " as a vamsas document.");
986         e.printStackTrace();
987       }
988       if (!inSession)
989       {
990         System.err.println("Failed to import " + vamsasImport
991                 + " as a vamsas document.");
992       }
993       else
994       {
995         System.out.println("Imported Successfully into new session "
996                 + desktop.getVamsasApplication().getCurrentSession());
997       }
998     }
999     if (vamsasSession != null)
1000     {
1001       if (vamsasImport != null)
1002       {
1003         // close the newly imported session and import the Jalview specific
1004         // remnants into the new session later on.
1005         desktop.vamsasStop_actionPerformed(null);
1006       }
1007       // now join the new session
1008       try
1009       {
1010         if (desktop.joinVamsasSession(vamsasSession))
1011         {
1012           System.out.println(
1013                   "Successfully joined vamsas session " + vamsasSession);
1014         }
1015         else
1016         {
1017           System.err.println("WARNING: Failed to join vamsas session "
1018                   + vamsasSession);
1019         }
1020       } catch (Exception e)
1021       {
1022         System.err.println(
1023                 "ERROR: Failed to join vamsas session " + vamsasSession);
1024         e.printStackTrace();
1025       }
1026       if (vamsasImport != null)
1027       {
1028         // the Jalview specific remnants can now be imported into the new
1029         // session at the user's leisure.
1030         Cache.log.info(
1031                 "Skipping Push for import of data into existing vamsas session.");
1032         // TODO:
1033         // enable
1034         // this
1035         // when
1036         // debugged
1037         // desktop.getVamsasApplication().push_update();
1038       }
1039     }
1040     return haveImport;
1041   }
1042
1043   private void checkOutputFile(ArgsParser aparser, AlignFrame af,
1044           FileFormatI format)
1045   {
1046     String imageName = "unnamed.png";
1047     while (aparser.getSize() > 1)
1048     {
1049       // PNG filename
1050       // SVG filename
1051       // HTML filename
1052       // biojsmsa filename
1053       String outputFormat = aparser.nextValue();
1054       String file = aparser.nextValue();
1055       if (outputFormat.equalsIgnoreCase("png"))
1056       {
1057         af.createPNG(new File(file));
1058         imageName = (new File(file)).getName();
1059         System.out.println("Creating PNG image: " + file);
1060         continue;
1061       }
1062       else if (outputFormat.equalsIgnoreCase("svg"))
1063       {
1064         File imageFile = new File(file);
1065         imageName = imageFile.getName();
1066         af.createSVG(imageFile);
1067         System.out.println("Creating SVG image: " + file);
1068         continue;
1069       }
1070       else if (outputFormat.equalsIgnoreCase("html"))
1071       {
1072         File imageFile = new File(file);
1073         imageName = imageFile.getName();
1074         HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
1075         htmlSVG.exportHTML(file);
1076
1077         System.out.println("Creating HTML image: " + file);
1078         continue;
1079       }
1080       else if (outputFormat.equalsIgnoreCase("biojsmsa"))
1081       {
1082         if (file == null)
1083         {
1084           System.err.println("The output html file must not be null");
1085           return;
1086         }
1087         try
1088         {
1089           BioJsHTMLOutput.refreshVersionInfo(
1090                   BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
1091         } catch (URISyntaxException e)
1092         {
1093           e.printStackTrace();
1094         }
1095         BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
1096         bjs.exportHTML(file);
1097         System.out.println("Creating BioJS MSA Viwer HTML file: " + file);
1098         continue;
1099       }
1100       else if (outputFormat.equalsIgnoreCase("imgMap"))
1101       {
1102         af.createImageMap(new File(file), imageName);
1103         System.out.println("Creating image map: " + file);
1104         continue;
1105       }
1106       else if (outputFormat.equalsIgnoreCase("eps"))
1107       {
1108         File outputFile = new File(file);
1109         System.out.println(
1110                 "Creating EPS file: " + outputFile.getAbsolutePath());
1111         af.createEPS(outputFile);
1112         continue;
1113       }
1114
1115       af.saveAlignment(file, format);
1116       if (af.isSaveAlignmentSuccessful())
1117       {
1118         System.out.println(
1119                 "Written alignment in " + format + " format to " + file);
1120       }
1121       else
1122       {
1123         System.out.println("Error writing file " + file + " in " + format
1124                 + " format!!");
1125       }
1126
1127     }
1128   }
1129
1130   private static void showUsage()
1131   {
1132     System.out.println(
1133             "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
1134                     + "-nodisplay\tRun Jalview without User Interface.\n"
1135                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
1136                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
1137                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
1138                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
1139                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
1140                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
1141                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
1142                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
1143                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
1144                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
1145                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
1146                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
1147                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
1148                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
1149                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
1150                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
1151                     + "-html FILE\tCreate HTML file from alignment.\n"
1152                     + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
1153                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
1154                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
1155                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
1156                     + "-noquestionnaire\tTurn off questionnaire check.\n"
1157                     + "-nonews\tTurn off check for Jalview news.\n"
1158                     + "-nousagestats\tTurn off google analytics tracking for this session.\n"
1159                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
1160                     // +
1161                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
1162                     // after all other properties files have been read\n\t
1163                     // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
1164                     // passed in correctly)"
1165                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
1166                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
1167                     // +
1168                     // "-vdoc vamsas-document\tImport vamsas document into new
1169                     // session or join existing session with same URN\n"
1170                     // + "-vses vamsas-session\tJoin session with given URN\n"
1171                     + "-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"
1172                     + "\n~Read documentation in Application or visit http://www.jalview.org for description of Features and Annotations file~\n\n");
1173   }
1174
1175   private static void startUsageStats(final Desktop desktop)
1176   {
1177     /**
1178      * start a User Config prompt asking if we can log usage statistics.
1179      */
1180     PromptUserConfig prompter = new PromptUserConfig(
1181             Desktop.getDesktopPane(), "USAGESTATS",
1182             "Jalview Usage Statistics",
1183             "Do you want to help make Jalview better by enabling "
1184                     + "the collection of usage statistics with Google Analytics ?"
1185                     + "\n\n(you can enable or disable usage tracking in the preferences)",
1186             new Runnable()
1187             {
1188               @Override
1189               public void run()
1190               {
1191                 Cache.log.debug(
1192                         "Initialising googletracker for usage stats.");
1193                 Cache.initGoogleTracker();
1194                 Cache.log.debug("Tracking enabled.");
1195               }
1196             }, new Runnable()
1197             {
1198               @Override
1199               public void run()
1200               {
1201                 Cache.log.debug("Not enabling Google Tracking.");
1202               }
1203             }, null, true);
1204     desktop.addDialogThread(prompter);
1205   }
1206
1207   /**
1208    * Locate the given string as a file and pass it to the groovy interpreter.
1209    * 
1210    * @param groovyscript
1211    *          the script to execute
1212    * @param jalviewContext
1213    *          the Jalview Desktop object passed in to the groovy binding as the
1214    *          'Jalview' object.
1215    */
1216   private void executeGroovyScript(String groovyscript, AlignFrame af)
1217   {
1218     /**
1219      * for scripts contained in files
1220      */
1221     File tfile = null;
1222     /**
1223      * script's URI
1224      */
1225     URL sfile = null;
1226     if (groovyscript.trim().equals("STDIN"))
1227     {
1228       // read from stdin into a tempfile and execute it
1229       try
1230       {
1231         tfile = File.createTempFile("jalview", "groovy");
1232         PrintWriter outfile = new PrintWriter(
1233                 new OutputStreamWriter(new FileOutputStream(tfile)));
1234         BufferedReader br = new BufferedReader(
1235                 new InputStreamReader(System.in));
1236         String line = null;
1237         while ((line = br.readLine()) != null)
1238         {
1239           outfile.write(line + "\n");
1240         }
1241         br.close();
1242         outfile.flush();
1243         outfile.close();
1244
1245       } catch (Exception ex)
1246       {
1247         System.err.println("Failed to read from STDIN into tempfile "
1248                 + ((tfile == null) ? "(tempfile wasn't created)"
1249                         : tfile.toString()));
1250         ex.printStackTrace();
1251         return;
1252       }
1253       try
1254       {
1255         sfile = tfile.toURI().toURL();
1256       } catch (Exception x)
1257       {
1258         System.err.println(
1259                 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1260                         + tfile.toURI());
1261         x.printStackTrace();
1262         return;
1263       }
1264     }
1265     else
1266     {
1267       try
1268       {
1269         sfile = new URI(groovyscript).toURL();
1270       } catch (Exception x)
1271       {
1272         tfile = new File(groovyscript);
1273         if (!tfile.exists())
1274         {
1275           System.err.println("File '" + groovyscript + "' does not exist.");
1276           return;
1277         }
1278         if (!tfile.canRead())
1279         {
1280           System.err.println("File '" + groovyscript + "' cannot be read.");
1281           return;
1282         }
1283         if (tfile.length() < 1)
1284         {
1285           System.err.println("File '" + groovyscript + "' is empty.");
1286           return;
1287         }
1288         try
1289         {
1290           sfile = tfile.getAbsoluteFile().toURI().toURL();
1291         } catch (Exception ex)
1292         {
1293           System.err.println("Failed to create a file URL for "
1294                   + tfile.getAbsoluteFile());
1295           return;
1296         }
1297       }
1298     }
1299     try
1300     {
1301       Map<String, Object> vbinding = new HashMap<>();
1302       vbinding.put("Jalview", this);
1303       if (af != null)
1304       {
1305         vbinding.put("currentAlFrame", af);
1306       }
1307       Binding gbinding = new Binding(vbinding);
1308       GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1309       gse.run(sfile.toString(), gbinding);
1310       if ("STDIN".equals(groovyscript))
1311       {
1312         // delete temp file that we made -
1313         // only if it was successfully executed
1314         tfile.delete();
1315       }
1316     } catch (Exception e)
1317     {
1318       System.err.println("Exception Whilst trying to execute file " + sfile
1319               + " as a groovy script.");
1320       e.printStackTrace(System.err);
1321
1322     }
1323   }
1324
1325   public AlignFrame[] getAlignFrames()
1326   {
1327     return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1328             : Desktop.getAlignFrames();
1329
1330   }
1331
1332   /**
1333    * Quit method delegates to Desktop.quit - unless running in headless mode
1334    * when it just ends the JVM
1335    */
1336   public void quit()
1337   {
1338     if (jsFunctionExec != null)
1339     {
1340       jsFunctionExec.tidyUp();
1341       jsFunctionExec = null;
1342     }
1343
1344     if (desktop != null)
1345     {
1346       desktop.quit();
1347     }
1348     else
1349     {
1350       System.exit(0);
1351     }
1352   }
1353
1354   /**
1355    * Get the SwingJS applet ID and combine that with the frameType
1356    * 
1357    * @param frameType
1358    *          "alignment", "desktop", etc., or null
1359    * @return
1360    */
1361   public static String getAppID(String frameType)
1362   {
1363     String id = Cache.getProperty("Info.j2sAppletID");
1364     if (id == null)
1365     {
1366       id = "jalview";
1367     }
1368     return id + (frameType == null ? "" : "-" + frameType);
1369   }
1370
1371   /**
1372    * Handle all JalviewLite applet parameters
1373    * 
1374    * @param aparser
1375    * @param af
1376    */
1377   private void loadAppletParams(ArgsParser aparser, AlignFrame af)
1378   {
1379     JalviewApp app = new JalviewApp()
1380     {
1381
1382       // TODO BH 2019
1383       //
1384       // These are methods that are in JalviewLite that various classes call
1385       // but are not in JalviewLiteJsApi. Or, even if they are, other classes
1386       // call
1387       // them to JalviewLite directly. Some may not be necessary, but they have
1388       // to
1389       // be at least mentioned here, or the classes calling them should
1390       // reference
1391       // JalviewLite itself.
1392
1393       private boolean alignPDBStructures; // From JalviewLite; not implemented
1394
1395       private Hashtable<String, Hashtable<String, String[]>> jsmessages;
1396
1397       private Hashtable<String, int[]> jshashes;
1398
1399       @Override
1400       public String getParameter(String name)
1401       {
1402         return aparser.getAppletValue(name, null);
1403       }
1404
1405       @Override
1406       public boolean getDefaultParameter(String name, boolean def)
1407       {
1408         String stn;
1409         return ((stn = getParameter(name)) == null ? def
1410                 : "true".equalsIgnoreCase(stn));
1411       }
1412
1413       /**
1414        * Get the applet-like document base even though this is an application.
1415        */
1416       @Override
1417       public URL getDocumentBase()
1418       {
1419         return Platform.getDocumentBase();
1420       }
1421
1422       /**
1423        * Get the applet-like code base even though this is an application.
1424        */
1425       @Override
1426       public URL getCodeBase()
1427       {
1428         return Platform.getCodeBase();
1429       }
1430
1431       @Override
1432       public AlignViewportI getViewport()
1433       {
1434         return af.getViewport();
1435       }
1436
1437       /**
1438        * features
1439        * 
1440        */
1441       @Override
1442       public boolean parseFeaturesFile(String filename,
1443               DataSourceType protocol)
1444       {
1445         return af.parseFeaturesFile(filename, protocol);
1446       }
1447
1448       /**
1449        * scorefile
1450        * 
1451        */
1452       @Override
1453       public boolean loadScoreFile(String sScoreFile) throws IOException
1454       {
1455         af.loadJalviewDataFile(sScoreFile, null, null, null);
1456         return true;
1457       }
1458
1459       /**
1460        * annotations, jpredfile, jnetfile
1461        * 
1462        */
1463       @Override
1464       public void updateForAnnotations()
1465       {
1466         af.updateForAnnotations();
1467       }
1468
1469       @Override
1470       public void loadTree(NewickFile fin, String treeFile)
1471               throws IOException
1472       {
1473         // n/a -- already done by standard Jalview command line processing
1474       }
1475
1476       @Override
1477       public void setAlignPdbStructures(boolean defaultParameter)
1478       {
1479         alignPDBStructures = true;
1480       }
1481
1482       @Override
1483       public void newStructureView(PDBEntry pdb, SequenceI[] seqs,
1484               String[] chains, DataSourceType protocol)
1485       {
1486         StructureViewer.launchStructureViewer(af.alignPanel, pdb, seqs);
1487       }
1488
1489       @Override
1490       public void setFeatureGroupState(String[] groups, boolean state)
1491       {
1492         af.setFeatureGroupState(groups, state);
1493       }
1494
1495       @Override
1496       public void alignedStructureView(PDBEntry[] pdb, SequenceI[][] seqs,
1497               String[][] chains, String[] protocols)
1498       {
1499         System.err.println(
1500                 "Jalview applet interface alignedStructureView not implemented");
1501       }
1502
1503       @Override
1504       public void newFeatureSettings()
1505       {
1506         System.err.println(
1507                 "Jalview applet interface newFeatureSettings not implemented");
1508       }
1509
1510       private Vector<Runnable> jsExecQueue;
1511
1512       @Override
1513       public Vector<Runnable> getJsExecQueue(JSFunctionExec exec)
1514       {
1515         jsFunctionExec = exec;
1516         return (jsExecQueue == null ? (jsExecQueue = new Vector<>())
1517                 : jsExecQueue);
1518       }
1519
1520       @Override
1521       public AppletContext getAppletContext()
1522       {
1523         // TODO Auto-generated method stub
1524         return null;
1525       }
1526
1527       @Override
1528       public boolean isJsfallbackEnabled()
1529       {
1530         // TODO Auto-generated method stub
1531         return false;
1532       }
1533
1534       @Override
1535       public JSObject getJSObject()
1536       {
1537         // TODO Auto-generated method stub
1538         return null;
1539       }
1540
1541       @Override
1542       public StructureSelectionManagerProvider getStructureSelectionManagerProvider()
1543       {
1544         // TODO Q: what exactly is this? BH
1545         return null;
1546       }
1547
1548       @Override
1549       public void updateColoursFromMouseOver(Object source,
1550               MouseOverStructureListener mouseOverStructureListener)
1551       {
1552         // TODO Auto-generated method stub
1553
1554       }
1555
1556       @Override
1557       public Object[] getSelectionForListener(SequenceGroup seqsel,
1558               ColumnSelection colsel, HiddenColumns hidden,
1559               SelectionSource source, Object alignFrame)
1560       {
1561         return appLoader.getSelectionForListener(getCurrentAlignFrame(),
1562                 seqsel, colsel, hidden, source, alignFrame);
1563       }
1564
1565       @Override
1566       public String arrayToSeparatorList(String[] array)
1567       {
1568         return appLoader.arrayToSeparatorList(array);
1569       }
1570
1571       @Override
1572       public Hashtable<String, int[]> getJSHashes()
1573       {
1574         return (jshashes == null ? (jshashes = new Hashtable<>())
1575                 : jshashes);
1576       }
1577
1578       @Override
1579       public Hashtable<String, Hashtable<String, String[]>> getJSMessages()
1580       {
1581         return (jsmessages == null ? (jsmessages = new Hashtable<>())
1582                 : jsmessages);
1583       }
1584
1585       @Override
1586       public Object getFrameForSource(VamsasSource source)
1587       {
1588         if (source != null)
1589         {
1590           AlignFrame af;
1591           if (source instanceof jalview.gui.AlignViewport
1592                   && source == (af = getCurrentAlignFrame()).getViewport())
1593           {
1594             // should be valid if it just generated an event!
1595             return af;
1596           }
1597           // TODO: ensure that if '_af' is specified along with a handler
1598           // function, then only events from that alignFrame are sent to that
1599           // function
1600         }
1601         return null;
1602       }
1603
1604       @Override
1605       public FeatureRenderer getNewFeatureRenderer(AlignViewportI vp)
1606       {
1607         return new jalview.gui.FeatureRenderer((AlignmentPanel) vp);
1608       }
1609
1610     };
1611
1612     appLoader = new JalviewAppLoader(true);
1613     appLoader.load(app);
1614   }
1615
1616   /**
1617    * 
1618    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences()
1619    */
1620   @Override
1621   public String getSelectedSequences()
1622   {
1623     return getSelectedSequencesFrom(getCurrentAlignFrame());
1624   }
1625
1626   /**
1627    * 
1628    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences(java.lang.String)
1629    */
1630   @Override
1631   public String getSelectedSequences(String sep)
1632   {
1633     return getSelectedSequencesFrom(getCurrentAlignFrame(), sep);
1634   }
1635
1636   /**
1637    * 
1638    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
1639    *      .AlignFrame)
1640    */
1641   @Override
1642   public String getSelectedSequencesFrom(AlignFrameI alf)
1643   {
1644     return getSelectedSequencesFrom(alf, null);
1645   }
1646
1647   /**
1648    * 
1649    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
1650    *      .AlignFrame, java.lang.String)
1651    */
1652   @Override
1653   public String getSelectedSequencesFrom(AlignFrameI alf, String sep)
1654   {
1655     return appLoader.getSelectedSequencesFrom(alf, sep);
1656   }
1657
1658   /**
1659    * 
1660    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
1661    *      .AlignFrame, java.lang.String)
1662    */
1663   @Override
1664   public void highlight(String sequenceId, String position,
1665           String alignedPosition)
1666   {
1667     highlightIn(getCurrentAlignFrame(), sequenceId, position,
1668             alignedPosition);
1669   }
1670
1671   @Override
1672   public void highlightIn(AlignFrameI alf, String sequenceId,
1673           String position, String alignedPosition)
1674   {
1675     appLoader.highlightIn(alf, sequenceId, position, alignedPosition);
1676   }
1677
1678   @Override
1679   public void select(String sequenceIds, String columns)
1680   {
1681     selectIn(getCurrentAlignFrame(), sequenceIds, columns, null);
1682   }
1683
1684   @Override
1685   public void select(String sequenceIds, String columns, String sep)
1686   {
1687     selectIn(getCurrentAlignFrame(), sequenceIds, columns, sep);
1688   }
1689
1690   @Override
1691   public void selectIn(AlignFrameI alf, String sequenceIds, String columns)
1692   {
1693     selectIn(alf, sequenceIds, columns, null);
1694   }
1695
1696   @Override
1697   public void selectIn(AlignFrameI alf, String sequenceIds, String columns,
1698           String sep)
1699   {
1700     appLoader.selectIn(alf, sequenceIds, columns, sep);
1701   }
1702
1703   @Override
1704   public String getSelectedSequencesAsAlignment(String format,
1705           String suffix)
1706   {
1707     return getSelectedSequencesAsAlignmentFrom(getCurrentAlignFrame(),
1708             format, suffix);
1709   }
1710
1711   @Override
1712   public String getSelectedSequencesAsAlignmentFrom(AlignFrameI alf,
1713           String format, String sep)
1714   {
1715     return appLoader.getSelectedSequencesAsAlignmentFrom(alf, format, sep);
1716   }
1717
1718   @Override
1719   public String getAlignmentOrder()
1720   {
1721     return getAlignmentFrom(getCurrentAlignFrame(), null);
1722   }
1723
1724   @Override
1725   public String getAlignmentOrderFrom(AlignFrameI alf)
1726   {
1727     return getAlignmentFrom(alf, null);
1728   }
1729
1730   @Override
1731   public String getAlignmentOrderFrom(AlignFrameI alf, String sep)
1732   {
1733     return appLoader.getAlignmentOrderFrom(alf, sep);
1734   }
1735
1736   @Override
1737   public String orderBy(String order, String undoName)
1738   {
1739     return orderBy(order, undoName, null);
1740   }
1741
1742   @Override
1743   public String orderBy(String order, String undoName, String sep)
1744   {
1745     return orderAlignmentBy(getCurrentAlignFrame(), order, undoName, sep);
1746   }
1747
1748   @Override
1749   public String orderAlignmentBy(AlignFrameI alf, String order,
1750           String undoName, String sep)
1751   {
1752     return appLoader.orderAlignmentBy(alf, order, undoName, sep);
1753   }
1754
1755   @Override
1756   public String getAlignment(String format)
1757   {
1758     return getAlignmentFrom(null, format, null);
1759   }
1760
1761   @Override
1762   public String getAlignmentFrom(AlignFrameI alf, String format)
1763   {
1764     return getAlignmentFrom(alf, format, null);
1765   }
1766
1767   @Override
1768   public String getAlignment(String format, String suffix)
1769   {
1770     return getAlignmentFrom(getCurrentAlignFrame(), format, suffix);
1771   }
1772
1773   @Override
1774   public String getAlignmentFrom(AlignFrameI alf, String format,
1775           String suffix)
1776   {
1777     return appLoader.getAlignmentFrom(alf, format, suffix);
1778   }
1779
1780   @Override
1781   public void loadAnnotation(String annotation)
1782   {
1783     loadAnnotationFrom(getCurrentAlignFrame(), annotation);
1784   }
1785
1786   @Override
1787   public void loadAnnotationFrom(AlignFrameI alf, String annotation)
1788   {
1789     appLoader.loadAnnotationFrom(alf, annotation);
1790   }
1791
1792   @Override
1793   public void loadFeatures(String features, boolean autoenabledisplay)
1794   {
1795     loadFeaturesFrom(currentAlignFrame, features, autoenabledisplay);
1796   }
1797
1798   @Override
1799   public boolean loadFeaturesFrom(AlignFrameI alf, String features,
1800           boolean autoenabledisplay)
1801   {
1802     return appLoader.loadFeaturesFrom(alf, features, autoenabledisplay);
1803   }
1804
1805   @Override
1806   public String getFeatures(String format)
1807   {
1808     return getFeaturesFrom(getCurrentAlignFrame(), format);
1809   }
1810
1811   @Override
1812   public String getFeaturesFrom(AlignFrameI alf, String format)
1813   {
1814     return appLoader.getFeaturesFrom(alf, format);
1815   }
1816
1817   @Override
1818   public String getAnnotation()
1819   {
1820     return getAnnotationFrom(getCurrentAlignFrame());
1821   }
1822
1823   @Override
1824   public String getAnnotationFrom(AlignFrameI alf)
1825   {
1826     return appLoader.getAnnotationFrom(alf);
1827   }
1828
1829   @Override
1830   public AlignFrameI newView()
1831   {
1832     return newViewFrom(getCurrentAlignFrame(), null);
1833   }
1834
1835   @Override
1836   public AlignFrameI newView(String name)
1837   {
1838     return newViewFrom(getCurrentAlignFrame(), name);
1839   }
1840
1841   @Override
1842   public AlignFrameI newViewFrom(AlignFrameI alf)
1843   {
1844     return newViewFrom(alf, null);
1845   }
1846
1847   @Override
1848   public AlignFrameI newViewFrom(AlignFrameI alf, String name)
1849   {
1850     return appLoader.newViewFrom(alf, name);
1851   }
1852
1853   @Override
1854   public AlignFrameI loadAlignment(String text, String title)
1855   {
1856     return appLoader.loadAlignment(text, AlignFrame.DEFAULT_WIDTH,
1857             AlignFrame.DEFAULT_HEIGHT, title);
1858   }
1859
1860   @Override
1861   public boolean addPdbFile(AlignFrameI alFrame, String sequenceId,
1862           String pdbEntryString, String pdbFile)
1863   {
1864     return appLoader.addPdbFile(alFrame, sequenceId, pdbEntryString,
1865             pdbFile);
1866   }
1867
1868   @Override
1869   public void scrollViewToIn(AlignFrameI alf, String topRow,
1870           String leftHandColumn)
1871   {
1872     appLoader.scrollViewToIn(alf, topRow, leftHandColumn);
1873   }
1874
1875   @Override
1876   public void scrollViewToRowIn(AlignFrameI alf, String topRow)
1877   {
1878     appLoader.scrollViewToRowIn(alf, topRow);
1879   }
1880
1881   @Override
1882   public void scrollViewToColumnIn(AlignFrameI alf, String leftHandColumn)
1883   {
1884     appLoader.scrollViewToColumnIn(alf, leftHandColumn);
1885   }
1886
1887   @Override
1888   public String getFeatureGroups()
1889   {
1890     return getFeatureGroupsOn(getCurrentAlignFrame());
1891   }
1892
1893   @Override
1894   public String getFeatureGroupsOn(AlignFrameI alf)
1895   {
1896     return appLoader.getFeatureGroupsOn(alf);
1897   }
1898
1899   @Override
1900   public String getFeatureGroupsOfState(boolean visible)
1901   {
1902     return getFeatureGroupsOfStateOn(getCurrentAlignFrame(), visible);
1903   }
1904
1905   @Override
1906   public String getFeatureGroupsOfStateOn(AlignFrameI alf, boolean visible)
1907   {
1908     return appLoader.getFeatureGroupsOfStateOn(alf, visible);
1909   }
1910
1911   @Override
1912   public void setFeatureGroupStateOn(AlignFrameI alf, String groups,
1913           boolean state)
1914   {
1915     setFeatureGroupStateOn(alf, groups, state);
1916   }
1917
1918   @Override
1919   public void setFeatureGroupState(String groups, boolean state)
1920   {
1921     appLoader.setFeatureGroupStateOn(getCurrentAlignFrame(), groups, state);
1922   }
1923
1924   @Override
1925   public String getSeparator()
1926   {
1927     return appLoader.getSeparator();
1928   }
1929
1930   @Override
1931   public void setSeparator(String separator)
1932   {
1933     appLoader.setSeparator(separator);
1934   }
1935
1936   @Override
1937   public String getJsMessage(String messageclass, String viewId)
1938   {
1939     // see http://www.jalview.org/examples/jalviewLiteJs.html
1940     return null;
1941   }
1942
1943   /**
1944    * Open a new Tree panel on the desktop statically. Params are standard (not
1945    * set by Groovy). No dialog is opened.
1946    * 
1947    * @param af
1948    * @param treeType
1949    * @param modelName
1950    * @return null, or the string "label.you_need_at_least_n_sequences" if number
1951    *         of sequences selected is inappropriate
1952    */
1953   @Override
1954   public Object openTreePanel(AlignFrame af, String treeType,
1955           String modelName)
1956   {
1957     return CalculationChooser.openTreePanel(af, treeType, modelName, null);
1958   }
1959
1960   /**
1961    * public static method for JalviewJS API to open a PCAPanel without
1962    * necessarily using a dialog.
1963    * 
1964    * @param af
1965    * @param modelName
1966    * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences"
1967    *         if number of sequences selected is inappropriate
1968    */
1969   @Override
1970   public Object openPcaPanel(AlignFrame af, String modelName)
1971   {
1972     return CalculationChooser.openPcaPanel(af, modelName, null);
1973   }
1974
1975   @Override
1976   public String getSelectedSequencesAsAlignment(String format,
1977           boolean suffix)
1978   {
1979     return getSelectedSequencesAsAlignmentFrom(getCurrentAlignFrame(),
1980             format, suffix);
1981   }
1982
1983   @Override
1984   public String getSelectedSequencesAsAlignmentFrom(AlignFrameI alf,
1985           String format, boolean suffix)
1986   {
1987     return appLoader.getSelectedSequencesAsAlignmentFrom(alf, format,
1988             "" + suffix);
1989   }
1990
1991   @Override
1992   public String arrayToSeparatorList(String[] array)
1993   {
1994     return appLoader.arrayToSeparatorList(array);
1995   }
1996
1997   @Override
1998   public String[] separatorListToArray(String list)
1999   {
2000     return appLoader.separatorListToArray(list);
2001   }
2002
2003   //// probably not needed in JalviewJS -- From when Jmol and Jalview did not
2004   //// have a direct connection?
2005
2006   @Override
2007   public void setMouseoverListener(String listener)
2008   {
2009     // TODO Auto-generated method stub
2010
2011   }
2012
2013   @Override
2014   public void setMouseoverListener(AlignFrameI af, String listener)
2015   {
2016     // TODO Auto-generated method stub
2017
2018   }
2019
2020   @Override
2021   public void setSelectionListener(String listener)
2022   {
2023     // TODO Auto-generated method stub
2024
2025   }
2026
2027   @Override
2028   public void setSelectionListener(AlignFrameI af, String listener)
2029   {
2030     // TODO Auto-generated method stub
2031
2032   }
2033
2034   @Override
2035   public void setStructureListener(String listener, String modelSet)
2036   {
2037     // TODO Auto-generated method stub
2038
2039   }
2040
2041   @Override
2042   public void removeJavascriptListener(AlignFrameI af, String listener)
2043   {
2044     // TODO Auto-generated method stub
2045
2046   }
2047
2048   @Override
2049   public void mouseOverStructure(String pdbResNum, String chain,
2050           String pdbfile)
2051   {
2052     // TODO Auto-generated method stub
2053
2054   }
2055
2056   @Override
2057   public void showOverview()
2058   {
2059     currentAlignFrame.overviewMenuItem_actionPerformed(null);
2060   }
2061
2062   public void notifyWorker(AlignCalcWorkerI worker, String status)
2063   {
2064     // System.out.println("Jalview worker " + worker.getClass().getSimpleName()
2065     // + " " + status);
2066   }
2067
2068 }
2069