Refactoring of Jalview, JalviewAppLoader, JalviewApp, various
[jalview.git] / src / jalview / api / JalviewJSApp.java
1 package jalview.api;
2
3 import java.awt.EventQueue;
4 //import java.applet.AppletContext;
5 import java.io.IOException;
6 import java.net.URL;
7 import java.util.ArrayList;
8 import java.util.Hashtable;
9 import java.util.List;
10 import java.util.StringTokenizer;
11 import java.util.Vector;
12
13 import jalview.bin.ArgsParser;
14 import jalview.bin.Jalview;
15 import jalview.bin.JalviewJSApi;
16 import jalview.datamodel.Alignment;
17 import jalview.datamodel.AlignmentI;
18 import jalview.datamodel.AlignmentOrder;
19 import jalview.datamodel.ColumnSelection;
20 import jalview.datamodel.HiddenColumns;
21 import jalview.datamodel.PDBEntry;
22 import jalview.datamodel.Sequence;
23 import jalview.datamodel.SequenceGroup;
24 import jalview.datamodel.SequenceI;
25 import jalview.gui.AlignFrame;
26 import jalview.gui.AlignViewport;
27 import jalview.gui.AlignmentPanel;
28 import jalview.gui.CalculationChooser;
29 import jalview.gui.Desktop;
30 import jalview.gui.StructureViewer;
31 import jalview.io.AnnotationFile;
32 import jalview.io.AppletFormatAdapter;
33 import jalview.io.DataSourceType;
34 import jalview.io.FeaturesFile;
35 import jalview.io.FileFormat;
36 import jalview.io.FileFormatI;
37 import jalview.io.FileFormats;
38 import jalview.io.IdentifyFile;
39 import jalview.io.JPredFile;
40 import jalview.io.JnetAnnotationMaker;
41 import jalview.io.NewickFile;
42 import jalview.renderer.seqfeatures.FeatureRenderer;
43 import jalview.structure.SelectionListener;
44 import jalview.structure.SelectionSource;
45 import jalview.structure.StructureSelectionManager;
46 import jalview.structure.VamsasSource;
47 import jalview.util.HttpUtils;
48 import jalview.util.MessageManager;
49 import jalview.util.Platform;
50
51 //import netscape.javascript.JSObject;
52
53 /**
54  * Basically the JalviewLite application, but without JalviewLite
55  * 
56  * @author hansonr
57  *
58  */
59 public class JalviewJSApp implements JalviewJSApi
60 {
61   private ArgsParser aparser;
62
63   private String j2sAppletID;
64
65   private boolean debug;
66
67   private String[] ret = new String[1];
68
69   private String separator = "\u00AC"; // JalviewLite note: the default used to
70                                        // be '|', but many sequence IDS include
71                                        // pipes.
72
73   public JalviewJSApp(ArgsParser aparser)
74   {
75     this.aparser = aparser;
76     this.j2sAppletID = (String) aparser.getAppletValue("j2sAppletID",
77             "Jalview", true);
78     Platform.setAppClass(this);
79   }
80
81
82   public void load(AlignFrame af)
83   {
84     String sep = (String) getAppletParameter("separator", true);
85     if (sep != null)
86     {
87       if (sep.length() > 0)
88       {
89         separator = sep;
90       }
91       else
92       {
93         throw new Error(MessageManager
94                 .getString("error.invalid_separator_parameter"));
95       }
96     }
97
98     loadTree(af);
99     loadScoreFile();
100     loadFeatures(af);
101     loadAnnotations(af);
102     loadJnetFile(af);
103     loadPdbFiles(af);
104   }
105
106   public String getAppID(String frameType)
107   {
108     return j2sAppletID + (frameType == null ? "" : "-" + frameType);
109   }
110
111   // TODO BH 2019
112   //
113   // These are methods that are in JalviewLite that various classes call
114   // but are not in JalviewLiteJsApi. Or, even if they are, other classes
115   // call
116   // them to JalviewLite directly. Some may not be necessary, but they have
117   // to
118   // be at least mentioned here, or the classes calling them should
119   // reference
120   // JalviewLite itself.
121
122   // private boolean alignPDBStructures; // From JalviewLite; not implemented
123   //
124   private Hashtable<String, Hashtable<String, String[]>> jsmessages;
125
126   private Hashtable<String, int[]> jshashes;
127
128   @Override
129   public String getParameter(String name)
130   {
131     return (String) getAppletParameter(name, true);
132   }
133
134   @Override
135   public Object getAppletParameter(String name, boolean asString)
136   {
137     return aparser.getAppletValue(name, null, asString);
138   }
139
140   /**
141    * Get the applet-like code base even though this is an application.
142    */
143
144   @Override
145   public URL getCodeBase()
146   {
147     return Platform.getCodeBase();
148   }
149
150   /**
151    * Get the applet-like document base even though this is an application.
152    */
153
154   @Override
155   public URL getDocumentBase()
156   {
157     return Platform.getDocumentBase();
158   }
159
160   @Override
161   public Object getFrameForSource(VamsasSource source)
162   {
163     if (source != null)
164     {
165       AlignFrame af;
166       if (source instanceof jalview.gui.AlignViewport
167               && source == (af = Jalview.getCurrentAlignFrame())
168                       .getViewport())
169       {
170         // should be valid if it just generated an event!
171         return af;
172       }
173       // TODO: ensure that if '_af' is specified along with a handler
174       // function, then only events from that alignFrame are sent to that
175       // function
176     }
177     return null;
178   }
179
180   @Override
181   public Hashtable<String, int[]> getJSHashes()
182   {
183     return (jshashes == null ? (jshashes = new Hashtable<>()) : jshashes);
184   }
185
186   @Override
187   public Hashtable<String, Hashtable<String, String[]>> getJSMessages()
188   {
189     return (jsmessages == null ? (jsmessages = new Hashtable<>())
190             : jsmessages);
191   }
192
193   @Override
194   public Object getJSObject()
195   {
196     return Jalview.getInstance();
197   }
198
199   @Override
200   public FeatureRenderer getNewFeatureRenderer(AlignViewportI vp)
201   {
202     return new jalview.gui.FeatureRenderer((AlignmentPanel) vp);
203   }
204
205   @Override
206   public Object[] getSelectionForListener(SequenceGroup seqsel,
207           ColumnSelection colsel, HiddenColumns hidden,
208           SelectionSource source, Object alignFrame)
209   {
210     return getSelectionForListener(null, seqsel, colsel, hidden, source,
211             alignFrame);
212   }
213
214   /**
215    * scorefile
216    * 
217    */
218
219   @Override
220   public boolean loadScoreFile(String sScoreFile) throws IOException
221   {
222     return loadScoreFile(null, sScoreFile);
223   }
224   
225   public boolean loadScoreFile(AlignFrame af, String sScoreFile) throws IOException
226   {
227     (af == null ? Jalview.getCurrentAlignFrame() : af).loadJalviewDataFile(sScoreFile, null, null, null);
228     return true;
229   }
230
231   public void loadTree(AlignFrame af, NewickFile nf, String treeFile) throws IOException
232   {
233     if (af == null)
234       af = Jalview.getCurrentAlignFrame();
235     af.getViewport()
236             .setCurrentTree(af.showNewickTree(nf, treeFile).getTree());
237   }
238
239   @Override
240   public void newFeatureSettings()
241   {
242     System.err.println(
243             "Jalview applet interface newFeatureSettings not implemented");
244   }
245
246   //
247   //
248   // public void setAlignPdbStructures(boolean defaultParameter)
249   // {
250   // alignPDBStructures = true;
251   // }
252   //
253
254   @Override
255   public void newStructureView(PDBEntry pdb, SequenceI[] seqs,
256           String[] chains, DataSourceType protocol)
257   {
258     newStructureView(null, pdb, seqs, chains, protocol);
259     
260   }
261
262   public void newStructureView(AlignFrame af, PDBEntry pdb,
263           SequenceI[] seqs, String[] chains, DataSourceType protocol)
264   {
265     StructureViewer.launchStructureViewer(
266             (af == null ? Jalview.getCurrentAlignFrame() : af).alignPanel,
267             pdb, seqs);
268   }
269
270   /**
271    * features
272    * @param af 
273    * 
274    */
275
276   @Override
277   public boolean parseFeaturesFile(String filename, DataSourceType protocol)
278   {
279     return parseFeaturesFile(null, filename, protocol);
280   }
281   
282   public boolean parseFeaturesFile(AlignFrame af, String filename, DataSourceType protocol)
283   {
284     return af.parseFeaturesFile(filename, protocol);
285   }
286
287   @Override
288   public void setFeatureGroupState(String[] groups, boolean state)
289   {
290     setFeatureGroupState(null, groups, state);
291   }
292
293   public void setFeatureGroupState(AlignFrame af, String[] groups, boolean state) {
294     (af == null ? Jalview.getCurrentAlignFrame() : af).setFeatureGroupState(groups, state);
295   }
296   /**
297    * annotations, jpredfile, jnetfile
298    * 
299    */
300
301   @Override
302   public void updateForAnnotations()
303   {
304     updateForAnnotations(null);
305   }
306
307   public void updateForAnnotations(AlignFrame af)
308   {
309     (af == null ? Jalview.getCurrentAlignFrame() : af).updateForAnnotations();
310   }
311
312   @Override
313   public boolean addPdbFile(AlignFrame alf, String sequenceId,
314           String pdbEntryString, String pdbFile)
315   {
316     if (alf == null)
317     {
318       alf = Jalview.getCurrentAlignFrame();
319     }
320     SequenceI toaddpdb = alf.getViewport().getAlignment()
321             .findName(sequenceId);
322     boolean needtoadd = false;
323     if (toaddpdb != null)
324     {
325       Vector<PDBEntry> pdbe = toaddpdb.getAllPDBEntries();
326       PDBEntry pdbentry = null;
327       if (pdbe != null && pdbe.size() > 0)
328       {
329         for (int pe = 0, peSize = pdbe.size(); pe < peSize; pe++)
330         {
331           pdbentry = pdbe.elementAt(pe);
332           if (!pdbentry.getId().equals(pdbEntryString)
333                   && !pdbentry.getFile().equals(pdbFile))
334           {
335             pdbentry = null;
336           }
337           else
338           {
339             continue;
340           }
341         }
342       }
343       if (pdbentry == null)
344       {
345         pdbentry = new PDBEntry();
346         pdbentry.setId(pdbEntryString);
347         pdbentry.setFile(pdbFile);
348         needtoadd = true; // add this new entry to sequence.
349       }
350       // resolve data source
351       // TODO: this code should be a refactored to an io package
352       DataSourceType protocol = AppletFormatAdapter.resolveProtocol(pdbFile,
353               FileFormat.PDB);
354       if (protocol == null)
355       {
356         return false;
357       }
358       if (needtoadd)
359       {
360         pdbentry.setProperty("protocol", protocol);
361         toaddpdb.addPDBId(pdbentry);
362         alf.alignPanel.getStructureSelectionManager()
363                 .registerPDBEntry(pdbentry);
364       }
365     }
366     return true;
367   }
368
369   @Override
370   public String arrayToSeparatorList(String[] array)
371   {
372     return arrayToSeparatorList(array, separator);
373   }
374
375   /**
376    * concatenate the list with separator
377    * 
378    * @param list
379    * @param separator
380    * @return concatenated string
381    */
382   public static String arrayToSeparatorList(String[] list, String separator)
383   {
384     // TODO use StringUtils version
385     StringBuffer v = new StringBuffer();
386     if (list != null && list.length > 0)
387     {
388       for (int i = 0, iSize = list.length; i < iSize; i++)
389       {
390         if (list[i] != null)
391         {
392           if (i > 0)
393           {
394             v.append(separator);
395           }
396           v.append(list[i]);
397         }
398       }
399       // if (debug)
400       // {
401       // System.err
402       // .println("Returning '" + separator + "' separated List:\n");
403       // System.err.println(v);
404       // }
405       return v.toString();
406     }
407     // if (debug)
408     // {
409     // System.err.println(
410     // "Returning empty '" + separator + "' separated List\n");
411     // }
412     return "" + separator;
413   }
414
415   @Override
416   public String getAlignment(String format)
417   {
418     return getAlignmentFrom(null, format, null);
419   }
420
421   @Override
422   public String getAlignment(String format, String suffix)
423   {
424     return getAlignmentFrom(Jalview.getCurrentAlignFrame(), format, suffix);
425   }
426
427   @Override
428   public String getAlignmentFrom(AlignFrame alf, String format)
429   {
430     return getAlignmentFrom(alf, format, null);
431   }
432
433   @Override
434   public String getAlignmentFrom(AlignFrame alf, String format,
435           String suffix)
436   {
437     try
438     {
439       if (alf == null)
440       {
441         alf = Jalview.getCurrentAlignFrame();
442       }
443       boolean seqlimits = (suffix == null
444               || suffix.equalsIgnoreCase("true"));
445
446       FileFormatI theFormat = FileFormats.getInstance().forName(format);
447       String reply = new AppletFormatAdapter().formatSequences(theFormat,
448               alf.getViewport().getAlignment(), seqlimits);
449       return reply;
450     } catch (IllegalArgumentException ex)
451     {
452       ex.printStackTrace();
453       return "Error retrieving alignment, possibly invalid format specifier: "
454               + format;
455     }
456   }
457
458   @Override
459   public String getAlignmentOrder()
460   {
461     return getAlignmentFrom(Jalview.getCurrentAlignFrame(), null);
462   }
463
464   @Override
465   public String getAlignmentOrderFrom(AlignFrame alf)
466   {
467     return getAlignmentFrom(alf, null);
468   }
469
470   @Override
471   public String getAlignmentOrderFrom(AlignFrame alf, String sep)
472   {
473     if (alf == null)
474     {
475       alf = Jalview.getCurrentAlignFrame();
476     }
477     AlignmentI alorder = alf.getViewport().getAlignment();
478     String[] order = new String[alorder.getHeight()];
479     for (int i = 0; i < order.length; i++)
480     {
481       order[i] = alorder.getSequenceAt(i).getName();
482     }
483     return arrayToSeparatorList(order, sep);
484   }
485
486   @Override
487   public String getAnnotation()
488   {
489     return getAnnotationFrom(null);
490   }
491
492   @Override
493   public String getAnnotationFrom(AlignFrame alf)
494   {
495     if (alf == null)
496     {
497       alf = Jalview.getCurrentAlignFrame();
498     }
499     String annotation = new AnnotationFile()
500             .printAnnotationsForView(alf.getViewport());
501     return annotation;
502   }
503
504   @Override
505   public String getFeatureGroups()
506   {
507     return getFeatureGroupsOn(null);
508   }
509
510   @Override
511   public String getFeatureGroupsOfState(boolean visible)
512   {
513     return getFeatureGroupsOfStateOn(null, visible);
514   }
515
516   @Override
517   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
518   {
519     if (alf == null)
520     {
521       alf = Jalview.getCurrentAlignFrame();
522     }
523     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
524   }
525
526   @Override
527   public String getFeatureGroupsOn(AlignFrame alf)
528   {
529     if (alf == null)
530     {
531       alf = Jalview.getCurrentAlignFrame();
532     }
533     return arrayToSeparatorList(alf.getFeatureGroups());
534   }
535
536   @Override
537   public String getFeatures(String format)
538   {
539     return getFeaturesFrom(null, format);
540   }
541
542   /**
543    * JavaScript interface to print the alignment frame
544    * 
545    * @param alf
546    * @param format
547    *          "jalview" or "gff" with or without ";includeComplement" or
548    *          ";includeNonpositional"; default with no ";" is
549    *          ";includeNonpositional"
550    * @return
551    */
552   @Override
553   public String getFeaturesFrom(AlignFrame alf, String format)
554   {
555     if (alf == null)
556     {
557       alf = Jalview.getCurrentAlignFrame();
558     }
559     String features;
560     FeaturesFile formatter = new FeaturesFile();
561     format = format.toLowerCase();
562     if (format.indexOf(";") < 0)
563       format += ";includenonpositional";
564     boolean nonpos = format.indexOf(";includenonpositional") > 0;
565     boolean compl = format.indexOf(";includecomplement") >= 0;
566     if (format.startsWith("jalview"))
567     {
568       features = formatter.printJalviewFormat(
569               alf.getViewport().getAlignment().getSequencesArray(),
570               alf.alignPanel.getFeatureRenderer(), nonpos, compl);
571     }
572     else
573     {
574       features = formatter.printGffFormat(
575               alf.getViewport().getAlignment().getSequencesArray(),
576               alf.alignPanel.getFeatureRenderer(), nonpos, compl);
577     }
578
579     if (features == null)
580     {
581       features = "";
582     }
583     return features;
584
585   }
586
587   @Override
588   public String getJsMessage(String messageclass, String viewId)
589   {
590     // TODO Auto-generated method stub
591     return null;
592   }
593
594   /**
595    * read sequence1...sequenceN as a raw alignment
596    * 
597    * @param jalviewApp
598    * @return
599    */
600   public String getPastedSequence(JalviewJSApp jalviewApp)
601   {
602     StringBuffer data = new StringBuffer("PASTE");
603     int i = 1;
604     String file = null;
605     while ((file = (String) getAppletParameter("sequence" + i,
606             true)) != null)
607     {
608       data.append(file.toString() + "\n");
609       i++;
610     }
611     if (data.length() > 5)
612     {
613       file = data.toString();
614     }
615     return file;
616   }
617
618   /**
619    * 
620    * @see jalview.appletgui.js.JalviewLiteJsApi#getSelectedSequences()
621    */
622   @Override
623   public String getSelectedSequences()
624   {
625     return getSelectedSequencesFrom(Jalview.getCurrentAlignFrame());
626   }
627
628   /**
629    * 
630    * @see jalview.appletgui.js.JalviewLiteJsApi#getSelectedSequences(java.lang.String)
631    */
632   @Override
633   public String getSelectedSequences(String sep)
634   {
635     return getSelectedSequencesFrom(Jalview.getCurrentAlignFrame(), sep);
636   }
637
638   @Override
639   public String getSelectedSequencesAsAlignment(String format,
640           String suffix)
641   {
642     return getSelectedSequencesAsAlignmentFrom(null, format, suffix);
643   }
644
645   @Override
646   public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
647           String format, String suffix)
648   {
649
650     if (alf == null)
651     {
652       alf = Jalview.getCurrentAlignFrame();
653     }
654
655     boolean seqlimits = (suffix == null || suffix.equalsIgnoreCase("true"));
656     try
657     {
658       AlignViewport vp = alf.getViewport();
659       FileFormatI theFormat = FileFormats.getInstance().forName(format);
660       if (vp.getSelectionGroup() != null)
661       {
662         // JBPNote: getSelectionAsNewSequence behaviour has changed - this
663         // method now returns a full copy of sequence data
664         // TODO consider using getSequenceSelection instead here
665         String reply = new AppletFormatAdapter().formatSequences(theFormat,
666                 new Alignment(vp.getSelectionAsNewSequence()), seqlimits);
667         return reply;
668       }
669     } catch (IllegalArgumentException ex)
670     {
671       ex.printStackTrace();
672       return "Error retrieving alignment, possibly invalid format specifier: "
673               + format;
674     }
675     return "";
676   }
677
678   /**
679    * 
680    * @see jalview.appletgui.js.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
681    *      .AlignFrame)
682    */
683   @Override
684   public String getSelectedSequencesFrom(AlignFrame alf)
685   {
686     return getSelectedSequencesFrom(alf, null);
687   }
688
689   @Override
690   public String getSelectedSequencesFrom(AlignFrame alf, String sep)
691   {
692     if (alf == null)
693     {
694       alf = Jalview.getCurrentAlignFrame();
695     }
696     StringBuffer result = new StringBuffer("");
697     if (sep == null || sep.length() == 0)
698     {
699       sep = separator; // "+0x00AC;
700     }
701     AlignViewport v = alf.getViewport();
702     if (v.getSelectionGroup() != null)
703     {
704       SequenceI[] seqs = v.getSelectionGroup()
705               .getSequencesInOrder(v.getAlignment());
706
707       for (int i = 0; i < seqs.length; i++)
708       {
709         result.append(seqs[i].getName());
710         result.append(sep);
711       }
712     }
713
714     return result.toString();
715   }
716
717   public Object[] getSelectionForListener(AlignFrame alf,
718           SequenceGroup seqsel, ColumnSelection colsel,
719           HiddenColumns hidden, SelectionSource source, Object alignFrame)
720   {
721     if (alf == null)
722     {
723       alf = Jalview.getCurrentAlignFrame();
724     }
725     // System.err.println("Testing selection event relay to
726     // jsfunction:"+_listener);
727     String setid = "";
728     AlignFrame src = (AlignFrame) alignFrame;
729     if (source != null)
730     {
731       if (source instanceof AlignViewport && alf.getViewport() == source)
732       {
733         // should be valid if it just generated an event!
734         src = alf;
735
736       }
737     }
738     String[] seqs = new String[] {};
739     String[] cols = new String[] {};
740     int strt = 0, end = (src == null) ? -1
741             : src.alignPanel.av.getAlignment().getWidth();
742     if (seqsel != null && seqsel.getSize() > 0)
743     {
744       seqs = new String[seqsel.getSize()];
745       for (int i = 0; i < seqs.length; i++)
746       {
747         seqs[i] = seqsel.getSequenceAt(i).getName();
748       }
749       if (strt < seqsel.getStartRes())
750       {
751         strt = seqsel.getStartRes();
752       }
753       if (end == -1 || end > seqsel.getEndRes())
754       {
755         end = seqsel.getEndRes();
756       }
757     }
758     if (colsel != null && !colsel.isEmpty())
759     {
760       if (end == -1)
761       {
762         end = colsel.getMax() + 1;
763       }
764       cols = new String[colsel.getSelected().size()];
765       for (int i = 0; i < cols.length; i++)
766       {
767         cols[i] = "" + (1 + colsel.getSelected().get(i).intValue());
768       }
769     }
770     else
771     {
772       if (seqsel != null && seqsel.getSize() > 0)
773       {
774         // send a valid range, otherwise we send the empty selection
775         cols = new String[2];
776         cols[0] = "" + (1 + strt) + "-" + (1 + end);
777       }
778     }
779     return new Object[] { src, setid, arrayToSeparatorList(seqs),
780         arrayToSeparatorList(cols) };
781   }
782
783   @Override
784   public String getSeparator()
785   {
786     return separator;
787   }
788
789   /**
790    * 
791    * @see jalview.appletgui.js.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
792    *      .AlignFrame, java.lang.String)
793    */
794   @Override
795   public void highlight(String sequenceId, String position,
796           String alignedPosition)
797   {
798     highlightIn(null, sequenceId, position, alignedPosition);
799   }
800
801   @Override
802   public void highlightIn(AlignFrame alf, final String sequenceId,
803           final String position, final String alignedPosition)
804   {
805     if (alf == null)
806     {
807       alf = Jalview.getCurrentAlignFrame();
808     }
809     // TODO: could try to highlight in all alignments if alf==null
810     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
811             alf.getViewport().getAlignment().getSequencesArray());
812     final SequenceI sq = matcher.findIdMatch(sequenceId);
813     if (sq != null)
814     {
815       int apos = -1;
816       try
817       {
818         apos = Integer.valueOf(position).intValue();
819         apos--;
820       } catch (NumberFormatException ex)
821       {
822         return;
823       }
824       final int pos = apos;
825       // use vamsas listener to broadcast to all listeners in scope
826       if (alignedPosition != null && (alignedPosition.trim().length() == 0
827               || alignedPosition.toLowerCase().indexOf("false") > -1))
828       {
829         java.awt.EventQueue.invokeLater(new Runnable()
830         {
831           @Override
832           public void run()
833           {
834             StructureSelectionManager
835                     .getStructureSelectionManager(Desktop.getInstance())
836                     .mouseOverVamsasSequence(sq, sq.findIndex(pos), null);
837           }
838         });
839       }
840       else
841       {
842         java.awt.EventQueue.invokeLater(new Runnable()
843         {
844           @Override
845           public void run()
846           {
847             StructureSelectionManager
848                     .getStructureSelectionManager(Desktop.getInstance())
849                     .mouseOverVamsasSequence(sq, pos, null);
850           }
851         });
852       }
853     }
854   }
855
856   public AlignFrame loadAlignment(String text, int width, int height,
857           String title)
858   {
859     AlignmentI al = null;
860
861     try
862     {
863       FileFormatI format = new IdentifyFile().identify(text,
864               DataSourceType.PASTE);
865       al = new AppletFormatAdapter().readFile(text, DataSourceType.PASTE,
866               format);
867       if (al.getHeight() > 0)
868       {
869         return new AlignFrame(al, width, height, title);
870       }
871     } catch (IOException ex)
872     {
873       ex.printStackTrace();
874     }
875     return null;
876   }
877
878   // public void setMouseoverListener(String listener)
879   // {
880   // appLoader.setMouseoverListener(listener);
881   // }
882   //
883   //
884   // public void setMouseoverListener(AlignFrame af, String listener)
885   // {
886   // }
887   //
888
889   @Override
890   public AlignFrame loadAlignment(String text, String title)
891   {
892     return loadAlignment(text, AlignFrame.DEFAULT_WIDTH,
893             AlignFrame.DEFAULT_HEIGHT, title);
894   }
895
896   @Override
897   public void loadAnnotation(String annotation)
898   {
899     loadAnnotationFrom(null, annotation);
900   }
901
902   @Override
903   public void loadAnnotationFrom(AlignFrame alf, String annotation)
904   {
905     if (alf == null)
906     {
907       alf = Jalview.getCurrentAlignFrame();
908     }
909     if (new AnnotationFile().annotateAlignmentView(alf.getViewport(),
910             annotation, DataSourceType.PASTE))
911     {
912       alf.alignPanel.fontChanged();
913       alf.alignPanel.setScrollValues(0, 0);
914     }
915     else
916     {
917       alf.parseFeaturesFile(annotation, DataSourceType.PASTE);
918     }
919   }
920
921   /**
922    * Load annotations if specified by parameter. Returns true if loaded, else
923    * false.
924    * 
925    * 
926    * @param alignFrame
927    * @return
928    */
929   protected boolean loadAnnotations(AlignFrame af)
930   {
931     boolean result = false;
932     String param = (String) getAppletParameter("annotations", true);
933     if (param != null)
934     {
935       ret[0] = param;
936       DataSourceType protocol = resolveFileProtocol(ret);
937       param = ret[0];
938       if (new AnnotationFile().annotateAlignmentView(af.getViewport(), param,
939               protocol))
940       {
941         updateForAnnotations();
942         result = true;
943       }
944       else
945       {
946         System.err
947                 .println("Annotations were not added from annotation file '"
948                         + param + "'");
949       }
950     }
951     return result;
952   }
953
954   //// JalviewJSApi
955
956   /**
957    * Load features file and view settings as specified by parameters. Returns
958    * true if features were loaded, else false.
959    * @param  
960    * 
961    * @param alignFrame
962    * @return
963    */
964   protected boolean loadFeatures(AlignFrame af)
965   {
966     boolean result = false;
967     // ///////////////////////////
968     // modify display of features
969     // we do this before any features have been loaded, ensuring any hidden
970     // groups are hidden when features first displayed
971     //
972     // hide specific groups
973     //
974     String param = (String) getAppletParameter("hidefeaturegroups", true);
975     if (param != null)
976     {
977       setFeatureGroupState(af, separatorListToArray(param, separator), false);
978       // setFeatureGroupStateOn(newAlignFrame, param, false);
979     }
980     // show specific groups
981     param = (String) getAppletParameter("showfeaturegroups", true);
982     if (param != null)
983     {
984       setFeatureGroupState(af, separatorListToArray(param, separator), true);
985       // setFeatureGroupStateOn(newAlignFrame, param, true);
986     }
987     // and now load features
988     param = (String) getAppletParameter("features", true);
989     if (param != null)
990     {
991       ret[0] = param;
992       DataSourceType protocol = resolveFileProtocol(ret);
993
994       result = parseFeaturesFile(af, ret[0], protocol);
995     }
996
997     param = (String) getAppletParameter("showFeatureSettings", true);
998     if (param != null && param.equalsIgnoreCase("true"))
999     {
1000       newFeatureSettings();
1001     }
1002     return result;
1003   }
1004
1005   @Override
1006   public void loadFeatures(String features, boolean autoenabledisplay)
1007   {
1008     loadFeaturesFrom(null, features, autoenabledisplay);
1009   }
1010
1011   @Override
1012   public boolean loadFeaturesFrom(AlignFrame alf, String features,
1013           boolean autoenabledisplay)
1014   {
1015     if (alf == null)
1016     {
1017       alf = Jalview.getCurrentAlignFrame();
1018     }
1019     boolean ret = alf.parseFeaturesFile(features, DataSourceType.PASTE);
1020     if (!ret)
1021     {
1022       return false;
1023     }
1024     if (autoenabledisplay)
1025     {
1026       alf.getViewport().setShowSequenceFeatures(true);
1027       // this next was for a checkbox in JalviewLite
1028       // ((AlignFrame) alf).getViewport().sequenceFeatures.setState(true);
1029     }
1030     return true;
1031   }
1032
1033   /**
1034    * Load in a Jnetfile if specified by parameter. Returns true if loaded, else
1035    * false.
1036    * 
1037    * @param alignFrame
1038    * @return
1039    */
1040   protected boolean loadJnetFile(AlignFrame af)
1041   {
1042     boolean result = false;
1043     String param = (String) getAppletParameter("jnetfile", true);
1044     if (param == null)
1045     {
1046       // jnet became jpred around 2016
1047       param = (String) getAppletParameter("jpredfile", true);
1048     }
1049     if (param != null)
1050     {
1051       try
1052       {
1053         ret[0] = param;
1054         DataSourceType protocol = resolveFileProtocol(ret);
1055         JPredFile predictions = new JPredFile(ret[0], protocol);
1056         JnetAnnotationMaker.add_annotation(predictions,
1057                 af.getViewport().getAlignment(), 0, false);
1058         // false == do not add sequence profile from concise output
1059         af.getViewport().getAlignment().setupJPredAlignment();
1060         updateForAnnotations();
1061         result = true;
1062       } catch (Exception ex)
1063       {
1064         ex.printStackTrace();
1065       }
1066     }
1067     return result;
1068   }
1069
1070   /**
1071    * Load PDBFiles if any specified by parameter(s). Returns true if loaded,
1072    * else false.
1073    * 
1074    * @param loaderFrame
1075    * @return
1076    */
1077   protected boolean loadPdbFiles(AlignFrame af)
1078   {
1079     boolean result = false;
1080     /*
1081      * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6 -
1082      * related to JAL-434
1083      */
1084
1085     // not supported (as for JalviewLite)
1086     // boolean doAlign = false;//"true".equalsIgnoreCase("" +
1087     // getAppletParameter("alignpdbfiles", false));
1088     // setAlignPdbStructures(doAlign);
1089     /*
1090      * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
1091      * PDB|1GAQ|1GAQ|C">
1092      * 
1093      * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
1094      * 
1095      * <param name="PDBfile3" value="1q0o Q45135_9MICO">
1096      */
1097
1098     // Accumulate pdbs here if they are heading for the same view (if
1099     // alignPdbStructures is true)
1100     // ArrayList<Object[]> pdbs = new ArrayList<>();
1101     // create a lazy matcher if we're asked to
1102     boolean relaxed = "true".equalsIgnoreCase(
1103             "" + getAppletParameter("relaxedidmatch", false));
1104     jalview.analysis.SequenceIdMatcher matcher = relaxed
1105             ? new jalview.analysis.SequenceIdMatcher(
1106                     af.getViewport().getAlignment().getSequencesArray())
1107             : null;
1108
1109     int pdbFileCount = 0;
1110     String param;
1111     do
1112     {
1113       if (pdbFileCount > 0)
1114       {
1115         param = (String) getAppletParameter("PDBFILE" + pdbFileCount, true);
1116       }
1117       else
1118       {
1119         param = (String) getAppletParameter("PDBFILE", true);
1120       }
1121
1122       if (param != null)
1123       {
1124         PDBEntry pdb = new PDBEntry();
1125
1126         String seqstring;
1127         SequenceI[] seqs = null;
1128         String[] chains = null;
1129
1130         StringTokenizer st = new StringTokenizer(param, " ");
1131
1132         if (st.countTokens() < 2)
1133         {
1134           String sequence = (String) getAppletParameter("PDBSEQ", true);
1135           if (sequence != null)
1136           {
1137             seqs = new SequenceI[] { matcher == null
1138                     ? (Sequence) af.getViewport().getAlignment()
1139                             .findName(sequence)
1140                     : matcher.findIdMatch(sequence) };
1141           }
1142
1143         }
1144         else
1145         {
1146           param = st.nextToken();
1147           List<SequenceI> tmp = new ArrayList<>();
1148           List<String> tmp2 = new ArrayList<>();
1149
1150           while (st.hasMoreTokens())
1151           {
1152             seqstring = st.nextToken();
1153             StringTokenizer st2 = new StringTokenizer(seqstring, "=");
1154             if (st2.countTokens() > 1)
1155             {
1156               // This is the chain
1157               tmp2.add(st2.nextToken());
1158               seqstring = st2.nextToken();
1159             }
1160             tmp.add(matcher == null
1161                     ? (Sequence) af.getViewport().getAlignment()
1162                             .findName(seqstring)
1163                     : matcher.findIdMatch(seqstring));
1164           }
1165
1166           seqs = tmp.toArray(new SequenceI[tmp.size()]);
1167           if (tmp2.size() == tmp.size())
1168           {
1169             chains = tmp2.toArray(new String[tmp2.size()]);
1170           }
1171         }
1172         pdb.setId(param);
1173         ret[0] = param;
1174         DataSourceType protocol = resolveFileProtocol(ret);
1175         // TODO check JAL-357 for files in a jar (CLASSLOADER)
1176         pdb.setFile(ret[0]);
1177
1178         if (seqs != null)
1179         {
1180           for (int i = 0; i < seqs.length; i++)
1181           {
1182             if (seqs[i] != null)
1183             {
1184               ((Sequence) seqs[i]).addPDBId(pdb);
1185               StructureSelectionManager
1186                       .getStructureSelectionManager(
1187                               (StructureSelectionManagerProvider) this)
1188                       .registerPDBEntry(pdb);
1189             }
1190             else
1191             {
1192               if (debug)
1193               {
1194                 // this may not really be a problem but we give a warning
1195                 // anyway
1196                 System.err.println(
1197                         "Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
1198                                 + i + ")");
1199               }
1200             }
1201           }
1202
1203           // if (doAlign)
1204           // {
1205           // pdbs.add(new Object[] { pdb, seqs, chains, protocol });
1206           // }
1207           // else
1208           {
1209             newStructureView(af, pdb, seqs, chains, protocol);
1210           }
1211         }
1212       }
1213
1214       pdbFileCount++;
1215     } while (param != null || pdbFileCount < 10);
1216     //
1217     // if (doAlign && pdbs.size() > 0)
1218     // {
1219     // SequenceI[][] seqs = new SequenceI[pdbs.size()][];
1220     // PDBEntry[] pdb = new PDBEntry[pdbs.size()];
1221     // String[][] chains = new String[pdbs.size()][];
1222     // String[] protocols = new String[pdbs.size()];
1223     // for (int pdbsi = 0, pdbsiSize = pdbs
1224     // .size(); pdbsi < pdbsiSize; pdbsi++)
1225     // {
1226     // Object[] o = pdbs.get(pdbsi);
1227     // pdb[pdbsi] = (PDBEntry) o[0];
1228     // seqs[pdbsi] = (SequenceI[]) o[1];
1229     // chains[pdbsi] = (String[]) o[2];
1230     // protocols[pdbsi] = (String) o[3];
1231     // }
1232     //// alignedStructureView(pdb, seqs, chains, protocols);
1233     // result = true;
1234     // }
1235     return result;
1236   }
1237
1238   /**
1239    * Load a score file if specified by parameter. Returns true if file was
1240    * loaded, else false.
1241    * 
1242    * @param loaderFrame
1243    */
1244   protected boolean loadScoreFile()
1245   {
1246     boolean result = false;
1247     String sScoreFile = (String) getAppletParameter("scoreFile", true);
1248     if (sScoreFile != null && !"".equals(sScoreFile))
1249     {
1250       try
1251       {
1252         if (debug)
1253         {
1254           System.err.println(
1255                   "Attempting to load T-COFFEE score file from the scoreFile parameter");
1256         }
1257         result = loadScoreFile(sScoreFile);
1258         if (!result)
1259         {
1260           System.err.println(
1261                   "Failed to parse T-COFFEE parameter as a valid score file ('"
1262                           + sScoreFile + "')");
1263         }
1264       } catch (Exception e)
1265       {
1266         System.err.printf("Cannot read score file: '%s'. Cause: %s \n",
1267                 sScoreFile, e.getMessage());
1268       }
1269     }
1270     return result;
1271   }
1272
1273   /**
1274    * Load a tree for the alignment if specified by parameter. Returns true if a
1275    * tree was loaded, else false.
1276    * 
1277    * @return
1278    */
1279   protected boolean loadTree(AlignFrame af)
1280   {
1281     boolean result = false;
1282     String treeFile = (String) getAppletParameter("tree", true);
1283     if (treeFile == null)
1284     {
1285       treeFile = (String) getAppletParameter("treefile", true);
1286     }
1287
1288     if (treeFile != null)
1289     {
1290       try
1291       {
1292         ret[0] = treeFile;
1293         NewickFile fin = new NewickFile(treeFile, resolveFileProtocol(ret));
1294         fin.parse();
1295
1296         if (fin.getTree() != null)
1297         {
1298           loadTree(af, fin, ret[0]);
1299           result = true;
1300           if (debug)
1301           {
1302             System.out.println("Successfully imported tree.");
1303           }
1304         }
1305         else
1306         {
1307           if (debug)
1308           {
1309             System.out.println(
1310                     "Tree parameter did not resolve to a valid tree.");
1311           }
1312         }
1313       } catch (Exception ex)
1314       {
1315         ex.printStackTrace();
1316       }
1317     }
1318     return result;
1319   }
1320
1321   /**
1322    * public static method for JalviewJS API to open a PCAPanel without
1323    * necessarily using a dialog.
1324    * 
1325    * @param af
1326    * @param modelName
1327    * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences"
1328    *         if number of sequences selected is inappropriate
1329    */
1330   @Override
1331   public Object openPcaPanel(AlignFrame af, String modelName)
1332   {
1333     if (af == null)
1334     {
1335       af = Jalview.getCurrentAlignFrame();
1336     }
1337     return CalculationChooser.openPcaPanel(af, modelName, null);
1338   }
1339
1340   /**
1341    * Open a new Tree panel on the desktop statically. Params are standard (not
1342    * set by Groovy). No dialog is opened.
1343    * 
1344    * @param af
1345    * @param treeType
1346    * @param modelName
1347    * @return null, or the string "label.you_need_at_least_n_sequences" if number
1348    *         of sequences selected is inappropriate
1349    */
1350   @Override
1351   public Object openTreePanel(AlignFrame af, String treeType,
1352           String modelName)
1353   {
1354     if (af == null)
1355     {
1356       af = Jalview.getCurrentAlignFrame();
1357     }
1358     return CalculationChooser.openTreePanel(af, treeType, modelName, null);
1359   }
1360
1361   @Override
1362   public String orderAlignmentBy(AlignFrame alf, String order,
1363           String undoName, String sep)
1364   {
1365     if (sep == null || sep.length() == 0)
1366     {
1367       sep = separator;
1368     }
1369     String[] ids = separatorListToArray(order, sep);
1370     SequenceI[] sqs = null;
1371     if (ids != null && ids.length > 0)
1372     {
1373       if (alf == null)
1374       {
1375         alf = Jalview.getCurrentAlignFrame();
1376       }
1377       jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
1378               alf.getViewport().getAlignment().getSequencesArray());
1379       int s = 0;
1380       sqs = new SequenceI[ids.length];
1381       for (int i = 0; i < ids.length; i++)
1382       {
1383         if (ids[i].trim().length() == 0)
1384         {
1385           continue;
1386         }
1387         SequenceI sq = matcher.findIdMatch(ids[i]);
1388         if (sq != null)
1389         {
1390           sqs[s++] = sq;
1391         }
1392       }
1393       if (s > 0)
1394       {
1395         SequenceI[] sqq = new SequenceI[s];
1396         System.arraycopy(sqs, 0, sqq, 0, s);
1397         sqs = sqq;
1398       }
1399       else
1400       {
1401         sqs = null;
1402       }
1403     }
1404     if (sqs == null)
1405     {
1406       return "";
1407     }
1408     ;
1409     final AlignmentOrder aorder = new AlignmentOrder(sqs);
1410
1411     if (undoName != null && undoName.trim().length() == 0)
1412     {
1413       undoName = null;
1414     }
1415     final String _undoName = undoName;
1416     // TODO: deal with synchronization here: cannot raise any events until after
1417     // this has returned.
1418     return alf.sortBy(aorder, _undoName) ? "true" : "";
1419   }
1420
1421   @Override
1422   public String orderBy(String order, String undoName)
1423   {
1424     return orderBy(order, undoName, null);
1425   }
1426
1427   @Override
1428   public String orderBy(String order, String undoName, String sep)
1429   {
1430     return orderAlignmentBy(Jalview.getCurrentAlignFrame(), order, undoName,
1431             sep);
1432   }
1433
1434   /**
1435    * Allow an outside entity to initiate the second half of argument parsing
1436    * (only).
1437    * 
1438    * @param args
1439    * @return null is good
1440    */
1441   @Override
1442   public Object parseArguments(String[] args)
1443   {
1444
1445     try
1446     {
1447       Jalview.getInstance().parseArguments(new ArgsParser(args), false);
1448       return null;
1449     } catch (Throwable t)
1450     {
1451       return t;
1452     }
1453   }
1454
1455   @Override
1456   public void removeSelectionListener(AlignFrame af, String listener)
1457   {
1458
1459     List<SelectionListener> listeners = Desktop
1460             .getStructureSelectionManager().getListeners();
1461     for (int i = listeners.size(); --i >= 0;)
1462     {
1463       SelectionListener l = listeners.get(i);
1464       if (l instanceof JsSelectionListener
1465               && ((JsSelectionListener) l).isFor(af, listener))
1466       {
1467         listeners.remove(i);
1468         break;
1469       }
1470     }
1471   }
1472
1473   @Override
1474   public void scrollViewToColumnIn(final AlignFrame alf,
1475           final String leftHandColumn)
1476   {
1477     java.awt.EventQueue.invokeLater(new Runnable()
1478     {
1479
1480       @Override
1481       public void run()
1482       {
1483         try
1484         {
1485           (alf == null ? Jalview.getCurrentAlignFrame() : alf)
1486                   .scrollToColumn(
1487                           Integer.valueOf(leftHandColumn).intValue());
1488
1489         } catch (Exception ex)
1490         {
1491           System.err.println(
1492                   "Couldn't parse integer arguments (leftHandColumn='"
1493                           + leftHandColumn + "')");
1494           ex.printStackTrace();
1495         }
1496       }
1497     });
1498
1499   }
1500
1501   @Override
1502   public void scrollViewToIn(final AlignFrame alf, final String topRow,
1503           final String leftHandColumn)
1504   {
1505     // TODO test
1506     java.awt.EventQueue.invokeLater(new Runnable()
1507     {
1508       @Override
1509       public void run()
1510       {
1511         try
1512         {
1513           (alf == null ? Jalview.getCurrentAlignFrame() : alf).scrollTo(
1514                   Integer.valueOf(topRow).intValue(),
1515                   Integer.valueOf(leftHandColumn).intValue());
1516
1517         } catch (Exception ex)
1518         {
1519           System.err.println("Couldn't parse integer arguments (topRow='"
1520                   + topRow + "' and leftHandColumn='" + leftHandColumn
1521                   + "')");
1522           ex.printStackTrace();
1523         }
1524       }
1525     });
1526   }
1527
1528   @Override
1529   public void scrollViewToRowIn(final AlignFrame alf, final String topRow)
1530   {
1531     // TODO test
1532
1533     java.awt.EventQueue.invokeLater(new Runnable()
1534     {
1535       @Override
1536       public void run()
1537       {
1538         try
1539         {
1540           (alf == null ? Jalview.getCurrentAlignFrame() : alf)
1541                   .scrollToRow(Integer.valueOf(topRow).intValue());
1542
1543         } catch (Exception ex)
1544         {
1545           System.err.println("Couldn't parse integer arguments (topRow='"
1546                   + topRow + "')");
1547           ex.printStackTrace();
1548         }
1549
1550       }
1551     });
1552   }
1553
1554   @Override
1555   public void select(String sequenceIds, String columns)
1556   {
1557     selectIn(Jalview.getCurrentAlignFrame(), sequenceIds, columns, null);
1558   }
1559
1560   @Override
1561   public void select(String sequenceIds, String columns, String sep)
1562   {
1563     selectIn(null, sequenceIds, columns, sep);
1564   }
1565
1566   // @Override
1567   // public AlignFrame newView()
1568   // {
1569   // return newViewFrom(null, null);
1570   // }
1571   //
1572   // @Override
1573   // public AlignFrame newView(String name)
1574   // {
1575   // return newViewFrom(null, name);
1576   // }
1577   //
1578   // @Override
1579   // public AlignFrame newViewFrom(AlignFrame alf)
1580   // {
1581   // return newViewFrom(alf, null);
1582   // }
1583   //
1584   // @Override
1585   // public AlignFrame newViewFrom(AlignFrame alf, String name)
1586   // {
1587   // if (alf == null)
1588   // {
1589   // alf = Jalview.getCurrentAlignFrame();
1590   // }
1591   // return appLoader.newViewFrom(alf, name);
1592   // }
1593
1594   @Override
1595   public void selectIn(AlignFrame alf, String sequenceIds, String columns)
1596   {
1597     selectIn(alf, sequenceIds, columns, null);
1598   }
1599
1600   @Override
1601   public void selectIn(AlignFrame af, String sequenceIds, String columns,
1602           String sep)
1603   {
1604     AlignFrame alf = (af == null ? Jalview.getCurrentAlignFrame() : af);
1605
1606     if (sep == null || sep.length() == 0)
1607     {
1608       sep = separator;
1609     }
1610     else
1611     {
1612       if (debug)
1613       {
1614         System.err.println("Selecting region using separator string '"
1615                 + separator + "'");
1616       }
1617     }
1618     // deparse fields
1619     String[] ids = separatorListToArray(sequenceIds, sep);
1620     String[] cols = separatorListToArray(columns, sep);
1621     final SequenceGroup sel = new SequenceGroup();
1622     final ColumnSelection csel = new ColumnSelection();
1623     AlignmentI al = alf.getViewport().getAlignment();
1624     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
1625             alf.getViewport().getAlignment().getSequencesArray());
1626     int start = 0, end = al.getWidth(), alw = al.getWidth();
1627     boolean seqsfound = true;
1628     if (ids != null && ids.length > 0)
1629     {
1630       seqsfound = false;
1631       for (int i = 0; i < ids.length; i++)
1632       {
1633         if (ids[i].trim().length() == 0)
1634         {
1635           continue;
1636         }
1637         SequenceI sq = matcher.findIdMatch(ids[i]);
1638         if (sq != null)
1639         {
1640           seqsfound = true;
1641           sel.addSequence(sq, false);
1642         }
1643       }
1644     }
1645     boolean inseqpos = false;
1646     if (cols != null && cols.length > 0)
1647     {
1648       boolean seset = false;
1649       for (int i = 0; i < cols.length; i++)
1650       {
1651         String cl = cols[i].trim();
1652         if (cl.length() == 0)
1653         {
1654           continue;
1655         }
1656         int p;
1657         if ((p = cl.indexOf("-")) > -1)
1658         {
1659           int from = -1, to = -1;
1660           try
1661           {
1662             from = Integer.valueOf(cl.substring(0, p)).intValue();
1663             from--;
1664           } catch (NumberFormatException ex)
1665           {
1666             System.err.println(
1667                     "ERROR: Couldn't parse first integer in range element column selection string '"
1668                             + cl + "' - format is 'from-to'");
1669             return;
1670           }
1671           try
1672           {
1673             to = Integer.valueOf(cl.substring(p + 1)).intValue();
1674             to--;
1675           } catch (NumberFormatException ex)
1676           {
1677             System.err.println(
1678                     "ERROR: Couldn't parse second integer in range element column selection string '"
1679                             + cl + "' - format is 'from-to'");
1680             return;
1681           }
1682           if (from >= 0 && to >= 0)
1683           {
1684             // valid range
1685             if (from < to)
1686             {
1687               int t = to;
1688               to = from;
1689               to = t;
1690             }
1691             if (!seset)
1692             {
1693               start = from;
1694               end = to;
1695               seset = true;
1696             }
1697             else
1698             {
1699               // comment to prevent range extension
1700               if (start > from)
1701               {
1702                 start = from;
1703               }
1704               if (end < to)
1705               {
1706                 end = to;
1707               }
1708             }
1709             for (int r = from; r <= to; r++)
1710             {
1711               if (r >= 0 && r < alw)
1712               {
1713                 csel.addElement(r);
1714               }
1715             }
1716             if (debug)
1717             {
1718               System.err.println("Range '" + cl + "' deparsed as [" + from
1719                       + "," + to + "]");
1720             }
1721           }
1722           else
1723           {
1724             System.err.println("ERROR: Invalid Range '" + cl
1725                     + "' deparsed as [" + from + "," + to + "]");
1726           }
1727         }
1728         else
1729         {
1730           int r = -1;
1731           try
1732           {
1733             r = Integer.valueOf(cl).intValue();
1734             r--;
1735           } catch (NumberFormatException ex)
1736           {
1737             if (cl.toLowerCase().equals("sequence"))
1738             {
1739               // we are in the dataset sequence's coordinate frame.
1740               inseqpos = true;
1741             }
1742             else
1743             {
1744               System.err.println(
1745                       "ERROR: Couldn't parse integer from point selection element of column selection string '"
1746                               + cl + "'");
1747               return;
1748             }
1749           }
1750           if (r >= 0 && r <= alw)
1751           {
1752             if (!seset)
1753             {
1754               start = r;
1755               end = r;
1756               seset = true;
1757             }
1758             else
1759             {
1760               // comment to prevent range extension
1761               if (start > r)
1762               {
1763                 start = r;
1764               }
1765               if (end < r)
1766               {
1767                 end = r;
1768               }
1769             }
1770             csel.addElement(r);
1771             if (debug)
1772             {
1773               System.err.println("Point selection '" + cl
1774                       + "' deparsed as [" + r + "]");
1775             }
1776           }
1777           else
1778           {
1779             System.err.println("ERROR: Invalid Point selection '" + cl
1780                     + "' deparsed as [" + r + "]");
1781           }
1782         }
1783       }
1784     }
1785     if (seqsfound)
1786     {
1787       // we only propagate the selection when it was the null selection, or the
1788       // given sequences were found in the alignment.
1789       if (inseqpos && sel.getSize() > 0)
1790       {
1791         // assume first sequence provides reference frame ?
1792         SequenceI rs = sel.getSequenceAt(0);
1793         start = rs.findIndex(start);
1794         end = rs.findIndex(end);
1795         List<Integer> cs = new ArrayList<>(csel.getSelected());
1796         csel.clear();
1797         for (Integer selectedCol : cs)
1798         {
1799           csel.addElement(rs.findIndex(selectedCol));
1800         }
1801       }
1802       sel.setStartRes(start);
1803       sel.setEndRes(end);
1804       EventQueue.invokeLater(new Runnable()
1805       {
1806         @Override
1807         public void run()
1808         {
1809           alf.select(sel, csel,
1810                   alf.getCurrentView().getAlignment().getHiddenColumns());
1811         }
1812       });
1813     }
1814   }
1815
1816   // public AlignFrame newViewFrom(AlignFrame alf, String name)
1817   // {
1818   // return (AlignFrame) alf.newView(name, true);
1819   // }
1820   //
1821   @Override
1822   public String[] separatorListToArray(String list)
1823   {
1824     return separatorListToArray(list, separator);
1825   }
1826
1827   @Override
1828   public void setFeatureGroupState(String groups, boolean state)
1829   { // JalviewLite API
1830     setFeatureGroupStateOn(null, groups, state);
1831   }
1832
1833   @Override
1834   public void setFeatureGroupStateOn(final AlignFrame alf,
1835           final String groups, boolean state)
1836   {
1837
1838     java.awt.EventQueue.invokeLater(new Runnable()
1839     {
1840       @Override
1841       public void run()
1842       {
1843         (alf == null ? Jalview.getCurrentAlignFrame() : alf)
1844                 .setFeatureGroupState(
1845                         separatorListToArray(groups, separator), state);
1846       }
1847     });
1848   }
1849
1850   @Override
1851   public void setSelectionListener(AlignFrame af, String listener)
1852   {
1853     Desktop.getStructureSelectionManager()
1854             .addSelectionListener(new JsSelectionListener(af, listener));
1855   }
1856
1857   @Override
1858   public void setSelectionListener(String listener)
1859   {
1860     Desktop.getStructureSelectionManager()
1861             .addSelectionListener(new JsSelectionListener(null, listener));
1862   }
1863
1864   @Override
1865   public void setSeparator(String separator)
1866   {
1867     this.separator = separator;
1868   }
1869
1870   @Override
1871   public void showOverview()
1872   {
1873     Jalview.getCurrentAlignFrame().overviewMenuItem_actionPerformed(null);
1874   }
1875
1876   /**
1877    * Allowing for a JavaScript function here.
1878    */
1879   public void callInitCallback()
1880   {
1881     Object initjscallback = getAppletParameter("oninit", false);
1882     if (initjscallback != null)
1883     {
1884       try
1885       {
1886         doSendCallback(initjscallback, new Object[0]);
1887       } catch (Exception e)
1888       {
1889         System.err.println("Exception when executing _oninit callback '"
1890                 + initjscallback + "'.");
1891         e.printStackTrace();
1892       }
1893     }
1894   }
1895
1896   /**
1897    * Pass the provided array prepended with Jalview.this
1898    * 
1899    * Appropriated from org.jmol.appletjs.Jmol
1900    * 
1901    * @param callback
1902    *          a window function or "alert"
1903    * @param data
1904    * @return String return from the callback method.
1905    */
1906   public String doSendCallback(Object callback, Object[] data)
1907   {
1908     Jalview me = Jalview.getInstance();
1909
1910     if (me != null && callback != null)
1911     {
1912       /**
1913        * @j2sNative
1914        * 
1915        *            try{
1916        * 
1917        *            if (callback == "alert") { alert(data[0]); return ""; } var
1918        *            o; if (typeof callback == "function") { o = callback; } else
1919        *            { if (!callback)return; var tokens = callback.split("."); o
1920        *            = window[tokens[0]]; for (var i = 1; i < tokens.length; i++)
1921        *            o = o[tokens[i]]; } var a = [me]; for (var i = 0; i <
1922        *            data.length; i++) a.push(data[i] ? data[i].booleanValue &&
1923        *            (data[i] = data[i].booleanValue()) : data[i]); return
1924        *            o.apply(null,a) } catch (e) { System.out.println(callback +
1925        *            " failed " + e); }
1926        */
1927     }
1928     return "";
1929   }
1930
1931   private DataSourceType resolveFileProtocol(String[] retPath)
1932   {
1933     String path = retPath[0];
1934     /*
1935      * is it paste data?
1936      */
1937     if (path.startsWith("PASTE"))
1938     {
1939       retPath[0] = path.substring(5);
1940       return DataSourceType.PASTE;
1941     }
1942
1943     /*
1944      * is it a URL?
1945      */
1946     if (path.indexOf("://") >= 0)
1947     {
1948       return DataSourceType.URL;
1949     }
1950
1951     /*
1952      * try relative to document root
1953      */
1954     URL documentBase = getDocumentBase();
1955     String withDocBase = resolveUrlForLocalOrAbsolute(path, documentBase);
1956     if (HttpUtils.isValidUrl(withDocBase))
1957     {
1958       // if (debug)
1959       // {
1960       // System.err.println("Prepended document base '" + documentBase
1961       // + "' to make: '" + withDocBase + "'");
1962       // }
1963       retPath[0] = withDocBase;
1964       return DataSourceType.URL;
1965     }
1966
1967     /*
1968      * try relative to codebase (if different to document base)
1969      */
1970     URL codeBase = getCodeBase();
1971     String withCodeBase = resolveUrlForLocalOrAbsolute(path, codeBase);
1972     if (!withCodeBase.equals(withDocBase)
1973             && HttpUtils.isValidUrl(withCodeBase))
1974     {
1975       // if (debug)
1976       // {
1977       // System.err.println("Prepended codebase '" + codeBase
1978       // + "' to make: '" + withCodeBase + "'");
1979       // }
1980       retPath[0] = withCodeBase;
1981       return DataSourceType.URL;
1982     }
1983
1984     /*
1985      * try locating by classloader; try this last so files in the directory
1986      * are resolved using document base
1987      */
1988     if (inArchive(getClass(), path))
1989     {
1990       return DataSourceType.CLASSLOADER;
1991     }
1992     return null;
1993   }
1994
1995   /**
1996    * Discovers whether the given file is in the Applet Archive
1997    * 
1998    * @param f
1999    *          String
2000    * @return boolean
2001    */
2002   private static boolean inArchive(Class<?> c, String f)
2003   {
2004     // This might throw a security exception in certain browsers
2005     // Netscape Communicator for instance.
2006     try
2007     {
2008       boolean rtn = (c.getResourceAsStream("/" + f) != null);
2009       return rtn;
2010     } catch (Exception ex)
2011     {
2012       System.out.println("Exception checking resources: " + f + " " + ex);
2013       return false;
2014     }
2015   }
2016
2017   /**
2018    * form a complete URL given a path to a resource and a reference location on
2019    * the same server
2020    * 
2021    * @param targetPath
2022    *          - an absolute path on the same server as localref or a document
2023    *          located relative to localref
2024    * @param localref
2025    *          - a URL on the same server as url
2026    * @return a complete URL for the resource located by url
2027    */
2028   public static String resolveUrlForLocalOrAbsolute(String targetPath,
2029           URL localref)
2030   {
2031     String resolvedPath = "";
2032     if (targetPath.startsWith("/"))
2033     {
2034       String codebase = localref.toString();
2035       String localfile = localref.getFile();
2036       resolvedPath = codebase.substring(0,
2037               codebase.length() - localfile.length()) + targetPath;
2038       return resolvedPath;
2039     }
2040
2041     /*
2042      * get URL path and strip off any trailing file e.g.
2043      * www.jalview.org/examples/index.html#applets?a=b is trimmed to
2044      * www.jalview.org/examples/
2045      */
2046     String urlPath = localref.toString();
2047     String directoryPath = urlPath;
2048     int lastSeparator = directoryPath.lastIndexOf("/");
2049     if (lastSeparator > 0)
2050     {
2051       directoryPath = directoryPath.substring(0, lastSeparator + 1);
2052     }
2053
2054     if (targetPath.startsWith("/"))
2055     {
2056       /*
2057        * construct absolute URL to a file on the server - this is not allowed?
2058        */
2059       // String localfile = localref.getFile();
2060       // resolvedPath = urlPath.substring(0,
2061       // urlPath.length() - localfile.length())
2062       // + targetPath;
2063       resolvedPath = directoryPath + targetPath.substring(1);
2064     }
2065     else
2066     {
2067       resolvedPath = directoryPath + targetPath;
2068     }
2069     // if (debug)
2070     // {
2071     // System.err.println(
2072     // "resolveUrlForLocalOrAbsolute returning " + resolvedPath);
2073     // }
2074     return resolvedPath;
2075   }
2076
2077   /**
2078    * parse the string into a list
2079    * 
2080    * @param list
2081    * @param separator
2082    * @return elements separated by separator
2083    */
2084   public static String[] separatorListToArray(String list, String separator)
2085   {
2086     // TODO use StringUtils version (slightly different...)
2087     int seplen = separator.length();
2088     if (list == null || list.equals("") || list.equals(separator))
2089     {
2090       return null;
2091     }
2092     Vector<String> jv = new Vector<>();
2093     int cp = 0, pos;
2094     while ((pos = list.indexOf(separator, cp)) > cp)
2095     {
2096       jv.addElement(list.substring(cp, pos));
2097       cp = pos + seplen;
2098     }
2099     if (cp < list.length())
2100     {
2101       String c = list.substring(cp);
2102       if (!c.equals(separator))
2103       {
2104         jv.addElement(c);
2105       }
2106     }
2107     if (jv.size() > 0)
2108     {
2109       String[] v = new String[jv.size()];
2110       for (int i = 0; i < v.length; i++)
2111       {
2112         v[i] = jv.elementAt(i);
2113       }
2114       jv.removeAllElements();
2115       return v;
2116     }
2117     return null;
2118   }
2119
2120   public class JsSelectionListener
2121           implements jalview.structure.SelectionListener
2122   {
2123
2124     AlignFrame _af;
2125
2126     String _listener;
2127
2128     public JsSelectionListener(AlignFrame af, String listener)
2129     {
2130       _af = af;
2131       _listener = listener;
2132     }
2133
2134     @Override
2135     public void selection(SequenceGroup seqsel, ColumnSelection colsel,
2136             HiddenColumns hidden, SelectionSource source)
2137     {
2138       // System.err.println("Testing selection event relay to
2139       // jsfunction:"+_listener);
2140       String setid = "";
2141       AlignFrame src = _af;
2142       if (source != null)
2143       {
2144         if (source instanceof AlignViewport
2145                 && Jalview.getCurrentAlignFrame().getViewport() == source)
2146         {
2147           src = Jalview.getCurrentAlignFrame();
2148           if (src != _af)
2149             return;
2150         }
2151       }
2152       String[] seqs = new String[] {};
2153       String[] cols = new String[] {};
2154       int strt = 0, end = (src == null) ? -1
2155               : src.alignPanel.av.getAlignment().getWidth();
2156       if (seqsel != null && seqsel.getSize() > 0)
2157       {
2158         seqs = new String[seqsel.getSize()];
2159         for (int i = 0; i < seqs.length; i++)
2160         {
2161           seqs[i] = seqsel.getSequenceAt(i).getName();
2162         }
2163         if (strt < seqsel.getStartRes())
2164         {
2165           strt = seqsel.getStartRes();
2166         }
2167         if (end == -1 || end > seqsel.getEndRes())
2168         {
2169           end = seqsel.getEndRes();
2170         }
2171       }
2172       if (colsel != null && !colsel.isEmpty())
2173       {
2174         if (end == -1)
2175         {
2176           end = colsel.getMax() + 1;
2177         }
2178         cols = new String[colsel.getSelected().size()];
2179         for (int i = 0; i < cols.length; i++)
2180         {
2181           cols[i] = "" + (1 + colsel.getSelected().get(i).intValue());
2182         }
2183       }
2184       else
2185       {
2186         if (seqsel != null && seqsel.getSize() > 0)
2187         {
2188           // send a valid range, otherwise we send the empty selection
2189           cols = new String[2];
2190           cols[0] = "" + (1 + strt) + "-" + (1 + end);
2191         }
2192         ;
2193
2194       }
2195
2196       Jalview jalview = Jalview.getInstance();
2197       jalview.doSendCallback(_listener,
2198               new Object[]
2199               { src, setid, jalview.arrayToSeparatorList(seqs),
2200                   jalview.arrayToSeparatorList(cols) });
2201     }
2202
2203     public boolean isFor(AlignFrame af, String listener)
2204     {
2205       return _af == af && _listener.contentEquals(listener);
2206     }
2207
2208   }
2209
2210   @Override
2211   public AlignViewportI getViewport()
2212   {
2213     return Jalview.getCurrentAlignFrame().getViewport();
2214   }
2215
2216 }