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