b6fc7e89557ac23daf8ffa55da324765d34ae68b
[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 (aparser.contains(ArgsParser.NOANNOTATION)
811                   || aparser.contains(ArgsParser.NOANNOTATION2))
812           {
813             af.getViewport().setShowAnnotation(false);
814             if (!af.getViewport().isShowAnnotation())
815             {
816               doUpdateAnnotation = true;
817               System.out
818                       .println("CMD no-annotation executed successfully!");
819             }
820           }
821         }
822         if (aparser.contains(ArgsParser.NOSORTBYTREE))
823         {
824           af.getViewport().setSortByTree(false);
825           if (!af.getViewport().getSortByTree())
826           {
827             doUpdateAnnotation = true;
828             System.out
829                     .println("CMD [-nosortbytree] executed successfully!");
830           }
831         }
832         if (doUpdateAnnotation)
833         { // BH 2019.07.24
834           af.setMenusForViewport();
835           af.alignPanel.updateLayout();
836         }
837         data = aparser.getValue(ArgsParser.TREE, true);
838         if (data != null)
839         {
840           try
841           {
842             System.out.println(
843                     "CMD [-tree " + data + "] executed successfully!");
844             NewickFile nf = new NewickFile(data,
845                     AppletFormatAdapter.checkProtocol(data));
846             af.getViewport()
847                     .setCurrentTree(af.showNewickTree(nf, data).getTree());
848           } catch (IOException ex)
849           {
850             System.err.println("Couldn't add tree " + data);
851             ex.printStackTrace(System.err);
852           }
853         }
854         // TODO - load PDB structure(s) to alignment JAL-629
855         // (associate with identical sequence in alignment, or a specified
856         // sequence)
857         if (isJavaAppletTag)
858         {
859           loadAppletParams(aparser, af);
860         }
861         else if (!isJS)
862         /**
863          * Java only
864          * 
865          * @j2sIgnore
866          */
867         {
868           if (groovyscript != null)
869           {
870             // Execute the groovy script after we've done all the rendering
871             // stuff
872             // and before any images or figures are generated.
873             System.out.println("Executing script " + groovyscript);
874             executeGroovyScript(groovyscript, af);
875             System.out.println("CMD groovy[" + groovyscript
876                     + "] executed successfully!");
877             groovyscript = null;
878           }
879         }
880         createOutputFiles(aparser, af, format);
881         while (aparser.getSize() > 0)
882         {
883           System.out.println("Unknown arg: " + aparser.nextValue());
884         }
885       }
886     }
887
888     AlignFrame startUpAlframe = null;
889     // We'll only open the default file if the desktop is visible.
890     // And the user
891     // ////////////////////
892
893     if (!isJS && !headless && file == null
894             && jalview.bin.Cache.getDefault("SHOW_STARTUP_FILE", true))
895     /**
896      * Java only
897      * 
898      * @j2sIgnore
899      */
900     {
901       file = jalview.bin.Cache.getDefault("STARTUP_FILE",
902               jalview.bin.Cache.getDefault("www.jalview.org",
903                       "http://www.jalview.org")
904                       + "/examples/exampleFile_2_7.jar");
905       if (file.equals(
906               "http://www.jalview.org/examples/exampleFile_2_3.jar"))
907       {
908         // hardwire upgrade of the startup file
909         file.replace("_2_3.jar", "_2_7.jar");
910         // and remove the stale setting
911         jalview.bin.Cache.removeProperty("STARTUP_FILE");
912       }
913
914       protocol = DataSourceType.FILE;
915
916       if (file.indexOf("http:") > -1)
917       {
918         protocol = DataSourceType.URL;
919       }
920
921       if (file.endsWith(".jar"))
922       {
923         format = FileFormat.Jalview;
924       }
925       else
926       {
927         try
928         {
929           format = new IdentifyFile().identify(file, protocol);
930         } catch (FileFormatException e)
931         {
932           // TODO what?
933         }
934       }
935
936       startUpAlframe = new FileLoader(!headless)
937               .loadFileWaitTillLoaded(file, protocol, format);
938       // extract groovy arguments before anything else.
939     }
940
941     // Once all other stuff is done, execute any groovy scripts (in order)
942     if (groovyscript != null)
943     {
944       if (Cache.groovyJarsPresent())
945       {
946         System.out.println("Executing script " + groovyscript);
947         executeGroovyScript(groovyscript, startUpAlframe);
948       }
949       else
950       {
951         System.err.println(
952                 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
953                         + groovyscript);
954       }
955     }
956     // and finally, turn off batch mode indicator - if the desktop still exists
957     if (desktop != null)
958     {
959       if (progress != -1)
960       {
961         desktop.setProgressBar(null, progress);
962       }
963       desktop.setInBatchMode(false);
964     }
965
966   }
967
968   /**
969    * Writes an output file for each format (if any) specified in the
970    * command-line arguments. Supported formats are currently
971    * <ul>
972    * <li>png</li>
973    * <li>svg</li>
974    * <li>html</li>
975    * <li>biojsmsa</li>
976    * <li>imgMap</li>
977    * <li>eps</li>
978    * </ul>
979    * A format parameter should be followed by a parameter specifying the output
980    * file name. {@code imgMap} parameters should follow those for the
981    * corresponding alignment image output.
982    * 
983    * @param aparser
984    * @param af
985    * @param format
986    */
987   private void createOutputFiles(ArgsParser aparser, AlignFrame af,
988           FileFormatI format)
989   {
990     String imageName = "unnamed.png";
991     while (aparser.getSize() > 1)
992     {
993       String outputFormat = aparser.nextValue();
994       String file = aparser.nextValue();
995       // System.out.println("format " + outputFormat);
996
997       if (outputFormat.equalsIgnoreCase("png"))
998       {
999         af.createPNG(new File(file));
1000         imageName = (new File(file)).getName();
1001         System.out.println("Creating PNG image: " + file);
1002         continue;
1003       }
1004       else if (outputFormat.equalsIgnoreCase("svg"))
1005       {
1006         File imageFile = new File(file);
1007         imageName = imageFile.getName();
1008         af.createSVG(imageFile);
1009         System.out.println("Creating SVG image: " + file);
1010         continue;
1011       }
1012       else if (outputFormat.equalsIgnoreCase("html"))
1013       {
1014         File imageFile = new File(file);
1015         imageName = imageFile.getName();
1016         HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
1017         htmlSVG.exportHTML(file);
1018         System.out.println("Creating HTML image: " + file);
1019         continue;
1020       }
1021       else if (outputFormat.equalsIgnoreCase("biojsmsa"))
1022       {
1023         if (file == null)
1024         {
1025           System.err.println("The output html file must not be null");
1026           return;
1027         }
1028         try
1029         {
1030           BioJsHTMLOutput.refreshVersionInfo(
1031                   BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
1032         } catch (URISyntaxException e)
1033         {
1034           e.printStackTrace();
1035         }
1036         BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
1037         bjs.exportHTML(file);
1038         System.out.println("Creating BioJS MSA Viwer HTML file: " + file);
1039         continue;
1040       }
1041       else if (outputFormat.equalsIgnoreCase("imgMap"))
1042       {
1043         af.createImageMap(new File(file), imageName);
1044         System.out.println("Creating image map: " + file);
1045         continue;
1046       }
1047       else if (outputFormat.equalsIgnoreCase("eps"))
1048       {
1049         File outputFile = new File(file);
1050         System.out.println(
1051                 "Creating EPS file: " + outputFile.getAbsolutePath());
1052         af.createEPS(outputFile);
1053         continue;
1054       }
1055
1056       af.saveAlignment(file, format);
1057       if (af.isSaveAlignmentSuccessful())
1058       {
1059         System.out.println(
1060                 "Written alignment in " + format + " format to " + file);
1061       }
1062       else
1063       {
1064         System.out.println("Error writing file " + file + " in " + format
1065                 + " format!!");
1066       }
1067
1068     }
1069   }
1070
1071   private static void showUsage()
1072   {
1073     System.out.println(
1074             "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
1075                     + "-nodisplay\tRun Jalview without User Interface.\n"
1076                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
1077                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
1078                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
1079                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
1080                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
1081                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
1082                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
1083                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
1084                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
1085                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
1086                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
1087                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
1088                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
1089                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
1090                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
1091                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
1092                     + "-html FILE\tCreate HTML file from alignment.\n"
1093                     + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
1094                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
1095                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
1096                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
1097                     + "-noquestionnaire\tTurn off questionnaire check.\n"
1098                     + "-nonews\tTurn off check for Jalview news.\n"
1099                     + "-nousagestats\tTurn off google analytics tracking for this session.\n"
1100                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
1101                     // +
1102                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
1103                     // after all other properties files have been read\n\t
1104                     // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
1105                     // passed in correctly)"
1106                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
1107                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
1108                     // +
1109                     // "-vdoc vamsas-document\tImport vamsas document into new
1110                     // session or join existing session with same URN\n"
1111                     // + "-vses vamsas-session\tJoin session with given URN\n"
1112                     + "-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"
1113                     + "\n~Read documentation in Application or visit http://www.jalview.org for description of Features and Annotations file~\n\n");
1114   }
1115
1116   private static void startUsageStats(final Desktop desktop)
1117   {
1118     /**
1119      * start a User Config prompt asking if we can log usage statistics.
1120      */
1121     PromptUserConfig prompter = new PromptUserConfig(
1122             Desktop.getDesktopPane(), "USAGESTATS",
1123             "Jalview Usage Statistics",
1124             "Do you want to help make Jalview better by enabling "
1125                     + "the collection of usage statistics with Google Analytics ?"
1126                     + "\n\n(you can enable or disable usage tracking in the preferences)",
1127             new Runnable()
1128             {
1129               @Override
1130               public void run()
1131               {
1132                 Cache.log.debug(
1133                         "Initialising googletracker for usage stats.");
1134                 Cache.initGoogleTracker();
1135                 Cache.log.debug("Tracking enabled.");
1136               }
1137             }, new Runnable()
1138             {
1139               @Override
1140               public void run()
1141               {
1142                 Cache.log.debug("Not enabling Google Tracking.");
1143               }
1144             }, null, true);
1145     desktop.addDialogThread(prompter);
1146   }
1147
1148   /**
1149    * Locate the given string as a file and pass it to the groovy interpreter.
1150    * 
1151    * @param groovyscript
1152    *          the script to execute
1153    * @param jalviewContext
1154    *          the Jalview Desktop object passed in to the groovy binding as the
1155    *          'Jalview' object.
1156    */
1157   private void executeGroovyScript(String groovyscript, AlignFrame af)
1158   {
1159     /**
1160      * for scripts contained in files
1161      */
1162     File tfile = null;
1163     /**
1164      * script's URI
1165      */
1166     URL sfile = null;
1167     if (groovyscript.trim().equals("STDIN"))
1168     {
1169       // read from stdin into a tempfile and execute it
1170       try
1171       {
1172         tfile = File.createTempFile("jalview", "groovy");
1173         PrintWriter outfile = new PrintWriter(
1174                 new OutputStreamWriter(new FileOutputStream(tfile)));
1175         BufferedReader br = new BufferedReader(
1176                 new InputStreamReader(System.in));
1177         String line = null;
1178         while ((line = br.readLine()) != null)
1179         {
1180           outfile.write(line + "\n");
1181         }
1182         br.close();
1183         outfile.flush();
1184         outfile.close();
1185
1186       } catch (Exception ex)
1187       {
1188         System.err.println("Failed to read from STDIN into tempfile "
1189                 + ((tfile == null) ? "(tempfile wasn't created)"
1190                         : tfile.toString()));
1191         ex.printStackTrace();
1192         return;
1193       }
1194       try
1195       {
1196         sfile = tfile.toURI().toURL();
1197       } catch (Exception x)
1198       {
1199         System.err.println(
1200                 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1201                         + tfile.toURI());
1202         x.printStackTrace();
1203         return;
1204       }
1205     }
1206     else
1207     {
1208       try
1209       {
1210         sfile = new URI(groovyscript).toURL();
1211       } catch (Exception x)
1212       {
1213         tfile = new File(groovyscript);
1214         if (!tfile.exists())
1215         {
1216           System.err.println("File '" + groovyscript + "' does not exist.");
1217           return;
1218         }
1219         if (!tfile.canRead())
1220         {
1221           System.err.println("File '" + groovyscript + "' cannot be read.");
1222           return;
1223         }
1224         if (tfile.length() < 1)
1225         {
1226           System.err.println("File '" + groovyscript + "' is empty.");
1227           return;
1228         }
1229         try
1230         {
1231           sfile = tfile.getAbsoluteFile().toURI().toURL();
1232         } catch (Exception ex)
1233         {
1234           System.err.println("Failed to create a file URL for "
1235                   + tfile.getAbsoluteFile());
1236           return;
1237         }
1238       }
1239     }
1240     try
1241     {
1242       Map<String, Object> vbinding = new HashMap<>();
1243       vbinding.put("Jalview", this);
1244       if (af != null)
1245       {
1246         vbinding.put("currentAlFrame", af);
1247       }
1248       Binding gbinding = new Binding(vbinding);
1249       GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1250       gse.run(sfile.toString(), gbinding);
1251       if ("STDIN".equals(groovyscript))
1252       {
1253         // delete temp file that we made -
1254         // only if it was successfully executed
1255         tfile.delete();
1256       }
1257     } catch (Exception e)
1258     {
1259       System.err.println("Exception Whilst trying to execute file " + sfile
1260               + " as a groovy script.");
1261       e.printStackTrace(System.err);
1262
1263     }
1264   }
1265
1266   public AlignFrame[] getAlignFrames()
1267   {
1268     return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1269             : Desktop.getAlignFrames();
1270
1271   }
1272
1273   /**
1274    * Quit method delegates to Desktop.quit - unless running in headless mode
1275    * when it just ends the JVM
1276    */
1277   public void quit()
1278   {
1279     if (jsFunctionExec != null)
1280     {
1281       jsFunctionExec.tidyUp();
1282       jsFunctionExec = null;
1283     }
1284
1285     if (desktop != null)
1286     {
1287       desktop.quit();
1288     }
1289     else
1290     {
1291       System.exit(0);
1292     }
1293   }
1294
1295   /**
1296    * Get the SwingJS applet ID and combine that with the frameType
1297    * 
1298    * @param frameType
1299    *          "alignment", "desktop", etc., or null
1300    * @return
1301    */
1302   public static String getAppID(String frameType)
1303   {
1304     String id = Cache.getProperty("Info.j2sAppletID");
1305     if (id == null)
1306     {
1307       id = "jalview";
1308     }
1309     return id + (frameType == null ? "" : "-" + frameType);
1310   }
1311
1312   /**
1313    * Handle all JalviewLite applet parameters
1314    * 
1315    * @param aparser
1316    * @param af
1317    */
1318   private void loadAppletParams(ArgsParser aparser, AlignFrame af)
1319   {
1320     JalviewApp app = new JalviewApp()
1321     {
1322
1323       // TODO BH 2019
1324       //
1325       // These are methods that are in JalviewLite that various classes call
1326       // but are not in JalviewLiteJsApi. Or, even if they are, other classes
1327       // call
1328       // them to JalviewLite directly. Some may not be necessary, but they have
1329       // to
1330       // be at least mentioned here, or the classes calling them should
1331       // reference
1332       // JalviewLite itself.
1333
1334       private boolean alignPDBStructures; // From JalviewLite; not implemented
1335
1336       private Hashtable<String, Hashtable<String, String[]>> jsmessages;
1337
1338       private Hashtable<String, int[]> jshashes;
1339
1340       @Override
1341       public String getParameter(String name)
1342       {
1343         return aparser.getAppletValue(name, null);
1344       }
1345
1346       @Override
1347       public boolean getDefaultParameter(String name, boolean def)
1348       {
1349         String stn;
1350         return ((stn = getParameter(name)) == null ? def
1351                 : "true".equalsIgnoreCase(stn));
1352       }
1353
1354       /**
1355        * Get the applet-like document base even though this is an application.
1356        */
1357       @Override
1358       public URL getDocumentBase()
1359       {
1360         return Platform.getDocumentBase();
1361       }
1362
1363       /**
1364        * Get the applet-like code base even though this is an application.
1365        */
1366       @Override
1367       public URL getCodeBase()
1368       {
1369         return Platform.getCodeBase();
1370       }
1371
1372       @Override
1373       public AlignViewportI getViewport()
1374       {
1375         return af.getViewport();
1376       }
1377
1378       /**
1379        * features
1380        * 
1381        */
1382       @Override
1383       public boolean parseFeaturesFile(String filename,
1384               DataSourceType protocol)
1385       {
1386         return af.parseFeaturesFile(filename, protocol);
1387       }
1388
1389       /**
1390        * scorefile
1391        * 
1392        */
1393       @Override
1394       public boolean loadScoreFile(String sScoreFile) throws IOException
1395       {
1396         af.loadJalviewDataFile(sScoreFile, null, null, null);
1397         return true;
1398       }
1399
1400       /**
1401        * annotations, jpredfile, jnetfile
1402        * 
1403        */
1404       @Override
1405       public void updateForAnnotations()
1406       {
1407         af.updateForAnnotations();
1408       }
1409
1410       @Override
1411       public void loadTree(NewickFile fin, String treeFile)
1412               throws IOException
1413       {
1414         // n/a -- already done by standard Jalview command line processing
1415       }
1416
1417       @Override
1418       public void setAlignPdbStructures(boolean defaultParameter)
1419       {
1420         alignPDBStructures = true;
1421       }
1422
1423       @Override
1424       public void newStructureView(PDBEntry pdb, SequenceI[] seqs,
1425               String[] chains, DataSourceType protocol)
1426       {
1427         StructureViewer.launchStructureViewer(af.alignPanel, pdb, seqs);
1428       }
1429
1430       @Override
1431       public void setFeatureGroupState(String[] groups, boolean state)
1432       {
1433         af.setFeatureGroupState(groups, state);
1434       }
1435
1436       @Override
1437       public void alignedStructureView(PDBEntry[] pdb, SequenceI[][] seqs,
1438               String[][] chains, String[] protocols)
1439       {
1440         System.err.println(
1441                 "Jalview applet interface alignedStructureView not implemented");
1442       }
1443
1444       @Override
1445       public void newFeatureSettings()
1446       {
1447         System.err.println(
1448                 "Jalview applet interface newFeatureSettings not implemented");
1449       }
1450
1451       private Vector<Runnable> jsExecQueue;
1452
1453       @Override
1454       public Vector<Runnable> getJsExecQueue(JSFunctionExec exec)
1455       {
1456         jsFunctionExec = exec;
1457         return (jsExecQueue == null ? (jsExecQueue = new Vector<>())
1458                 : jsExecQueue);
1459       }
1460
1461       @Override
1462       public AppletContext getAppletContext()
1463       {
1464         // TODO Auto-generated method stub
1465         return null;
1466       }
1467
1468       @Override
1469       public boolean isJsfallbackEnabled()
1470       {
1471         // TODO Auto-generated method stub
1472         return false;
1473       }
1474
1475       @Override
1476       public JSObject getJSObject()
1477       {
1478         // TODO Auto-generated method stub
1479         return null;
1480       }
1481
1482       @Override
1483       public StructureSelectionManagerProvider getStructureSelectionManagerProvider()
1484       {
1485         // TODO Q: what exactly is this? BH
1486         return null;
1487       }
1488
1489       @Override
1490       public void updateColoursFromMouseOver(Object source,
1491               MouseOverStructureListener mouseOverStructureListener)
1492       {
1493         // TODO Auto-generated method stub
1494
1495       }
1496
1497       @Override
1498       public Object[] getSelectionForListener(SequenceGroup seqsel,
1499               ColumnSelection colsel, HiddenColumns hidden,
1500               SelectionSource source, Object alignFrame)
1501       {
1502         return appLoader.getSelectionForListener(getCurrentAlignFrame(),
1503                 seqsel, colsel, hidden, source, alignFrame);
1504       }
1505
1506       @Override
1507       public String arrayToSeparatorList(String[] array)
1508       {
1509         return appLoader.arrayToSeparatorList(array);
1510       }
1511
1512       @Override
1513       public Hashtable<String, int[]> getJSHashes()
1514       {
1515         return (jshashes == null ? (jshashes = new Hashtable<>())
1516                 : jshashes);
1517       }
1518
1519       @Override
1520       public Hashtable<String, Hashtable<String, String[]>> getJSMessages()
1521       {
1522         return (jsmessages == null ? (jsmessages = new Hashtable<>())
1523                 : jsmessages);
1524       }
1525
1526       @Override
1527       public Object getFrameForSource(VamsasSource source)
1528       {
1529         if (source != null)
1530         {
1531           AlignFrame af;
1532           if (source instanceof jalview.gui.AlignViewport
1533                   && source == (af = getCurrentAlignFrame()).getViewport())
1534           {
1535             // should be valid if it just generated an event!
1536             return af;
1537           }
1538           // TODO: ensure that if '_af' is specified along with a handler
1539           // function, then only events from that alignFrame are sent to that
1540           // function
1541         }
1542         return null;
1543       }
1544
1545       @Override
1546       public FeatureRenderer getNewFeatureRenderer(AlignViewportI vp)
1547       {
1548         return new jalview.gui.FeatureRenderer((AlignmentPanel) vp);
1549       }
1550
1551     };
1552
1553     appLoader = new JalviewAppLoader(true);
1554     appLoader.load(app);
1555   }
1556
1557   /**
1558    * 
1559    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences()
1560    */
1561   @Override
1562   public String getSelectedSequences()
1563   {
1564     return getSelectedSequencesFrom(getCurrentAlignFrame());
1565   }
1566
1567   /**
1568    * 
1569    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences(java.lang.String)
1570    */
1571   @Override
1572   public String getSelectedSequences(String sep)
1573   {
1574     return getSelectedSequencesFrom(getCurrentAlignFrame(), sep);
1575   }
1576
1577   /**
1578    * 
1579    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
1580    *      .AlignFrame)
1581    */
1582   @Override
1583   public String getSelectedSequencesFrom(AlignFrame alf)
1584   {
1585     if (alf == null)
1586     {
1587       alf = getCurrentAlignFrame();
1588     }
1589     return getSelectedSequencesFrom(alf, null);
1590   }
1591
1592   /**
1593    * 
1594    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
1595    *      .AlignFrame, java.lang.String)
1596    */
1597   @Override
1598   public String getSelectedSequencesFrom(AlignFrame alf, String sep)
1599   {
1600     if (alf == null)
1601     {
1602       alf = getCurrentAlignFrame();
1603     }
1604     return appLoader.getSelectedSequencesFrom(alf, sep);
1605   }
1606
1607   /**
1608    * 
1609    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
1610    *      .AlignFrame, java.lang.String)
1611    */
1612   @Override
1613   public void highlight(String sequenceId, String position,
1614           String alignedPosition)
1615   {
1616     highlightIn(null, sequenceId, position,
1617             alignedPosition);
1618   }
1619
1620   @Override
1621   public void highlightIn(AlignFrame alf, String sequenceId,
1622           String position, String alignedPosition)
1623   {
1624     if (alf == null)
1625     {
1626       alf = getCurrentAlignFrame();
1627     }
1628     appLoader.highlightIn(alf, sequenceId, position, alignedPosition);
1629   }
1630
1631   @Override
1632   public void select(String sequenceIds, String columns)
1633   {
1634     selectIn(getCurrentAlignFrame(), sequenceIds, columns, null);
1635   }
1636
1637   @Override
1638   public void select(String sequenceIds, String columns, String sep)
1639   {
1640     selectIn(null, sequenceIds, columns, sep);
1641   }
1642
1643   @Override
1644   public void selectIn(AlignFrame alf, String sequenceIds, String columns)
1645   {
1646     selectIn(alf, sequenceIds, columns, null);
1647   }
1648
1649   @Override
1650   public void selectIn(AlignFrame alf, String sequenceIds, String columns,
1651           String sep)
1652   {
1653     if (alf == null)
1654     {
1655       alf = getCurrentAlignFrame();
1656     }
1657     appLoader.selectIn(alf, sequenceIds, columns, sep);
1658   }
1659
1660   @Override
1661   public String getSelectedSequencesAsAlignment(String format,
1662           String suffix)
1663   {
1664     return getSelectedSequencesAsAlignmentFrom(null,
1665             format, suffix);
1666   }
1667
1668   @Override
1669   public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
1670           String format, String sep)
1671   {
1672     if (alf == null)
1673     {
1674       alf = getCurrentAlignFrame();
1675     }
1676     return appLoader.getSelectedSequencesAsAlignmentFrom(alf, format, sep);
1677   }
1678
1679   @Override
1680   public String getAlignmentOrder()
1681   {
1682     return getAlignmentFrom(getCurrentAlignFrame(), null);
1683   }
1684
1685   @Override
1686   public String getAlignmentOrderFrom(AlignFrame alf)
1687   {
1688     return getAlignmentFrom(alf, null);
1689   }
1690
1691   @Override
1692   public String getAlignmentOrderFrom(AlignFrame alf, String sep)
1693   {
1694     if (alf == null)
1695     {
1696       alf = getCurrentAlignFrame();
1697     }
1698     return appLoader.getAlignmentOrderFrom(alf, sep);
1699   }
1700
1701   @Override
1702   public String orderBy(String order, String undoName)
1703   {
1704     return orderBy(order, undoName, null);
1705   }
1706
1707   @Override
1708   public String orderBy(String order, String undoName, String sep)
1709   {
1710     return orderAlignmentBy(getCurrentAlignFrame(), order, undoName, sep);
1711   }
1712
1713   @Override
1714   public String orderAlignmentBy(AlignFrame alf, String order,
1715           String undoName, String sep)
1716   {
1717     if (alf == null)
1718     {
1719       alf = getCurrentAlignFrame();
1720     }
1721     return appLoader.orderAlignmentBy(alf, order, undoName, sep);
1722   }
1723
1724   @Override
1725   public String getAlignment(String format)
1726   {
1727     return getAlignmentFrom(null, format, null);
1728   }
1729
1730   @Override
1731   public String getAlignmentFrom(AlignFrame alf, String format)
1732   {
1733     return getAlignmentFrom(alf, format, null);
1734   }
1735
1736   @Override
1737   public String getAlignment(String format, String suffix)
1738   {
1739     return getAlignmentFrom(getCurrentAlignFrame(), format, suffix);
1740   }
1741
1742   @Override
1743   public String getAlignmentFrom(AlignFrame alf, String format,
1744           String suffix)
1745   {
1746     return appLoader.getAlignmentFrom(alf, format, suffix);
1747   }
1748
1749   @Override
1750   public void loadAnnotation(String annotation)
1751   {
1752     loadAnnotationFrom(getCurrentAlignFrame(), annotation);
1753   }
1754
1755   @Override
1756   public void loadAnnotationFrom(AlignFrame alf, String annotation)
1757   {
1758     if (alf == null)
1759     {
1760       alf = getCurrentAlignFrame();
1761     }
1762     appLoader.loadAnnotationFrom(alf, annotation);
1763   }
1764
1765   @Override
1766   public void loadFeatures(String features, boolean autoenabledisplay)
1767   {
1768     loadFeaturesFrom(currentAlignFrame, features, autoenabledisplay);
1769   }
1770
1771   @Override
1772   public boolean loadFeaturesFrom(AlignFrame alf, String features,
1773           boolean autoenabledisplay)
1774   {
1775     if (alf == null)
1776     {
1777       alf = getCurrentAlignFrame();
1778     }
1779     return appLoader.loadFeaturesFrom(alf, features, autoenabledisplay);
1780   }
1781
1782   @Override
1783   public String getFeatures(String format)
1784   {
1785     return getFeaturesFrom(null, format);
1786   }
1787
1788   @Override
1789   public String getFeaturesFrom(AlignFrame alf, String format)
1790   {
1791     if (alf == null)
1792     {
1793       alf = getCurrentAlignFrame();
1794     }
1795     return appLoader.getFeaturesFrom(alf, format);
1796   }
1797
1798   @Override
1799   public String getAnnotation()
1800   {
1801     return getAnnotationFrom(null);
1802   }
1803
1804   @Override
1805   public String getAnnotationFrom(AlignFrame alf)
1806   {
1807     if (alf == null)
1808     {
1809       alf = getCurrentAlignFrame();
1810     }
1811     return appLoader.getAnnotationFrom(alf);
1812   }
1813
1814   // @Override
1815   // public AlignFrame newView()
1816   // {
1817   // return newViewFrom(null, null);
1818   // }
1819   //
1820   // @Override
1821   // public AlignFrame newView(String name)
1822   // {
1823   // return newViewFrom(null, name);
1824   // }
1825   //
1826   // @Override
1827   // public AlignFrame newViewFrom(AlignFrame alf)
1828   // {
1829   // return newViewFrom(alf, null);
1830   // }
1831
1832   // @Override
1833   // public AlignFrame newViewFrom(AlignFrame alf, String name)
1834   // {
1835   // if (alf == null)
1836   // {
1837   // alf = getCurrentAlignFrame();
1838   // }
1839   // return appLoader.newViewFrom(alf, name);
1840   // }
1841
1842   @Override
1843   public AlignFrame loadAlignment(String text, String title)
1844   {
1845     return appLoader.loadAlignment(text, AlignFrame.DEFAULT_WIDTH,
1846             AlignFrame.DEFAULT_HEIGHT, title);
1847   }
1848
1849   @Override
1850   public boolean addPdbFile(AlignFrame alFrame, String sequenceId,
1851           String pdbEntryString, String pdbFile)
1852   {
1853     if (alFrame == null)
1854     {
1855       alFrame = getCurrentAlignFrame();
1856     }
1857     return appLoader.addPdbFile(alFrame, sequenceId, pdbEntryString,
1858             pdbFile);
1859   }
1860
1861   @Override
1862   public void scrollViewToIn(AlignFrame alf, String topRow,
1863           String leftHandColumn)
1864   {
1865     if (alf == null)
1866     {
1867       alf = getCurrentAlignFrame();
1868     }
1869     appLoader.scrollViewToIn(alf, topRow, leftHandColumn);
1870   }
1871
1872   @Override
1873   public void scrollViewToRowIn(AlignFrame alf, String topRow)
1874   {
1875     if (alf == null)
1876     {
1877       alf = getCurrentAlignFrame();
1878     }
1879     appLoader.scrollViewToRowIn(alf, topRow);
1880   }
1881
1882   @Override
1883   public void scrollViewToColumnIn(AlignFrame alf, String leftHandColumn)
1884   {
1885     if (alf == null)
1886     {
1887       alf = getCurrentAlignFrame();
1888     }
1889     appLoader.scrollViewToColumnIn(alf, leftHandColumn);
1890   }
1891
1892   @Override
1893   public String getFeatureGroups()
1894   {
1895     return getFeatureGroupsOn(null);
1896   }
1897
1898   @Override
1899   public String getFeatureGroupsOn(AlignFrame alf)
1900   {
1901     if (alf == null)
1902     {
1903       alf = getCurrentAlignFrame();
1904     }
1905     return appLoader.getFeatureGroupsOn(alf);
1906   }
1907
1908   @Override
1909   public String getFeatureGroupsOfState(boolean visible)
1910   {
1911     return getFeatureGroupsOfStateOn(null, visible);
1912   }
1913
1914   @Override
1915   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
1916   {
1917     if (alf == null)
1918     {
1919       alf = getCurrentAlignFrame();
1920     }
1921     return appLoader.getFeatureGroupsOfStateOn(alf, visible);
1922   }
1923
1924   @Override
1925   public void setFeatureGroupState(String groups, boolean state)
1926   { // JalviewLite API
1927     setFeatureGroupStateOn(null, groups, state);
1928   }
1929
1930   @Override
1931   public void setFeatureGroupStateOn(AlignFrame alf, String groups,
1932           boolean state)
1933   {
1934     if (alf == null)
1935     {
1936       alf = getCurrentAlignFrame();
1937     }
1938     appLoader.setFeatureGroupStateOn(alf, groups, state);
1939   }
1940
1941   @Override
1942   public String getSeparator()
1943   {
1944     return appLoader.getSeparator();
1945   }
1946
1947   @Override
1948   public void setSeparator(String separator)
1949   {
1950     appLoader.setSeparator(separator);
1951   }
1952
1953   @Override
1954   public String getJsMessage(String messageclass, String viewId)
1955   {
1956     // see http://www.jalview.org/examples/jalviewLiteJs.html
1957     return null;
1958   }
1959
1960   /**
1961    * Open a new Tree panel on the desktop statically. Params are standard (not
1962    * set by Groovy). No dialog is opened.
1963    * 
1964    * @param af
1965    * @param treeType
1966    * @param modelName
1967    * @return null, or the string "label.you_need_at_least_n_sequences" if number
1968    *         of sequences selected is inappropriate
1969    */
1970   @Override
1971   public Object openTreePanel(AlignFrame af, String treeType,
1972           String modelName)
1973   { // JalviewJS api
1974     if (af == null)
1975     {
1976       af = getCurrentAlignFrame();
1977     }
1978     return CalculationChooser.openTreePanel(af, treeType, modelName, null);
1979   }
1980
1981   /**
1982    * public static method for JalviewJS API to open a PCAPanel without
1983    * necessarily using a dialog.
1984    * 
1985    * @param af
1986    * @param modelName
1987    * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences"
1988    *         if number of sequences selected is inappropriate
1989    */
1990   @Override
1991   public Object openPcaPanel(AlignFrame af, String modelName)
1992   {
1993     if (af == null)
1994     {
1995       af = getCurrentAlignFrame();
1996     }
1997     return CalculationChooser.openPcaPanel(af, modelName, null);
1998   }
1999
2000   @Override
2001   public String getSelectedSequencesAsAlignment(String format,
2002           boolean suffix)
2003   {
2004     return getSelectedSequencesAsAlignmentFrom(null,
2005             format, suffix);
2006   }
2007
2008   @Override
2009   public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
2010           String format, boolean suffix)
2011   {
2012     if (alf == null)
2013     {
2014       alf = getCurrentAlignFrame();
2015     }
2016     return appLoader.getSelectedSequencesAsAlignmentFrom(alf, format,
2017             "" + suffix);
2018   }
2019
2020   @Override
2021   public String arrayToSeparatorList(String[] array)
2022   {
2023     return appLoader.arrayToSeparatorList(array);
2024   }
2025
2026   @Override
2027   public String[] separatorListToArray(String list)
2028   {
2029     return appLoader.separatorListToArray(list);
2030   }
2031
2032   //// probably not needed in JalviewJS -- From when Jmol and Jalview did not
2033   //// have a direct connection?
2034
2035   @Override
2036   public void setMouseoverListener(String listener)
2037   {
2038     // TODO Auto-generated method stub
2039
2040   }
2041
2042   @Override
2043   public void setMouseoverListener(AlignFrame af, String listener)
2044   {
2045     // TODO Auto-generated method stub
2046
2047   }
2048
2049   @Override
2050   public void setSelectionListener(String listener)
2051   {
2052     // TODO Auto-generated method stub
2053
2054   }
2055
2056   @Override
2057   public void setSelectionListener(AlignFrame af, String listener)
2058   {
2059     // TODO Auto-generated method stub
2060
2061   }
2062
2063   @Override
2064   public void setStructureListener(String listener, String modelSet)
2065   {
2066     // TODO Auto-generated method stub
2067
2068   }
2069
2070   @Override
2071   public void removeJavascriptListener(AlignFrame af, String listener)
2072   {
2073     // TODO Auto-generated method stub
2074
2075   }
2076
2077   @Override
2078   public void mouseOverStructure(String pdbResNum, String chain,
2079           String pdbfile)
2080   {
2081     // TODO Auto-generated method stub
2082
2083   }
2084
2085   @Override
2086   public void showOverview()
2087   {
2088     currentAlignFrame.overviewMenuItem_actionPerformed(null);
2089   }
2090
2091   public void notifyWorker(AlignCalcWorkerI worker, String status)
2092   {
2093     // System.out.println("Jalview worker " + worker.getClass().getSimpleName()
2094     // + " " + status);
2095   }
2096
2097   private static boolean isInteractive = true;
2098
2099   public static boolean isInteractive()
2100   {
2101     return isInteractive;
2102   }
2103
2104   public static void setInteractive(boolean tf)
2105   {
2106     isInteractive = tf;
2107   }
2108
2109 }
2110