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