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