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