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