2cf6f37488552e2922d70cd48953d6fdc95de7e8
[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.AlignFrameI;
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 import java.util.logging.ConsoleHandler;
87 import java.util.logging.Level;
88 import java.util.logging.Logger;
89
90 import javax.swing.LookAndFeel;
91 import javax.swing.UIManager;
92
93 import groovy.lang.Binding;
94 import groovy.util.GroovyScriptEngine;
95 import netscape.javascript.JSObject;
96
97 /**
98  * Main class for Jalview Application <br>
99  * <br>
100  * start with: java -classpath "$PATH_TO_LIB$/*:$PATH_TO_CLASSES$" \
101  * jalview.bin.Jalview
102  * 
103  * or on Windows: java -classpath "$PATH_TO_LIB$/*;$PATH_TO_CLASSES$" \
104  * jalview.bin.Jalview jalview.bin.Jalview
105  * 
106  * (ensure -classpath arg is quoted to avoid shell expansion of '*' and do not
107  * embellish '*' to e.g. '*.jar')
108  * 
109  * @author $author$
110  * @version $Revision$
111  */
112 public class Jalview implements ApplicationSingletonI, JalviewJSApi
113 {
114
115   public static Jalview getInstance()
116   {
117     return (Jalview) ApplicationSingletonProvider
118             .getInstance(Jalview.class);
119   }
120
121   private Jalview()
122   {
123   }
124
125   static
126   {
127     Platform.getURLCommandArguments();
128   }
129
130   private boolean headless;
131
132   public static boolean isHeadlessMode()
133   {
134     return getInstance().headless;
135   }
136
137   private Desktop desktop;
138
139   private AlignFrame currentAlignFrame;
140
141   public boolean isJavaAppletTag;
142
143   public String appletResourcePath;
144
145   JalviewAppLoader appLoader;
146
147   protected JSFunctionExec jsFunctionExec;
148
149   public static AlignFrame getCurrentAlignFrame()
150   {
151     return getInstance().currentAlignFrame;
152   }
153
154   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
155   {
156     getInstance().currentAlignFrame = currentAlignFrame;
157   }
158
159   static
160   {
161     if (!Platform.isJS())
162     /**
163      * Java only
164      * 
165      * @j2sIgnore
166      */
167     {
168       // grab all the rights we can for the JVM
169       Policy.setPolicy(new Policy()
170       {
171         @Override
172         public PermissionCollection getPermissions(CodeSource codesource)
173         {
174           Permissions perms = new Permissions();
175           perms.add(new AllPermission());
176           return (perms);
177         }
178
179         @Override
180         public void refresh()
181         {
182         }
183       });
184     }
185   }
186
187   /**
188    * keep track of feature fetching tasks.
189    * 
190    * @author JimP
191    * 
192    */
193   class FeatureFetcher
194   {
195     /*
196      * TODO: generalise to track all jalview events to orchestrate batch
197      * processing events.
198      */
199
200     private int queued = 0;
201
202     private int running = 0;
203
204     public FeatureFetcher()
205     {
206
207     }
208
209     public void addFetcher(final AlignFrame af,
210             final Vector<String> dasSources)
211     {
212       final long id = System.currentTimeMillis();
213       queued++;
214       final FeatureFetcher us = this;
215       new Thread(new Runnable()
216       {
217
218         @Override
219         public void run()
220         {
221           synchronized (us)
222           {
223             queued--;
224             running++;
225           }
226
227           af.setProgressBar(MessageManager
228                   .getString("status.das_features_being_retrived"), id);
229           af.featureSettings_actionPerformed(null);
230           af.setProgressBar(null, id);
231           synchronized (us)
232           {
233             running--;
234           }
235         }
236       }).start();
237     }
238
239     public synchronized boolean allFinished()
240     {
241       return queued == 0 && running == 0;
242     }
243
244   }
245
246   /**
247    * main class for Jalview application
248    * 
249    * @param args
250    *          open <em>filename</em>
251    */
252   public static void main(String[] args)
253   {
254     // setLogging(); // BH - for event debugging in JavaScript
255     getInstance().doMain(args);
256   }
257
258   /**
259    * Allow an outside entity to initiate the second half of argument parsing
260    * (only).
261    * 
262    * @param args
263    * @return null is good
264    */
265   @Override
266   public Object parseArguments(String[] args)
267   {
268
269     try
270     {
271       ArgsParser aparser = new ArgsParser(args);
272       return parseArguments(aparser, false);
273     } catch (Throwable t)
274     {
275       return t;
276     }
277   }
278
279   private static void logClass(String name)
280   {
281     // BH - for event debugging in JavaScript
282     ConsoleHandler consoleHandler = new ConsoleHandler();
283     consoleHandler.setLevel(Level.ALL);
284     Logger logger = Logger.getLogger(name);
285     logger.setLevel(Level.ALL);
286     logger.addHandler(consoleHandler);
287   }
288
289   @SuppressWarnings("unused")
290   private static void setLogging()
291   {
292
293     /**
294      * @j2sIgnore
295      * 
296      */
297     {
298       System.out.println("not in js");
299     }
300
301     // BH - for event debugging in JavaScript (Java mode only)
302     if (!Platform.isJS())
303     /**
304      * Java only
305      * 
306      * @j2sIgnore
307      */
308     {
309       Logger.getLogger("").setLevel(Level.ALL);
310       logClass("java.awt.EventDispatchThread");
311       logClass("java.awt.EventQueue");
312       logClass("java.awt.Component");
313       logClass("java.awt.focus.Component");
314       logClass("java.awt.focus.DefaultKeyboardFocusManager");
315     }
316
317   }
318
319   /**
320    * @param args
321    */
322   void doMain(String[] args)
323   {
324
325     boolean isJS = Platform.isJS();
326     if (!isJS)
327     {
328       System.setSecurityManager(null);
329     }
330
331     System.out
332             .println("Java version: " + System.getProperty("java.version"));
333     System.out.println(System.getProperty("os.arch") + " "
334             + System.getProperty("os.name") + " "
335             + System.getProperty("os.version"));
336
337     ArgsParser aparser = new ArgsParser(args);
338
339     String usrPropsFile = aparser.getValue(ArgsParser.PROPS);
340     Cache.loadProperties(usrPropsFile);
341     if (isJS)
342     {
343       isJavaAppletTag = aparser.isApplet();
344       if (isJavaAppletTag)
345       {
346         Preferences.setAppletDefaults();
347         Cache.loadProperties(usrPropsFile); // again, because we
348         // might be changing defaults here?
349       }
350       System.out.println(
351               "<Applet> found: " + aparser.getValue("Info.j2sAppletID"));
352       appletResourcePath = aparser.getValue("Info.resourcePath");
353     }
354     else
355     /**
356      * Java only
357      * 
358      * @j2sIgnore
359      */
360     {
361       if (usrPropsFile != null)
362       {
363         System.out.println(
364                 "CMD [-props " + usrPropsFile + "] executed successfully!");
365       }
366
367       if (aparser.contains("help") || aparser.contains("h"))
368       {
369         showUsage();
370         System.exit(0);
371       }
372       if (aparser.contains(ArgsParser.NODISPLAY)
373               || aparser.contains(ArgsParser.NOGUI)
374               || aparser.contains(ArgsParser.HEADLESS)
375               || "true".equals(System.getProperty("java.awt.headless")))
376       {
377         headless = true;
378       }
379
380       // anything else!
381
382       final String jabawsUrl = aparser.getValue(ArgsParser.JABAWS);
383       if (jabawsUrl != null)
384       {
385         try
386         {
387           Jws2Discoverer.getInstance().setPreferredUrl(jabawsUrl);
388           System.out.println(
389                   "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
390         } catch (MalformedURLException e)
391         {
392           System.err.println(
393                   "Invalid jabaws parameter: " + jabawsUrl + " ignored");
394         }
395       }
396
397     }
398     // check for property setting
399     String defs = aparser.getValue(ArgsParser.SETPROP);
400     while (defs != null)
401     {
402       int p = defs.indexOf('=');
403       if (p == -1)
404       {
405         System.err.println("Ignoring invalid setprop argument : " + defs);
406       }
407       else
408       {
409         System.out.println("Executing setprop argument: " + defs);
410         if (isJS)
411         {
412           Cache.setProperty(defs.substring(0, p), defs.substring(p + 1));
413         }
414       }
415       defs = aparser.getValue(ArgsParser.SETPROP);
416     }
417     System.setProperty("http.agent",
418             "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown"));
419     try
420     {
421       Cache.initLogger();
422     } catch (NoClassDefFoundError error)
423     {
424       error.printStackTrace();
425       System.out.println("\nEssential logging libraries not found."
426               + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview");
427       System.exit(0);
428     }
429
430     desktop = null;
431
432     try
433     {
434       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
435     } catch (Exception ex)
436     {
437       System.err.println("Unexpected Look and Feel Exception");
438       ex.printStackTrace();
439     }
440     if (Platform.isAMacAndNotJS())
441     {
442
443       LookAndFeel lookAndFeel = ch.randelshofer.quaqua.QuaquaManager
444               .getLookAndFeel();
445       System.setProperty("com.apple.mrj.application.apple.menu.about.name",
446               "Jalview");
447       System.setProperty("apple.laf.useScreenMenuBar", "true");
448       if (lookAndFeel != null)
449       {
450         try
451         {
452           UIManager.setLookAndFeel(lookAndFeel);
453         } catch (Throwable e)
454         {
455           System.err.println(
456                   "Failed to set QuaQua look and feel: " + e.toString());
457         }
458       }
459       if (lookAndFeel == null
460               || !(lookAndFeel.getClass().isAssignableFrom(
461                       UIManager.getLookAndFeel().getClass()))
462               || !UIManager.getLookAndFeel().getClass().toString()
463                       .toLowerCase().contains("quaqua"))
464       {
465         try
466         {
467           System.err.println(
468                   "Quaqua LaF not available on this plaform. Using VAqua(4).\nSee https://issues.jalview.org/browse/JAL-2976");
469           UIManager.setLookAndFeel("org.violetlib.aqua.AquaLookAndFeel");
470         } catch (Throwable e)
471         {
472           System.err.println(
473                   "Failed to reset look and feel: " + e.toString());
474         }
475       }
476     }
477
478     /*
479      * configure 'full' SO model if preferences say to, 
480      * else use the default (SO Lite)
481      */
482     if (Cache.getDefault(Preferences.USE_FULL_SO, false))
483     {
484       SequenceOntologyFactory.setSequenceOntology(new SequenceOntology());
485     }
486
487     if (!headless)
488     {
489       desktop = Desktop.getInstance();
490       desktop.setInBatchMode(true); // indicate we are starting up
491       desktop.setVisible(true);
492
493       if (!isJS)
494       /**
495        * Java only
496        * 
497        * @j2sIgnore
498        */
499       {
500         desktop.startServiceDiscovery();
501         if (!aparser.contains(ArgsParser.NOUSAGESTATS))
502         {
503           startUsageStats(desktop);
504         }
505         else
506         {
507           System.err.println("CMD [-nousagestats] executed successfully!");
508         }
509
510         if (!aparser.contains(ArgsParser.NOQUESTIONNAIRE))
511         {
512           String url = aparser.getValue(ArgsParser.QUESTIONNAIRE);
513           if (url != null)
514           {
515             // Start the desktop questionnaire prompter with the specified
516             // questionnaire
517             Cache.log.debug("Starting questionnaire url at " + url);
518             desktop.checkForQuestionnaire(url);
519             System.out.println("CMD questionnaire[-" + url
520                     + "] executed successfully!");
521           }
522           else
523           {
524             if (Cache.getProperty(Preferences.NOQUESTIONNAIRES) == null)
525             {
526               // Start the desktop questionnaire prompter with the specified
527               // questionnaire
528               // String defurl =
529               // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
530               // //
531               String defurl = "http://www.jalview.org/cgi-bin/questionnaire.pl";
532               Cache.log.debug(
533                       "Starting questionnaire with default url: " + defurl);
534               desktop.checkForQuestionnaire(defurl);
535             }
536           }
537         }
538         else
539         {
540           System.err
541                   .println("CMD [-noquestionnaire] executed successfully!");
542         }
543
544         if (!aparser.contains(ArgsParser.NONEWS))
545         {
546           desktop.checkForNews();
547         }
548
549         BioJsHTMLOutput.updateBioJS();
550       }
551     }
552
553     parseArguments(aparser, true);
554   }
555
556   private Object parseArguments(ArgsParser aparser, boolean isStartup)
557   {
558     boolean isJS = Platform.isJS();
559
560     Desktop desktop = (headless ? null : Desktop.getInstance());
561     // script to execute after all loading is
562     // completed one way or another
563     // extract groovy argument and execute if necessary
564     String groovyscript = (isJS ? null
565             : aparser.getValue(ArgsParser.GROOVY, true));
566     String file = aparser.getValue(ArgsParser.OPEN, true);
567     // BH this here to allow split frame; not working as of 5/17/2019
568     String file2 = aparser.getValue(ArgsParser.OPEN2, true);
569     String fileFormat = (isJavaAppletTag
570             ? aparser.getAppletValue("format", null)
571             : null);
572
573     FileFormatI format = null;
574     DataSourceType protocol = null;
575
576     if (file == null && desktop == null)
577     {
578       System.out.println("No files to open!");
579       System.exit(1);
580     }
581     boolean haveImport = checkStartVamas(aparser);
582     // Finally, deal with the remaining input data.
583     long progress = -1;
584     if (file == null && isJavaAppletTag)
585     {
586       // Maybe the sequences are added as parameters
587       StringBuffer data = new StringBuffer("PASTE");
588       int i = 1;
589       while ((file = aparser.getAppletValue("sequence" + i, null)) != null)
590       {
591         data.append(file.toString() + "\n");
592         i++;
593       }
594       if (data.length() > 5)
595       {
596         file = data.toString();
597       }
598     }
599
600     if (file != null)
601     {
602       if (!headless)
603       {
604         desktop.setProgressBar(
605                 MessageManager
606                         .getString("status.processing_commandline_args"),
607                 progress = System.currentTimeMillis());
608       }
609
610       if (!isJS)
611       /**
612        * ignore in JavaScript -- can't just check file existence - could load
613        * it?
614        * 
615        * @j2sIgnore
616        */
617       {
618         if (!file.startsWith("http://") && !file.startsWith("https://"))
619         // BH 2019 added https check for Java
620         {
621           if (!(new File(file)).exists())
622           {
623             System.out.println("Can't find " + file);
624             if (headless)
625             {
626               System.exit(1);
627             }
628           }
629         }
630       }
631
632       protocol = AppletFormatAdapter.checkProtocol(file);
633
634       try
635       {
636         format = (isJavaAppletTag && fileFormat != null
637                 ? FileFormats.getInstance().forName(fileFormat)
638                 : null);
639         if (format == null)
640         {
641           format = new IdentifyFile().identify(file, protocol);
642         }
643       } catch (FileFormatException e1)
644       {
645         // TODO ?
646       }
647
648       AlignFrame af = new FileLoader(!headless).loadFileWaitTillLoaded(file,
649               protocol, format);
650       if (af == null)
651       {
652         System.out.println("error");
653       }
654       else
655       {
656         System.out
657                 .println("CMD [-open " + file + "] executed successfully!");
658         if (file2 != null)
659         {
660           protocol = AppletFormatAdapter.checkProtocol(file2);
661           try
662           {
663             format = new IdentifyFile().identify(file2, protocol);
664           } catch (FileFormatException e1)
665           {
666             // TODO ?
667           }
668           AlignFrame af2 = new FileLoader(!headless)
669                   .loadFileWaitTillLoaded(file2, protocol, format);
670           if (af2 == null)
671           {
672             System.out.println("error");
673           }
674           else
675           {
676             AlignViewport.openLinkedAlignmentAs(af,
677                     af.getViewport().getAlignment(),
678                     af2.getViewport().getAlignment(), "",
679                     AlignViewport.SPLIT_FRAME);
680             System.out.println(
681                     "CMD [-open2 " + file2 + "] executed successfully!");
682           }
683         }
684
685         setCurrentAlignFrame(af);
686
687         // TODO: file2 How to implement file2 for the applet spit screen?
688
689         String data = aparser.getValue(ArgsParser.COLOUR, true);
690         if (data != null)
691         {
692           data.replaceAll("%20", " ");
693
694           ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
695                   af.getViewport(), af.getViewport().getAlignment(), data);
696
697           if (cs != null)
698           {
699             System.out.println(
700                     "CMD [-color " + data + "] executed successfully!");
701           }
702           af.changeColour(cs);
703         }
704
705         // Must maintain ability to use the groups flag
706         data = aparser.getValue(ArgsParser.GROUPS, true);
707         if (data != null)
708         {
709           af.parseFeaturesFile(data,
710                   AppletFormatAdapter.checkProtocol(data));
711           // System.out.println("Added " + data);
712           System.out.println(
713                   "CMD groups[-" + data + "]  executed successfully!");
714         }
715         data = aparser.getValue(ArgsParser.FEATURES, true);
716         if (data != null)
717         {
718           af.parseFeaturesFile(data,
719                   AppletFormatAdapter.checkProtocol(data));
720           // System.out.println("Added " + data);
721           System.out.println(
722                   "CMD [-features " + data + "]  executed successfully!");
723         }
724
725         data = aparser.getValue(ArgsParser.ANNOTATIONS, true);
726         if (data != null)
727         {
728           af.loadJalviewDataFile(data, null, null, null);
729           // System.out.println("Added " + data);
730           System.out.println(
731                   "CMD [-annotations " + data + "] executed successfully!");
732         }
733         // set or clear the sortbytree flag.
734         if (aparser.contains(ArgsParser.SORTBYTREE))
735         {
736           af.getViewport().setSortByTree(true);
737           if (af.getViewport().getSortByTree())
738           {
739             System.out.println("CMD [-sortbytree] executed successfully!");
740           }
741         }
742         if (aparser.contains(ArgsParser.NOANNOTATION)
743                 || aparser.contains(ArgsParser.NOANNOTATION2))
744         {
745           af.getViewport().setShowAnnotation(false);
746           if (!af.getViewport().isShowAnnotation())
747           {
748             System.out.println("CMD no-annotation executed successfully!");
749           }
750         }
751         if (aparser.contains(ArgsParser.NOSORTBYTREE))
752         {
753           af.getViewport().setSortByTree(false);
754           if (!af.getViewport().getSortByTree())
755           {
756             System.out
757                     .println("CMD [-nosortbytree] executed successfully!");
758           }
759         }
760         data = aparser.getValue(ArgsParser.TREE, true);
761         if (data != null)
762         {
763           try
764           {
765             System.out.println(
766                     "CMD [-tree " + data + "] executed successfully!");
767             NewickFile nf = new NewickFile(data,
768                     AppletFormatAdapter.checkProtocol(data));
769             af.getViewport()
770                     .setCurrentTree(af.showNewickTree(nf, data).getTree());
771           } catch (IOException ex)
772           {
773             System.err.println("Couldn't add tree " + data);
774             ex.printStackTrace(System.err);
775           }
776         }
777         // TODO - load PDB structure(s) to alignment JAL-629
778         // (associate with identical sequence in alignment, or a specified
779         // sequence)
780         if (isJavaAppletTag)
781         {
782           loadAppletParams(aparser, af);
783         }
784         else if (!isJS)
785         /**
786          * Java only
787          * 
788          * @j2sIgnore
789          */
790         {
791           if (groovyscript != null)
792           {
793             // Execute the groovy script after we've done all the rendering
794             // stuff
795             // and before any images or figures are generated.
796             System.out.println("Executing script " + groovyscript);
797             executeGroovyScript(groovyscript, af);
798             System.out.println("CMD groovy[" + groovyscript
799                     + "] executed successfully!");
800             groovyscript = null;
801           }
802           checkOutputFile(aparser, af, format);
803           while (aparser.getSize() > 0)
804           {
805             System.out.println("Unknown arg: " + aparser.nextValue());
806           }
807         }
808       }
809     }
810     AlignFrame startUpAlframe = null;
811     // We'll only open the default file if the desktop is visible.
812     // And the user
813     // ////////////////////
814
815     if (!isJS && !headless && file == null && !haveImport
816             && jalview.bin.Cache.getDefault("SHOW_STARTUP_FILE", true))
817     /**
818      * Java only
819      * 
820      * @j2sIgnore
821      */
822     {
823       file = jalview.bin.Cache.getDefault("STARTUP_FILE",
824               jalview.bin.Cache.getDefault("www.jalview.org",
825                       "http://www.jalview.org")
826                       + "/examples/exampleFile_2_7.jar");
827       if (file.equals(
828               "http://www.jalview.org/examples/exampleFile_2_3.jar"))
829       {
830         // hardwire upgrade of the startup file
831         file.replace("_2_3.jar", "_2_7.jar");
832         // and remove the stale setting
833         jalview.bin.Cache.removeProperty("STARTUP_FILE");
834       }
835
836       protocol = DataSourceType.FILE;
837
838       if (file.indexOf("http:") > -1)
839       {
840         protocol = DataSourceType.URL;
841       }
842
843       if (file.endsWith(".jar"))
844       {
845         format = FileFormat.Jalview;
846       }
847       else
848       {
849         try
850         {
851           format = new IdentifyFile().identify(file, protocol);
852         } catch (FileFormatException e)
853         {
854           // TODO what?
855         }
856       }
857
858       startUpAlframe = new FileLoader(!headless)
859               .loadFileWaitTillLoaded(file, protocol, format);
860       // extract groovy arguments before anything else.
861     }
862
863     // Once all other stuff is done, execute any groovy scripts (in order)
864     if (groovyscript != null)
865     {
866       if (Cache.groovyJarsPresent())
867       {
868         System.out.println("Executing script " + groovyscript);
869         executeGroovyScript(groovyscript, startUpAlframe);
870       }
871       else
872       {
873         System.err.println(
874                 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
875                         + groovyscript);
876       }
877     }
878     // and finally, turn off batch mode indicator - if the desktop still exists
879     if (desktop != null)
880     {
881       if (progress != -1)
882       {
883         desktop.setProgressBar(null, progress);
884       }
885       desktop.setInBatchMode(false);
886     }
887
888     return null;
889   }
890
891   private boolean checkStartVamas(ArgsParser aparser)
892   {
893     String vamsasImport = aparser.getValue(ArgsParser.VDOC);
894     String vamsasSession = aparser.getValue(ArgsParser.VSESS);
895     if (vamsasImport == null && vamsasSession == null)
896     {
897       return false;
898     }
899     if (desktop == null || headless)
900     {
901       System.out.println(
902               "Headless vamsas sessions not yet supported. Sorry.");
903       System.exit(1);
904     }
905     boolean haveImport = (vamsasImport != null);
906     if (haveImport)
907     {
908       // if we have a file, start a new session and import it.
909       boolean inSession = false;
910       try
911       {
912         DataSourceType viprotocol = AppletFormatAdapter
913                 .checkProtocol(vamsasImport);
914         if (viprotocol == DataSourceType.FILE)
915         {
916           inSession = desktop.vamsasImport(new File(vamsasImport));
917         }
918         else if (viprotocol == DataSourceType.URL)
919         {
920           inSession = desktop.vamsasImport(new URL(vamsasImport));
921         }
922
923       } catch (Exception e)
924       {
925         System.err.println("Exeption when importing " + vamsasImport
926                 + " as a vamsas document.");
927         e.printStackTrace();
928       }
929       if (!inSession)
930       {
931         System.err.println("Failed to import " + vamsasImport
932                 + " as a vamsas document.");
933       }
934       else
935       {
936         System.out.println("Imported Successfully into new session "
937                 + desktop.getVamsasApplication().getCurrentSession());
938       }
939     }
940     if (vamsasSession != null)
941     {
942       if (vamsasImport != null)
943       {
944         // close the newly imported session and import the Jalview specific
945         // remnants into the new session later on.
946         desktop.vamsasStop_actionPerformed(null);
947       }
948       // now join the new session
949       try
950       {
951         if (desktop.joinVamsasSession(vamsasSession))
952         {
953           System.out.println(
954                   "Successfully joined vamsas session " + vamsasSession);
955         }
956         else
957         {
958           System.err.println("WARNING: Failed to join vamsas session "
959                   + vamsasSession);
960         }
961       } catch (Exception e)
962       {
963         System.err.println(
964                 "ERROR: Failed to join vamsas session " + vamsasSession);
965         e.printStackTrace();
966       }
967       if (vamsasImport != null)
968       {
969         // the Jalview specific remnants can now be imported into the new
970         // session at the user's leisure.
971         Cache.log.info(
972                 "Skipping Push for import of data into existing vamsas session.");
973         // TODO:
974         // enable
975         // this
976         // when
977         // debugged
978         // desktop.getVamsasApplication().push_update();
979       }
980     }
981     return haveImport;
982   }
983
984   private void checkOutputFile(ArgsParser aparser, AlignFrame af,
985           FileFormatI format)
986   {
987     String imageName = "unnamed.png";
988     while (aparser.getSize() > 1)
989     {
990       // PNG filename
991       // SVG filename
992       // HTML filename
993       // biojsmsa filename
994       String outputFormat = aparser.nextValue();
995       String file = aparser.nextValue();
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
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(AlignFrameI alf)
1584   {
1585     return getSelectedSequencesFrom(alf, null);
1586   }
1587
1588   /**
1589    * 
1590    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
1591    *      .AlignFrame, java.lang.String)
1592    */
1593   @Override
1594   public String getSelectedSequencesFrom(AlignFrameI alf, String sep)
1595   {
1596     return appLoader.getSelectedSequencesFrom(alf, sep);
1597   }
1598
1599   /**
1600    * 
1601    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
1602    *      .AlignFrame, java.lang.String)
1603    */
1604   @Override
1605   public void highlight(String sequenceId, String position,
1606           String alignedPosition)
1607   {
1608     highlightIn(getCurrentAlignFrame(), sequenceId, position,
1609             alignedPosition);
1610   }
1611
1612   @Override
1613   public void highlightIn(AlignFrameI alf, String sequenceId,
1614           String position, String alignedPosition)
1615   {
1616     appLoader.highlightIn(alf, sequenceId, position, alignedPosition);
1617   }
1618
1619   @Override
1620   public void select(String sequenceIds, String columns)
1621   {
1622     selectIn(getCurrentAlignFrame(), sequenceIds, columns, null);
1623   }
1624
1625   @Override
1626   public void select(String sequenceIds, String columns, String sep)
1627   {
1628     selectIn(getCurrentAlignFrame(), sequenceIds, columns, sep);
1629   }
1630
1631   @Override
1632   public void selectIn(AlignFrameI alf, String sequenceIds, String columns)
1633   {
1634     selectIn(alf, sequenceIds, columns, null);
1635   }
1636
1637   @Override
1638   public void selectIn(AlignFrameI alf, String sequenceIds, String columns,
1639           String sep)
1640   {
1641     appLoader.selectIn(alf, sequenceIds, columns, sep);
1642   }
1643
1644   @Override
1645   public String getSelectedSequencesAsAlignment(String format,
1646           String suffix)
1647   {
1648     return getSelectedSequencesAsAlignmentFrom(getCurrentAlignFrame(),
1649             format, suffix);
1650   }
1651
1652   @Override
1653   public String getSelectedSequencesAsAlignmentFrom(AlignFrameI alf,
1654           String format, String sep)
1655   {
1656     return appLoader.getSelectedSequencesAsAlignmentFrom(alf, format, sep);
1657   }
1658
1659   @Override
1660   public String getAlignmentOrder()
1661   {
1662     return getAlignmentFrom(getCurrentAlignFrame(), null);
1663   }
1664
1665   @Override
1666   public String getAlignmentOrderFrom(AlignFrameI alf)
1667   {
1668     return getAlignmentFrom(alf, null);
1669   }
1670
1671   @Override
1672   public String getAlignmentOrderFrom(AlignFrameI alf, String sep)
1673   {
1674     return appLoader.getAlignmentOrderFrom(alf, sep);
1675   }
1676
1677   @Override
1678   public String orderBy(String order, String undoName)
1679   {
1680     return orderBy(order, undoName, null);
1681   }
1682
1683   @Override
1684   public String orderBy(String order, String undoName, String sep)
1685   {
1686     return orderAlignmentBy(getCurrentAlignFrame(), order, undoName, sep);
1687   }
1688
1689   @Override
1690   public String orderAlignmentBy(AlignFrameI alf, String order,
1691           String undoName, String sep)
1692   {
1693     return appLoader.orderAlignmentBy(alf, order, undoName, sep);
1694   }
1695
1696   @Override
1697   public String getAlignment(String format)
1698   {
1699     return getAlignmentFrom(null, format, null);
1700   }
1701
1702   @Override
1703   public String getAlignmentFrom(AlignFrameI alf, String format)
1704   {
1705     return getAlignmentFrom(alf, format, null);
1706   }
1707
1708   @Override
1709   public String getAlignment(String format, String suffix)
1710   {
1711     return getAlignmentFrom(getCurrentAlignFrame(), format, suffix);
1712   }
1713
1714   @Override
1715   public String getAlignmentFrom(AlignFrameI alf, String format,
1716           String suffix)
1717   {
1718     return appLoader.getAlignmentFrom(alf, format, suffix);
1719   }
1720
1721   @Override
1722   public void loadAnnotation(String annotation)
1723   {
1724     loadAnnotationFrom(getCurrentAlignFrame(), annotation);
1725   }
1726
1727   @Override
1728   public void loadAnnotationFrom(AlignFrameI alf, String annotation)
1729   {
1730     appLoader.loadAnnotationFrom(alf, annotation);
1731   }
1732
1733   @Override
1734   public void loadFeatures(String features, boolean autoenabledisplay)
1735   {
1736     loadFeaturesFrom(currentAlignFrame, features, autoenabledisplay);
1737   }
1738
1739   @Override
1740   public boolean loadFeaturesFrom(AlignFrameI alf, String features,
1741           boolean autoenabledisplay)
1742   {
1743     return appLoader.loadFeaturesFrom(alf, features, autoenabledisplay);
1744   }
1745
1746   @Override
1747   public String getFeatures(String format)
1748   {
1749     return getFeaturesFrom(getCurrentAlignFrame(), format);
1750   }
1751
1752   @Override
1753   public String getFeaturesFrom(AlignFrameI alf, String format)
1754   {
1755     return appLoader.getFeaturesFrom(alf, format);
1756   }
1757
1758   @Override
1759   public String getAnnotation()
1760   {
1761     return getAnnotationFrom(getCurrentAlignFrame());
1762   }
1763
1764   @Override
1765   public String getAnnotationFrom(AlignFrameI alf)
1766   {
1767     return appLoader.getAnnotationFrom(alf);
1768   }
1769
1770   @Override
1771   public AlignFrameI newView()
1772   {
1773     return newViewFrom(getCurrentAlignFrame(), null);
1774   }
1775
1776   @Override
1777   public AlignFrameI newView(String name)
1778   {
1779     return newViewFrom(getCurrentAlignFrame(), name);
1780   }
1781
1782   @Override
1783   public AlignFrameI newViewFrom(AlignFrameI alf)
1784   {
1785     return newViewFrom(alf, null);
1786   }
1787
1788   @Override
1789   public AlignFrameI newViewFrom(AlignFrameI alf, String name)
1790   {
1791     return appLoader.newViewFrom(alf, name);
1792   }
1793
1794   @Override
1795   public AlignFrameI loadAlignment(String text, String title)
1796   {
1797     return appLoader.loadAlignment(text, AlignFrame.DEFAULT_WIDTH,
1798             AlignFrame.DEFAULT_HEIGHT, title);
1799   }
1800
1801   @Override
1802   public boolean addPdbFile(AlignFrameI alFrame, String sequenceId,
1803           String pdbEntryString, String pdbFile)
1804   {
1805     return appLoader.addPdbFile(alFrame, sequenceId, pdbEntryString,
1806             pdbFile);
1807   }
1808
1809   @Override
1810   public void scrollViewToIn(AlignFrameI alf, String topRow,
1811           String leftHandColumn)
1812   {
1813     appLoader.scrollViewToIn(alf, topRow, leftHandColumn);
1814   }
1815
1816   @Override
1817   public void scrollViewToRowIn(AlignFrameI alf, String topRow)
1818   {
1819     appLoader.scrollViewToRowIn(alf, topRow);
1820   }
1821
1822   @Override
1823   public void scrollViewToColumnIn(AlignFrameI alf, String leftHandColumn)
1824   {
1825     appLoader.scrollViewToColumnIn(alf, leftHandColumn);
1826   }
1827
1828   @Override
1829   public String getFeatureGroups()
1830   {
1831     return getFeatureGroupsOn(getCurrentAlignFrame());
1832   }
1833
1834   @Override
1835   public String getFeatureGroupsOn(AlignFrameI alf)
1836   {
1837     return appLoader.getFeatureGroupsOn(alf);
1838   }
1839
1840   @Override
1841   public String getFeatureGroupsOfState(boolean visible)
1842   {
1843     return getFeatureGroupsOfStateOn(getCurrentAlignFrame(), visible);
1844   }
1845
1846   @Override
1847   public String getFeatureGroupsOfStateOn(AlignFrameI alf, boolean visible)
1848   {
1849     return appLoader.getFeatureGroupsOfStateOn(alf, visible);
1850   }
1851
1852   @Override
1853   public void setFeatureGroupStateOn(AlignFrameI alf, String groups,
1854           boolean state)
1855   {
1856     setFeatureGroupStateOn(alf, groups, state);
1857   }
1858
1859   @Override
1860   public void setFeatureGroupState(String groups, boolean state)
1861   {
1862     appLoader.setFeatureGroupStateOn(getCurrentAlignFrame(), groups, state);
1863   }
1864
1865   @Override
1866   public String getSeparator()
1867   {
1868     return appLoader.getSeparator();
1869   }
1870
1871   @Override
1872   public void setSeparator(String separator)
1873   {
1874     appLoader.setSeparator(separator);
1875   }
1876
1877   @Override
1878   public String getJsMessage(String messageclass, String viewId)
1879   {
1880     // see http://www.jalview.org/examples/jalviewLiteJs.html
1881     return null;
1882   }
1883
1884   /**
1885    * Open a new Tree panel on the desktop statically. Params are standard (not
1886    * set by Groovy). No dialog is opened.
1887    * 
1888    * @param af
1889    * @param treeType
1890    * @param modelName
1891    * @return null, or the string "label.you_need_at_least_n_sequences" if number
1892    *         of sequences selected is inappropriate
1893    */
1894   @Override
1895   public Object openTreePanel(AlignFrame af, String treeType,
1896           String modelName)
1897   {
1898     return CalculationChooser.openTreePanel(af, treeType, modelName, null);
1899   }
1900
1901   /**
1902    * public static method for JalviewJS API to open a PCAPanel without
1903    * necessarily using a dialog.
1904    * 
1905    * @param af
1906    * @param modelName
1907    * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences"
1908    *         if number of sequences selected is inappropriate
1909    */
1910   @Override
1911   public Object openPcaPanel(AlignFrame af, String modelName)
1912   {
1913     return CalculationChooser.openPcaPanel(af, modelName, null);
1914   }
1915
1916   @Override
1917   public String getSelectedSequencesAsAlignment(String format,
1918           boolean suffix)
1919   {
1920     return getSelectedSequencesAsAlignmentFrom(getCurrentAlignFrame(),
1921             format, suffix);
1922   }
1923
1924   @Override
1925   public String getSelectedSequencesAsAlignmentFrom(AlignFrameI alf,
1926           String format, boolean suffix)
1927   {
1928     return appLoader.getSelectedSequencesAsAlignmentFrom(alf, format,
1929             "" + suffix);
1930   }
1931
1932   @Override
1933   public String arrayToSeparatorList(String[] array)
1934   {
1935     return appLoader.arrayToSeparatorList(array);
1936   }
1937
1938   @Override
1939   public String[] separatorListToArray(String list)
1940   {
1941     return appLoader.separatorListToArray(list);
1942   }
1943
1944   //// probably not needed in JalviewJS -- From when Jmol and Jalview did not
1945   //// have a direct connection?
1946
1947   @Override
1948   public void setMouseoverListener(String listener)
1949   {
1950     // TODO Auto-generated method stub
1951
1952   }
1953
1954   @Override
1955   public void setMouseoverListener(AlignFrameI af, String listener)
1956   {
1957     // TODO Auto-generated method stub
1958
1959   }
1960
1961   @Override
1962   public void setSelectionListener(String listener)
1963   {
1964     // TODO Auto-generated method stub
1965
1966   }
1967
1968   @Override
1969   public void setSelectionListener(AlignFrameI af, String listener)
1970   {
1971     // TODO Auto-generated method stub
1972
1973   }
1974
1975   @Override
1976   public void setStructureListener(String listener, String modelSet)
1977   {
1978     // TODO Auto-generated method stub
1979
1980   }
1981
1982   @Override
1983   public void removeJavascriptListener(AlignFrameI af, String listener)
1984   {
1985     // TODO Auto-generated method stub
1986
1987   }
1988
1989   @Override
1990   public void mouseOverStructure(String pdbResNum, String chain,
1991           String pdbfile)
1992   {
1993     // TODO Auto-generated method stub
1994
1995   }
1996
1997 }