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