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