047356bf088457a818a3dcf09806da7db8a7eeeb
[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,
557           boolean isStartup)
558   {
559     boolean isJS = Platform.isJS();
560
561     Desktop desktop = (headless ? null : Desktop.getInstance());
562     // script to execute after all loading is
563     // completed one way or another
564     // extract groovy argument and execute if necessary
565     String groovyscript = (isJS ? null
566             : aparser.getValue(ArgsParser.GROOVY, true));
567     String file = aparser.getValue(ArgsParser.OPEN, true);
568     // BH this here to allow split frame; not working as of 5/17/2019
569     String file2 = aparser.getValue(ArgsParser.OPEN2, true);
570     String fileFormat = (isJavaAppletTag
571             ? aparser.getAppletValue("format", null)
572             : null);
573
574     FileFormatI format = null;
575     DataSourceType protocol = null;
576
577     if (file == null && desktop == null)
578     {
579       System.out.println("No files to open!");
580       System.exit(1);
581     }
582     boolean haveImport = checkStartVamas(aparser);
583     // Finally, deal with the remaining input data.
584     long progress = -1;
585     if (file == null && isJavaAppletTag)
586     {
587       // Maybe the sequences are added as parameters
588       StringBuffer data = new StringBuffer("PASTE");
589       int i = 1;
590       while ((file = aparser.getAppletValue("sequence" + i, null)) != null)
591       {
592         data.append(file.toString() + "\n");
593         i++;
594       }
595       if (data.length() > 5)
596       {
597         file = data.toString();
598       }
599     }
600
601     if (file != null)
602     {
603       if (!headless)
604       {
605         desktop.setProgressBar(
606                 MessageManager
607                         .getString("status.processing_commandline_args"),
608                 progress = System.currentTimeMillis());
609       }
610
611       if (!isJS)
612       /**
613        * ignore in JavaScript -- can't just check file existence - could load
614        * it?
615        * 
616        * @j2sIgnore
617        */
618       {
619         if (!file.startsWith("http://") && !file.startsWith("https://"))
620         // BH 2019 added https check for Java
621         {
622           if (!(new File(file)).exists())
623           {
624             System.out.println("Can't find " + file);
625             if (headless)
626             {
627               System.exit(1);
628             }
629           }
630         }
631       }
632
633       protocol = AppletFormatAdapter.checkProtocol(file);
634
635       try
636       {
637         format = (isJavaAppletTag && fileFormat != null
638                 ? FileFormats.getInstance().forName(fileFormat)
639                 : null);
640         if (format == null)
641         {
642           format = new IdentifyFile().identify(file, protocol);
643         }
644       } catch (FileFormatException e1)
645       {
646         // TODO ?
647       }
648
649       AlignFrame af = new FileLoader(!headless).loadFileWaitTillLoaded(file,
650               protocol, format);
651       if (af == null)
652       {
653         System.out.println("error");
654       }
655       else
656       {
657         System.out
658                 .println("CMD [-open " + file + "] executed successfully!");
659         if (file2 != null)
660         {
661           protocol = AppletFormatAdapter.checkProtocol(file2);
662           try
663           {
664             format = new IdentifyFile().identify(file2, protocol);
665           } catch (FileFormatException e1)
666           {
667             // TODO ?
668           }
669           AlignFrame af2 = new FileLoader(!headless)
670                   .loadFileWaitTillLoaded(file2, protocol, format);
671           if (af2 == null)
672           {
673             System.out.println("error");
674           }
675           else
676           {
677             AlignViewport.openLinkedAlignmentAs(af,
678                     af.getViewport().getAlignment(),
679                     af2.getViewport().getAlignment(), "",
680                     AlignViewport.SPLIT_FRAME);
681             System.out.println(
682                     "CMD [-open2 " + file2 + "] executed successfully!");
683           }
684         }
685
686         setCurrentAlignFrame(af);
687
688         // TODO: file2 How to implement file2 for the applet spit screen?
689
690         String data = aparser.getValue(ArgsParser.COLOUR, true);
691         if (data != null)
692         {
693           data.replaceAll("%20", " ");
694
695           ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
696                   af.getViewport(), af.getViewport().getAlignment(), data);
697
698           if (cs != null)
699           {
700             System.out.println(
701                     "CMD [-color " + data + "] executed successfully!");
702           }
703           af.changeColour(cs);
704         }
705
706         // Must maintain ability to use the groups flag
707         data = aparser.getValue(ArgsParser.GROUPS, true);
708         if (data != null)
709         {
710           af.parseFeaturesFile(data,
711                   AppletFormatAdapter.checkProtocol(data));
712           // System.out.println("Added " + data);
713           System.out.println(
714                   "CMD groups[-" + data + "]  executed successfully!");
715         }
716         data = aparser.getValue(ArgsParser.FEATURES, true);
717         if (data != null)
718         {
719           af.parseFeaturesFile(data,
720                   AppletFormatAdapter.checkProtocol(data));
721           // System.out.println("Added " + data);
722           System.out.println(
723                   "CMD [-features " + data + "]  executed successfully!");
724         }
725
726         data = aparser.getValue(ArgsParser.ANNOTATIONS, true);
727         if (data != null)
728         {
729           af.loadJalviewDataFile(data, null, null, null);
730           // System.out.println("Added " + data);
731           System.out.println(
732                   "CMD [-annotations " + data + "] executed successfully!");
733         }
734         // set or clear the sortbytree flag.
735         if (aparser.contains(ArgsParser.SORTBYTREE))
736         {
737           af.getViewport().setSortByTree(true);
738           if (af.getViewport().getSortByTree())
739           {
740             System.out.println("CMD [-sortbytree] executed successfully!");
741           }
742         }
743         if (aparser.contains(ArgsParser.NOANNOTATION)
744                 || aparser.contains(ArgsParser.NOANNOTATION2))
745         {
746           af.getViewport().setShowAnnotation(false);
747           if (!af.getViewport().isShowAnnotation())
748           {
749             System.out.println("CMD no-annotation executed successfully!");
750           }
751         }
752         if (aparser.contains(ArgsParser.NOSORTBYTREE))
753         {
754           af.getViewport().setSortByTree(false);
755           if (!af.getViewport().getSortByTree())
756           {
757             System.out
758                     .println("CMD [-nosortbytree] executed successfully!");
759           }
760         }
761         data = aparser.getValue(ArgsParser.TREE, true);
762         if (data != null)
763         {
764           try
765           {
766             System.out.println(
767                     "CMD [-tree " + data + "] executed successfully!");
768             NewickFile nf = new NewickFile(data,
769                     AppletFormatAdapter.checkProtocol(data));
770             af.getViewport()
771                     .setCurrentTree(af.showNewickTree(nf, data).getTree());
772           } catch (IOException ex)
773           {
774             System.err.println("Couldn't add tree " + data);
775             ex.printStackTrace(System.err);
776           }
777         }
778         // TODO - load PDB structure(s) to alignment JAL-629
779         // (associate with identical sequence in alignment, or a specified
780         // sequence)
781         if (isJavaAppletTag)
782         {
783           loadAppletParams(aparser, af);
784         }
785         else if (!isJS)
786         /**
787          * Java only
788          * 
789          * @j2sIgnore
790          */
791         {
792           if (groovyscript != null)
793           {
794             // Execute the groovy script after we've done all the rendering
795             // stuff
796             // and before any images or figures are generated.
797             System.out.println("Executing script " + groovyscript);
798             executeGroovyScript(groovyscript, af);
799             System.out.println("CMD groovy[" + groovyscript
800                     + "] executed successfully!");
801             groovyscript = null;
802           }
803           checkOutputFile(aparser, af, format);
804           while (aparser.getSize() > 0)
805           {
806             System.out.println("Unknown arg: " + aparser.nextValue());
807           }
808         }
809       }
810     }
811     AlignFrame startUpAlframe = null;
812     // We'll only open the default file if the desktop is visible.
813     // And the user
814     // ////////////////////
815
816     if (!isJS && !headless && file == null && !haveImport
817             && jalview.bin.Cache.getDefault("SHOW_STARTUP_FILE", true))
818     /**
819      * Java only
820      * 
821      * @j2sIgnore
822      */
823     {
824       file = jalview.bin.Cache.getDefault("STARTUP_FILE",
825               jalview.bin.Cache.getDefault("www.jalview.org",
826                       "http://www.jalview.org")
827                       + "/examples/exampleFile_2_7.jar");
828       if (file.equals(
829               "http://www.jalview.org/examples/exampleFile_2_3.jar"))
830       {
831         // hardwire upgrade of the startup file
832         file.replace("_2_3.jar", "_2_7.jar");
833         // and remove the stale setting
834         jalview.bin.Cache.removeProperty("STARTUP_FILE");
835       }
836
837       protocol = DataSourceType.FILE;
838
839       if (file.indexOf("http:") > -1)
840       {
841         protocol = DataSourceType.URL;
842       }
843
844       if (file.endsWith(".jar"))
845       {
846         format = FileFormat.Jalview;
847       }
848       else
849       {
850         try
851         {
852           format = new IdentifyFile().identify(file, protocol);
853         } catch (FileFormatException e)
854         {
855           // TODO what?
856         }
857       }
858
859       startUpAlframe = new FileLoader(!headless)
860               .loadFileWaitTillLoaded(file, protocol, format);
861       // extract groovy arguments before anything else.
862     }
863
864     // Once all other stuff is done, execute any groovy scripts (in order)
865     if (groovyscript != null)
866     {
867       if (Cache.groovyJarsPresent())
868       {
869         System.out.println("Executing script " + groovyscript);
870         executeGroovyScript(groovyscript, startUpAlframe);
871       }
872       else
873       {
874         System.err.println(
875                 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
876                         + groovyscript);
877       }
878     }
879     // and finally, turn off batch mode indicator - if the desktop still exists
880     if (desktop != null)
881     {
882       if (progress != -1)
883       {
884         desktop.setProgressBar(null, progress);
885       }
886       desktop.setInBatchMode(false);
887     }
888
889     return null;
890   }
891
892   private boolean checkStartVamas(ArgsParser aparser)
893   {
894     String vamsasImport = aparser.getValue(ArgsParser.VDOC);
895     String vamsasSession = aparser.getValue(ArgsParser.VSESS);
896     if (vamsasImport == null && vamsasSession == null)
897     {
898       return false;
899     }
900     if (desktop == null || headless)
901     {
902       System.out.println(
903               "Headless vamsas sessions not yet supported. Sorry.");
904       System.exit(1);
905     }
906     boolean haveImport = (vamsasImport != null);
907     if (haveImport)
908     {
909       // if we have a file, start a new session and import it.
910       boolean inSession = false;
911       try
912       {
913         DataSourceType viprotocol = AppletFormatAdapter
914                 .checkProtocol(vamsasImport);
915         if (viprotocol == DataSourceType.FILE)
916         {
917           inSession = desktop.vamsasImport(new File(vamsasImport));
918         }
919         else if (viprotocol == DataSourceType.URL)
920         {
921           inSession = desktop.vamsasImport(new URL(vamsasImport));
922         }
923
924       } catch (Exception e)
925       {
926         System.err.println("Exeption when importing " + vamsasImport
927                 + " as a vamsas document.");
928         e.printStackTrace();
929       }
930       if (!inSession)
931       {
932         System.err.println("Failed to import " + vamsasImport
933                 + " as a vamsas document.");
934       }
935       else
936       {
937         System.out.println("Imported Successfully into new session "
938                 + desktop.getVamsasApplication().getCurrentSession());
939       }
940     }
941     if (vamsasSession != null)
942     {
943       if (vamsasImport != null)
944       {
945         // close the newly imported session and import the Jalview specific
946         // remnants into the new session later on.
947         desktop.vamsasStop_actionPerformed(null);
948       }
949       // now join the new session
950       try
951       {
952         if (desktop.joinVamsasSession(vamsasSession))
953         {
954           System.out.println(
955                   "Successfully joined vamsas session " + vamsasSession);
956         }
957         else
958         {
959           System.err.println("WARNING: Failed to join vamsas session "
960                   + vamsasSession);
961         }
962       } catch (Exception e)
963       {
964         System.err.println(
965                 "ERROR: Failed to join vamsas session " + vamsasSession);
966         e.printStackTrace();
967       }
968       if (vamsasImport != null)
969       {
970         // the Jalview specific remnants can now be imported into the new
971         // session at the user's leisure.
972         Cache.log.info(
973                 "Skipping Push for import of data into existing vamsas session.");
974         // TODO:
975         // enable
976         // this
977         // when
978         // debugged
979         // desktop.getVamsasApplication().push_update();
980       }
981     }
982     return haveImport;
983   }
984
985   private void checkOutputFile(ArgsParser aparser, AlignFrame af,
986           FileFormatI format)
987   {
988     String imageName = "unnamed.png";
989     while (aparser.getSize() > 1)
990     {
991       // PNG filename
992       // SVG filename
993       // HTML filename
994       // biojsmsa filename
995       String outputFormat = aparser.nextValue();
996       String file = aparser.nextValue();
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
1019         System.out.println("Creating HTML image: " + file);
1020         continue;
1021       }
1022       else if (outputFormat.equalsIgnoreCase("biojsmsa"))
1023       {
1024         if (file == null)
1025         {
1026           System.err.println("The output html file must not be null");
1027           return;
1028         }
1029         try
1030         {
1031           BioJsHTMLOutput.refreshVersionInfo(
1032                   BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
1033         } catch (URISyntaxException e)
1034         {
1035           e.printStackTrace();
1036         }
1037         BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
1038         bjs.exportHTML(file);
1039         System.out.println("Creating BioJS MSA Viwer HTML file: " + file);
1040         continue;
1041       }
1042       else if (outputFormat.equalsIgnoreCase("imgMap"))
1043       {
1044         af.createImageMap(new File(file), imageName);
1045         System.out.println("Creating image map: " + file);
1046         continue;
1047       }
1048       else if (outputFormat.equalsIgnoreCase("eps"))
1049       {
1050         File outputFile = new File(file);
1051         System.out.println(
1052                 "Creating EPS file: " + outputFile.getAbsolutePath());
1053         af.createEPS(outputFile);
1054         continue;
1055       }
1056
1057       af.saveAlignment(file, format);
1058       if (af.isSaveAlignmentSuccessful())
1059       {
1060         System.out.println(
1061                 "Written alignment in " + format + " format to " + file);
1062       }
1063       else
1064       {
1065         System.out.println("Error writing file " + file + " in " + format
1066                 + " format!!");
1067       }
1068
1069     }
1070   }
1071
1072   private static void showUsage()
1073   {
1074     System.out.println(
1075             "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
1076                     + "-nodisplay\tRun Jalview without User Interface.\n"
1077                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
1078                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
1079                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
1080                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
1081                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
1082                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
1083                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
1084                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
1085                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
1086                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
1087                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
1088                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
1089                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
1090                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
1091                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
1092                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
1093                     + "-html FILE\tCreate HTML file from alignment.\n"
1094                     + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
1095                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
1096                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
1097                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
1098                     + "-noquestionnaire\tTurn off questionnaire check.\n"
1099                     + "-nonews\tTurn off check for Jalview news.\n"
1100                     + "-nousagestats\tTurn off google analytics tracking for this session.\n"
1101                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
1102                     // +
1103                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
1104                     // after all other properties files have been read\n\t
1105                     // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
1106                     // passed in correctly)"
1107                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
1108                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
1109                     // +
1110                     // "-vdoc vamsas-document\tImport vamsas document into new
1111                     // session or join existing session with same URN\n"
1112                     // + "-vses vamsas-session\tJoin session with given URN\n"
1113                     + "-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"
1114                     + "\n~Read documentation in Application or visit http://www.jalview.org for description of Features and Annotations file~\n\n");
1115   }
1116
1117   private static void startUsageStats(final Desktop desktop)
1118   {
1119     /**
1120      * start a User Config prompt asking if we can log usage statistics.
1121      */
1122     PromptUserConfig prompter = new PromptUserConfig(
1123             Desktop.getDesktopPane(), "USAGESTATS",
1124             "Jalview Usage Statistics",
1125             "Do you want to help make Jalview better by enabling "
1126                     + "the collection of usage statistics with Google Analytics ?"
1127                     + "\n\n(you can enable or disable usage tracking in the preferences)",
1128             new Runnable()
1129             {
1130               @Override
1131               public void run()
1132               {
1133                 Cache.log.debug(
1134                         "Initialising googletracker for usage stats.");
1135                 Cache.initGoogleTracker();
1136                 Cache.log.debug("Tracking enabled.");
1137               }
1138             }, new Runnable()
1139             {
1140               @Override
1141               public void run()
1142               {
1143                 Cache.log.debug("Not enabling Google Tracking.");
1144               }
1145             }, null, true);
1146     desktop.addDialogThread(prompter);
1147   }
1148
1149   /**
1150    * Locate the given string as a file and pass it to the groovy interpreter.
1151    * 
1152    * @param groovyscript
1153    *          the script to execute
1154    * @param jalviewContext
1155    *          the Jalview Desktop object passed in to the groovy binding as the
1156    *          'Jalview' object.
1157    */
1158   private void executeGroovyScript(String groovyscript, AlignFrame af)
1159   {
1160     /**
1161      * for scripts contained in files
1162      */
1163     File tfile = null;
1164     /**
1165      * script's URI
1166      */
1167     URL sfile = null;
1168     if (groovyscript.trim().equals("STDIN"))
1169     {
1170       // read from stdin into a tempfile and execute it
1171       try
1172       {
1173         tfile = File.createTempFile("jalview", "groovy");
1174         PrintWriter outfile = new PrintWriter(
1175                 new OutputStreamWriter(new FileOutputStream(tfile)));
1176         BufferedReader br = new BufferedReader(
1177                 new InputStreamReader(System.in));
1178         String line = null;
1179         while ((line = br.readLine()) != null)
1180         {
1181           outfile.write(line + "\n");
1182         }
1183         br.close();
1184         outfile.flush();
1185         outfile.close();
1186
1187       } catch (Exception ex)
1188       {
1189         System.err.println("Failed to read from STDIN into tempfile "
1190                 + ((tfile == null) ? "(tempfile wasn't created)"
1191                         : tfile.toString()));
1192         ex.printStackTrace();
1193         return;
1194       }
1195       try
1196       {
1197         sfile = tfile.toURI().toURL();
1198       } catch (Exception x)
1199       {
1200         System.err.println(
1201                 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1202                         + tfile.toURI());
1203         x.printStackTrace();
1204         return;
1205       }
1206     }
1207     else
1208     {
1209       try
1210       {
1211         sfile = new URI(groovyscript).toURL();
1212       } catch (Exception x)
1213       {
1214         tfile = new File(groovyscript);
1215         if (!tfile.exists())
1216         {
1217           System.err.println("File '" + groovyscript + "' does not exist.");
1218           return;
1219         }
1220         if (!tfile.canRead())
1221         {
1222           System.err.println("File '" + groovyscript + "' cannot be read.");
1223           return;
1224         }
1225         if (tfile.length() < 1)
1226         {
1227           System.err.println("File '" + groovyscript + "' is empty.");
1228           return;
1229         }
1230         try
1231         {
1232           sfile = tfile.getAbsoluteFile().toURI().toURL();
1233         } catch (Exception ex)
1234         {
1235           System.err.println("Failed to create a file URL for "
1236                   + tfile.getAbsoluteFile());
1237           return;
1238         }
1239       }
1240     }
1241     try
1242     {
1243       Map<String, Object> vbinding = new HashMap<>();
1244       vbinding.put("Jalview", this);
1245       if (af != null)
1246       {
1247         vbinding.put("currentAlFrame", af);
1248       }
1249       Binding gbinding = new Binding(vbinding);
1250       GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1251       gse.run(sfile.toString(), gbinding);
1252       if ("STDIN".equals(groovyscript))
1253       {
1254         // delete temp file that we made -
1255         // only if it was successfully executed
1256         tfile.delete();
1257       }
1258     } catch (Exception e)
1259     {
1260       System.err.println("Exception Whilst trying to execute file " + sfile
1261               + " as a groovy script.");
1262       e.printStackTrace(System.err);
1263
1264     }
1265   }
1266
1267   public AlignFrame[] getAlignFrames()
1268   {
1269     return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1270             : Desktop.getAlignFrames();
1271
1272   }
1273
1274   /**
1275    * Quit method delegates to Desktop.quit - unless running in headless mode
1276    * when it just ends the JVM
1277    */
1278   public void quit()
1279   {
1280     if (jsFunctionExec != null)
1281     {
1282       jsFunctionExec.tidyUp();
1283       jsFunctionExec = null;
1284     }
1285
1286     if (desktop != null)
1287     {
1288       desktop.quit();
1289     }
1290     else
1291     {
1292       System.exit(0);
1293     }
1294   }
1295
1296   /**
1297    * Get the SwingJS applet ID and combine that with the frameType
1298    * 
1299    * @param frameType
1300    *          "alignment", "desktop", etc., or null
1301    * @return
1302    */
1303   public static String getAppID(String frameType)
1304   {
1305     String id = Cache.getProperty("Info.j2sAppletID");
1306     if (id == null)
1307     {
1308       id = "jalview";
1309     }
1310     return id + (frameType == null ? "" : "-" + frameType);
1311   }
1312
1313   /**
1314    * Handle all JalviewLite applet parameters
1315    * 
1316    * @param aparser
1317    * @param af
1318    */
1319   private void loadAppletParams(ArgsParser aparser, AlignFrame af)
1320   {
1321     JalviewApp app = new JalviewApp()
1322     {
1323
1324       private boolean alignPDBStructures; // From JalviewLite; not implemented
1325
1326       private Hashtable<String, Hashtable<String, String[]>> jsmessages;
1327
1328       private Hashtable<String, int[]> jshashes;
1329
1330       @Override
1331       public String getParameter(String name)
1332       {
1333         return aparser.getAppletValue(name, null);
1334       }
1335
1336       @Override
1337       public boolean getDefaultParameter(String name, boolean def)
1338       {
1339         String stn;
1340         return ((stn = getParameter(name)) == null ? def
1341                 : "true".equalsIgnoreCase(stn));
1342       }
1343
1344       /**
1345        * Get the applet-like document base even though this is an application.
1346        */
1347       @Override
1348       public URL getDocumentBase()
1349       {
1350         return Platform.getDocumentBase();
1351       }
1352
1353       /**
1354        * Get the applet-like code base even though this is an application.
1355        */
1356       @Override
1357       public URL getCodeBase()
1358       {
1359         return Platform.getCodeBase();
1360       }
1361
1362       @Override
1363       public AlignViewportI getViewport()
1364       {
1365         return af.getViewport();
1366       }
1367
1368       /**
1369        * features
1370        * 
1371        */
1372       @Override
1373       public boolean parseFeaturesFile(String filename,
1374               DataSourceType protocol)
1375       {
1376         return af.parseFeaturesFile(filename, protocol);
1377       }
1378
1379       /**
1380        * scorefile
1381        * 
1382        */
1383       @Override
1384       public boolean loadScoreFile(String sScoreFile) throws IOException
1385       {
1386         af.loadJalviewDataFile(sScoreFile, null, null, null);
1387         return true;
1388       }
1389
1390       /**
1391        * annotations, jpredfile, jnetfile
1392        * 
1393        */
1394       @Override
1395       public void updateForAnnotations()
1396       {
1397         af.updateForAnnotations();
1398       }
1399
1400       @Override
1401       public void loadTree(NewickFile fin, String treeFile)
1402               throws IOException
1403       {
1404         // n/a -- already done by standard Jalview command line processing
1405       }
1406
1407       @Override
1408       public void setAlignPdbStructures(boolean defaultParameter)
1409       {
1410         alignPDBStructures = true;
1411       }
1412
1413       @Override
1414       public void newStructureView(PDBEntry pdb, SequenceI[] seqs,
1415               String[] chains, DataSourceType protocol)
1416       {
1417         StructureViewer.launchStructureViewer(af.alignPanel, pdb, seqs);
1418       }
1419
1420       @Override
1421       public void setFeatureGroupState(String[] groups, boolean state)
1422       {
1423         af.setFeatureGroupState(groups, state);
1424       }
1425
1426       @Override
1427       public void alignedStructureView(PDBEntry[] pdb, SequenceI[][] seqs,
1428               String[][] chains, String[] protocols)
1429       {
1430         System.err.println(
1431                 "Jalview applet interface alignedStructureView not implemented");
1432       }
1433
1434       @Override
1435       public void newFeatureSettings()
1436       {
1437         System.err.println(
1438                 "Jalview applet interface newFeatureSettings not implemented");
1439       }
1440
1441       private Vector<Runnable> jsExecQueue;
1442
1443       @Override
1444       public Vector<Runnable> getJsExecQueue(JSFunctionExec exec)
1445       {
1446         jsFunctionExec = exec;
1447         return (jsExecQueue == null ? (jsExecQueue = new Vector<>())
1448                 : jsExecQueue);
1449       }
1450
1451       @Override
1452       public AppletContext getAppletContext()
1453       {
1454         // TODO Auto-generated method stub
1455         return null;
1456       }
1457
1458       @Override
1459       public boolean isJsfallbackEnabled()
1460       {
1461         // TODO Auto-generated method stub
1462         return false;
1463       }
1464
1465       @Override
1466       public JSObject getJSObject()
1467       {
1468         // TODO Auto-generated method stub
1469         return null;
1470       }
1471
1472       @Override
1473       public StructureSelectionManagerProvider getStructureSelectionManagerProvider()
1474       {
1475         // TODO Auto-generated method stub
1476         return null;
1477       }
1478
1479       @Override
1480       public void updateColoursFromMouseOver(Object source,
1481               MouseOverStructureListener mouseOverStructureListener)
1482       {
1483         // TODO Auto-generated method stub
1484
1485       }
1486
1487       @Override
1488       public Object[] getSelectionForListener(SequenceGroup seqsel,
1489               ColumnSelection colsel, HiddenColumns hidden,
1490               SelectionSource source, Object alignFrame)
1491       {
1492         // TODO Auto-generated method stub
1493         return null;
1494       }
1495
1496       @Override
1497       public String arrayToSeparatorList(String[] array)
1498       {
1499         return appLoader.arrayToSeparatorList(array);
1500       }
1501
1502       @Override
1503       public Hashtable<String, int[]> getJSHashes()
1504       {
1505         return (jshashes == null
1506                 ? (jshashes = new Hashtable<>())
1507                 : jshashes);
1508       }
1509
1510       @Override
1511       public Hashtable<String, Hashtable<String, String[]>> getJSMessages()
1512       {
1513         return (jsmessages == null
1514                 ? (jsmessages = new Hashtable<>())
1515                 : jsmessages);
1516       }
1517
1518       @Override
1519       public Object getFrameForSource(VamsasSource source)
1520       {
1521         if (source != null)
1522         {
1523           AlignFrame af;
1524           if (source instanceof jalview.gui.AlignViewport
1525                   && source == (af = getCurrentAlignFrame()).getViewport())
1526           {
1527             // should be valid if it just generated an event!
1528             return af;
1529           }
1530           // TODO: ensure that if '_af' is specified along with a handler
1531           // function, then only events from that alignFrame are sent to that
1532           // function
1533         }
1534         return null;
1535       }
1536
1537       @Override
1538       public FeatureRenderer getNewFeatureRenderer(AlignViewportI vp)
1539       {
1540         return new jalview.gui.FeatureRenderer((AlignmentPanel) vp);
1541       }
1542
1543     };
1544
1545     appLoader = new JalviewAppLoader(true);
1546     appLoader.load(app);
1547   }
1548
1549   /**
1550    * 
1551    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences()
1552    */
1553   @Override
1554   public String getSelectedSequences()
1555   {
1556     return getSelectedSequencesFrom(getCurrentAlignFrame());
1557   }
1558
1559   /**
1560    * 
1561    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences(java.lang.String)
1562    */
1563   @Override
1564   public String getSelectedSequences(String sep)
1565   {
1566     return getSelectedSequencesFrom(getCurrentAlignFrame(), sep);
1567   }
1568
1569   /**
1570    * 
1571    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
1572    *      .AlignFrame)
1573    */
1574   @Override
1575   public String getSelectedSequencesFrom(AlignFrameI alf)
1576   {
1577     return getSelectedSequencesFrom(alf, null);
1578   }
1579
1580   /**
1581    * 
1582    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
1583    *      .AlignFrame, java.lang.String)
1584    */
1585   @Override
1586   public String getSelectedSequencesFrom(AlignFrameI alf, String sep)
1587   {
1588     return appLoader.getSelectedSequencesFrom(alf, sep);
1589   }
1590
1591   /**
1592    * 
1593    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
1594    *      .AlignFrame, java.lang.String)
1595    */
1596   @Override
1597   public void highlight(String sequenceId, String position,
1598           String alignedPosition)
1599   {
1600     highlightIn(getCurrentAlignFrame(), sequenceId, position,
1601             alignedPosition);
1602   }
1603
1604   @Override
1605   public void highlightIn(AlignFrameI alf,
1606           String sequenceId, String position, String alignedPosition)
1607   {
1608     appLoader.highlightIn(alf, sequenceId, position,
1609             alignedPosition);
1610   }
1611
1612   @Override
1613   public void select(String sequenceIds, String columns)
1614   {
1615     selectIn(getCurrentAlignFrame(), sequenceIds, columns, null);
1616   }
1617
1618   @Override
1619   public void select(String sequenceIds, String columns, String sep)
1620   {
1621     selectIn(getCurrentAlignFrame(), sequenceIds, columns, sep);
1622   }
1623
1624   @Override
1625   public void selectIn(AlignFrameI alf, String sequenceIds,
1626           String columns)
1627   {
1628     selectIn(alf, sequenceIds, columns, null);
1629   }
1630
1631   @Override
1632   public void selectIn(AlignFrameI alf, String sequenceIds,
1633           String columns, String sep)
1634   {
1635     appLoader.selectIn(alf, sequenceIds, columns, sep);
1636   }
1637
1638   @Override
1639   public String getSelectedSequencesAsAlignment(String format,
1640           String suffix)
1641   {
1642     return getSelectedSequencesAsAlignmentFrom(getCurrentAlignFrame(),
1643             format, suffix);
1644   }
1645
1646   @Override
1647   public String getSelectedSequencesAsAlignmentFrom(
1648           AlignFrameI alf, String format, String sep)
1649   {
1650     return appLoader.getSelectedSequencesAsAlignmentFrom(alf, format, sep);
1651   }
1652
1653   @Override
1654   public String getAlignmentOrder()
1655   {
1656     return getAlignmentFrom(getCurrentAlignFrame(), null);
1657   }
1658
1659   @Override
1660   public String getAlignmentOrderFrom(AlignFrameI alf)
1661   {
1662     return getAlignmentFrom(alf, null);
1663   }
1664
1665   @Override
1666   public String getAlignmentOrderFrom(AlignFrameI alf,
1667           String sep)
1668   {
1669     return appLoader.getAlignmentOrderFrom(alf, sep);
1670   }
1671
1672   @Override
1673   public String orderBy(String order, String undoName)
1674   {
1675     return orderBy(order, undoName, null);
1676   }
1677
1678   @Override
1679   public String orderBy(String order, String undoName, String sep)
1680   {
1681     return orderAlignmentBy(getCurrentAlignFrame(), order, undoName, sep);
1682   }
1683
1684   @Override
1685   public String orderAlignmentBy(AlignFrameI alf,
1686           String order, String undoName, String sep)
1687   {
1688     return appLoader.orderAlignmentBy(alf, order, undoName, sep);
1689   }
1690
1691   @Override
1692   public String getAlignment(String format)
1693   {
1694     return getAlignmentFrom(null, format, null);
1695   }
1696
1697   @Override
1698   public String getAlignmentFrom(AlignFrameI alf,
1699           String format)
1700   {
1701     return getAlignmentFrom(alf, format, null);
1702   }
1703
1704   @Override
1705   public String getAlignment(String format, String suffix)
1706   {
1707     return getAlignmentFrom(getCurrentAlignFrame(), format, suffix);
1708   }
1709
1710   @Override
1711   public String getAlignmentFrom(AlignFrameI alf,
1712           String format, String suffix)
1713   {
1714     return appLoader.getAlignmentFrom(alf, format, suffix);
1715   }
1716
1717   @Override
1718   public void loadAnnotation(String annotation)
1719   {
1720     // TODO Auto-generated method stub
1721
1722   }
1723
1724   @Override
1725   public void loadAnnotationFrom(AlignFrameI alf,
1726           String annotation)
1727   {
1728     // TODO Auto-generated method stub
1729
1730   }
1731
1732   @Override
1733   public void loadFeatures(String features, boolean autoenabledisplay)
1734   {
1735     // TODO Auto-generated method stub
1736
1737   }
1738
1739   @Override
1740   public boolean loadFeaturesFrom(AlignFrameI alf,
1741           String features, boolean autoenabledisplay)
1742   {
1743     // TODO Auto-generated method stub
1744     return false;
1745   }
1746
1747   @Override
1748   public String getFeatures(String format)
1749   {
1750     // TODO Auto-generated method stub
1751     return null;
1752   }
1753
1754   @Override
1755   public String getFeaturesFrom(AlignFrameI alf,
1756           String format)
1757   {
1758     // TODO Auto-generated method stub
1759     return null;
1760   }
1761
1762   @Override
1763   public String getAnnotation()
1764   {
1765     // TODO Auto-generated method stub
1766     return null;
1767   }
1768
1769   @Override
1770   public String getAnnotationFrom(AlignFrameI alf)
1771   {
1772     // TODO Auto-generated method stub
1773     return null;
1774   }
1775
1776   @Override
1777   public AlignFrameI newView()
1778   {
1779     // TODO Auto-generated method stub
1780     return null;
1781   }
1782
1783   @Override
1784   public AlignFrameI newView(String name)
1785   {
1786     // TODO Auto-generated method stub
1787     return null;
1788   }
1789
1790   @Override
1791   public AlignFrameI newViewFrom(AlignFrameI alf)
1792   {
1793     // TODO Auto-generated method stub
1794     return null;
1795   }
1796
1797   @Override
1798   public AlignFrameI newViewFrom(AlignFrameI alf, String name)
1799   {
1800     // TODO Auto-generated method stub
1801     return null;
1802   }
1803
1804   @Override
1805   public AlignFrameI loadAlignment(String text,
1806           String title)
1807   {
1808     return appLoader.loadAlignment(text, AlignFrame.DEFAULT_WIDTH,
1809             AlignFrame.DEFAULT_HEIGHT, title);
1810   }
1811
1812   @Override
1813   public void setMouseoverListener(String listener)
1814   {
1815     // TODO Auto-generated method stub
1816
1817   }
1818
1819   @Override
1820   public void setMouseoverListener(AlignFrameI af,
1821           String listener)
1822   {
1823     // TODO Auto-generated method stub
1824
1825   }
1826
1827   @Override
1828   public void setSelectionListener(String listener)
1829   {
1830     // TODO Auto-generated method stub
1831
1832   }
1833
1834   @Override
1835   public void setSelectionListener(AlignFrameI af,
1836           String listener)
1837   {
1838     // TODO Auto-generated method stub
1839
1840   }
1841
1842   @Override
1843   public void setStructureListener(String listener, String modelSet)
1844   {
1845     // TODO Auto-generated method stub
1846
1847   }
1848
1849   @Override
1850   public void removeJavascriptListener(AlignFrameI af,
1851           String listener)
1852   {
1853     // TODO Auto-generated method stub
1854
1855   }
1856
1857   @Override
1858   public void mouseOverStructure(String pdbResNum, String chain,
1859           String pdbfile)
1860   {
1861     // TODO Auto-generated method stub
1862
1863   }
1864
1865   @Override
1866   public boolean addPdbFile(AlignFrameI alFrame,
1867           String sequenceId, String pdbEntryString, String pdbFile)
1868   {
1869     return appLoader.addPdbFile(alFrame, sequenceId, pdbEntryString,
1870             pdbFile);
1871   }
1872
1873   @Override
1874   public void scrollViewToIn(AlignFrameI alf,
1875           String topRow, String leftHandColumn)
1876   {
1877     appLoader.scrollViewToIn(alf, topRow, leftHandColumn);
1878   }
1879
1880   @Override
1881   public void scrollViewToRowIn(AlignFrameI alf,
1882           String topRow)
1883   {
1884     appLoader.scrollViewToRowIn(alf, topRow);
1885   }
1886
1887   @Override
1888   public void scrollViewToColumnIn(AlignFrameI alf,
1889           String leftHandColumn)
1890   {
1891     appLoader.scrollViewToColumnIn(alf, leftHandColumn);
1892   }
1893
1894   @Override
1895   public String getFeatureGroups()
1896   {
1897     return getFeatureGroupsOn(getCurrentAlignFrame());
1898   }
1899
1900   @Override
1901   public String getFeatureGroupsOn(AlignFrameI alf)
1902   {
1903     return appLoader.getFeatureGroupsOn(alf);
1904   }
1905
1906   @Override
1907   public String getFeatureGroupsOfState(boolean visible)
1908   {
1909     return getFeatureGroupsOfStateOn(getCurrentAlignFrame(), visible);
1910   }
1911
1912   @Override
1913   public String getFeatureGroupsOfStateOn(AlignFrameI alf,
1914           boolean visible)
1915   {
1916     return appLoader.getFeatureGroupsOfStateOn(alf, visible);
1917   }
1918
1919   @Override
1920   public void setFeatureGroupStateOn(AlignFrameI alf,
1921           String groups, boolean state)
1922   {
1923     setFeatureGroupStateOn(alf, groups, state);
1924   }
1925
1926   @Override
1927   public void setFeatureGroupState(String groups, boolean state)
1928   {
1929     appLoader.setFeatureGroupStateOn(getCurrentAlignFrame(), groups, state);
1930   }
1931
1932   @Override
1933   public String getSeparator()
1934   {
1935     return appLoader.getSeparator();
1936   }
1937
1938   @Override
1939   public void setSeparator(String separator)
1940   {
1941     appLoader.setSeparator(separator);
1942   }
1943
1944   @Override
1945   public String getJsMessage(String messageclass, String viewId)
1946   {
1947     // see http://www.jalview.org/examples/jalviewLiteJs.html
1948     return null;
1949   }
1950
1951   /**
1952    * Open a new Tree panel on the desktop statically. Params are standard (not
1953    * set by Groovy). No dialog is opened.
1954    * 
1955    * @param af
1956    * @param treeType
1957    * @param modelName
1958    * @return null, or the string "label.you_need_at_least_n_sequences" if number
1959    *         of sequences selected is inappropriate
1960    */
1961   @Override
1962   public Object openTreePanel(AlignFrame af, String treeType,
1963           String modelName)
1964   {
1965     return CalculationChooser.openTreePanel(af, treeType, modelName, null);
1966   }
1967
1968   /**
1969    * public static method for JalviewJS API to open a PCAPanel without
1970    * necessarily using a dialog.
1971    * 
1972    * @param af
1973    * @param modelName
1974    * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences"
1975    *         if number of sequences selected is inappropriate
1976    */
1977   @Override
1978   public Object openPcaPanel(AlignFrame af, String modelName)
1979   {
1980     return CalculationChooser.openPcaPanel(af, modelName, null);
1981   }
1982
1983 }