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