added mechanism to initiate DAS feature fetching programmatically from jalview.bin...
[jalview.git] / src / jalview / ws / DasSequenceFeatureFetcher.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  *\r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  *\r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  *\r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 package jalview.ws;\r
20 \r
21 import java.net.*;\r
22 import java.util.*;\r
23 \r
24 import javax.swing.*;\r
25 \r
26 import org.biojava.dasobert.das.*;\r
27 import org.biojava.dasobert.das2.*;\r
28 import org.biojava.dasobert.das2.io.*;\r
29 import org.biojava.dasobert.dasregistry.*;\r
30 import org.biojava.dasobert.eventmodel.*;\r
31 import jalview.bin.Cache;\r
32 import jalview.datamodel.*;\r
33 import jalview.gui.*;\r
34 \r
35 /**\r
36  * DOCUMENT ME!\r
37  *\r
38  * @author $author$\r
39  * @version $Revision$\r
40  */\r
41 public class DasSequenceFeatureFetcher\r
42 {\r
43   SequenceI[] sequences;\r
44 \r
45   AlignFrame af;\r
46 \r
47   FeatureSettings fsettings;\r
48 \r
49   StringBuffer sbuffer = new StringBuffer();\r
50 \r
51   Vector selectedSources;\r
52 \r
53   boolean cancelled = false;\r
54 \r
55   long startTime;\r
56 \r
57   /**\r
58    * Creates a new SequenceFeatureFetcher object.\r
59    * Uses default\r
60    *\r
61    * @param align DOCUMENT ME!\r
62    * @param ap DOCUMENT ME!\r
63    */\r
64   public DasSequenceFeatureFetcher(SequenceI[] sequences,\r
65           FeatureSettings fsettings, Vector selectedSources)\r
66   {\r
67     this(sequences, fsettings, selectedSources, true, true);\r
68   }\r
69   public DasSequenceFeatureFetcher(SequenceI[] sequences,\r
70           FeatureSettings fsettings, Vector selectedSources, boolean checkDbrefs, boolean promptFetchDbrefs)\r
71   {\r
72     this.selectedSources = selectedSources;\r
73     this.sequences = sequences;\r
74     if (fsettings!=null)\r
75     {\r
76       this.fsettings = fsettings;\r
77       this.af = fsettings.af;\r
78       af.getViewport().setShowSequenceFeatures(true);\r
79     }\r
80     int uniprotCount = 0;\r
81     for (int i = 0; i < selectedSources.size(); i++)\r
82     {\r
83       DasSource source = (DasSource) selectedSources.elementAt(i);\r
84       DasCoordinateSystem[] coords = source.getCoordinateSystem();\r
85       for (int c = 0; c < coords.length; c++)\r
86       {\r
87         if (coords[c].getName().indexOf("UniProt") > -1)\r
88         {\r
89           uniprotCount++;\r
90           break;\r
91         }\r
92       }\r
93     }\r
94 \r
95     int refCount = 0;\r
96     for (int i = 0; i < sequences.length; i++)\r
97     {\r
98       DBRefEntry[] dbref = sequences[i].getDBRef();\r
99       if (dbref != null)\r
100       {\r
101         for (int j = 0; j < dbref.length; j++)\r
102         {\r
103           if (dbref[j].getSource().equals(\r
104                   jalview.datamodel.DBRefSource.UNIPROT))\r
105           {\r
106             refCount++;\r
107             break;\r
108           }\r
109         }\r
110       }\r
111     }\r
112 \r
113     if (checkDbrefs && refCount < sequences.length && uniprotCount > 0)\r
114     {\r
115 \r
116       int reply = JOptionPane.YES_OPTION;\r
117       if (promptFetchDbrefs)\r
118       {\r
119         reply = JOptionPane.showInternalConfirmDialog(Desktop.desktop,\r
120              "Do you want Jalview to find\n"\r
121                       + "Uniprot Accession ids for given sequence names?",\r
122               "Find Uniprot Accession Ids", JOptionPane.YES_NO_OPTION,\r
123               JOptionPane.QUESTION_MESSAGE);\r
124       }\r
125               \r
126 \r
127       if (reply == JOptionPane.YES_OPTION)\r
128       {\r
129         Thread thread = new Thread(new FetchDBRefs());\r
130         thread.start();\r
131       }\r
132       else\r
133       {\r
134         startFetching();\r
135       }\r
136     }\r
137     else\r
138     {\r
139       startFetching();\r
140     }\r
141 \r
142   }\r
143 \r
144   class FetchDBRefs implements Runnable\r
145   {\r
146     public void run()\r
147     {\r
148       new DBRefFetcher(sequences, af).fetchDBRefs(true);\r
149       startFetching();\r
150     }\r
151   }\r
152 \r
153   /**\r
154    * Spawns a number of dasobert Fetcher threads to add features to sequences in the dataset\r
155    */\r
156   void startFetching()\r
157   {\r
158     cancelled = false;\r
159     startTime = System.currentTimeMillis();\r
160     af.setProgressBar("Fetching DAS Sequence Features", startTime);\r
161 \r
162     DasSource[] sources = new jalview.gui.DasSourceBrowser().getDASSource();\r
163 \r
164     if (selectedSources == null || selectedSources.size() == 0)\r
165     {\r
166       String active = jalview.bin.Cache.getDefault("DAS_ACTIVE_SOURCE",\r
167               "uniprot");\r
168       StringTokenizer st = new StringTokenizer(active, "\t");\r
169       Vector selectedSources = new Vector();\r
170       String token;\r
171       while (st.hasMoreTokens())\r
172       {\r
173         token = st.nextToken();\r
174         for (int i = 0; i < sources.length; i++)\r
175         {\r
176           if (sources[i].getNickname().equals(token))\r
177           {\r
178             selectedSources.addElement(sources[i]);\r
179             break;\r
180           }\r
181         }\r
182       }\r
183     }\r
184 \r
185     if (selectedSources == null || selectedSources.size() == 0)\r
186     {\r
187       System.out.println("No DAS Sources active");\r
188       cancelled = true;\r
189       setGuiNoDassourceActive();\r
190       return;\r
191     }\r
192 \r
193     sourcesRemaining = selectedSources.size();\r
194     //Now sending requests one at a time to each server\r
195     for (int sourceIndex = 0; sourceIndex < selectedSources.size()\r
196             && !cancelled; sourceIndex++)\r
197     {\r
198       DasSource dasSource = (DasSource) selectedSources\r
199               .elementAt(sourceIndex);\r
200 \r
201       nextSequence(dasSource, sequences[0]);\r
202     }\r
203   }\r
204 \r
205   private void setGuiNoDassourceActive()\r
206   {\r
207 \r
208     if (af!=null)\r
209     {\r
210       af.setProgressBar("No DAS Sources Active", startTime);\r
211     }\r
212     if (getFeatSettings()!=null)\r
213     {\r
214       fsettings.noDasSourceActive();\r
215      }\r
216   }\r
217 \r
218   /**\r
219    * Update our fsettings dialog reference if we didn't have one when we were first initialised.\r
220    * @return fsettings\r
221    */\r
222   private FeatureSettings getFeatSettings()\r
223   {\r
224     if (fsettings == null)\r
225     {\r
226       if (af != null)\r
227       {\r
228         fsettings = af.featureSettings;\r
229       }\r
230     }\r
231     return fsettings;\r
232   }\r
233 \r
234   public void cancel()\r
235   {\r
236     if (af!=null)\r
237     {\r
238       af.setProgressBar("DAS Feature Fetching Cancelled", startTime);\r
239     }\r
240     cancelled = true;\r
241   }\r
242 \r
243   int sourcesRemaining = 0;\r
244 \r
245   void responseComplete(DasSource dasSource, SequenceI seq)\r
246   {\r
247     if (seq != null)\r
248     {\r
249       for (int seqIndex = 0; seqIndex < sequences.length - 1 && !cancelled; seqIndex++)\r
250       {\r
251         if (sequences[seqIndex] == seq)\r
252         {\r
253           nextSequence(dasSource, sequences[++seqIndex]);\r
254           return;\r
255         }\r
256       }\r
257     }\r
258 \r
259     sourcesRemaining--;\r
260 \r
261     if (sourcesRemaining == 0)\r
262     {\r
263       System.err.println("Fetching Complete.");\r
264       setGuiFetchComplete();\r
265     }\r
266 \r
267   }\r
268 \r
269   private void setGuiFetchComplete()\r
270   {\r
271 \r
272     if (af != null)\r
273     {\r
274       af.setProgressBar("DAS Feature Fetching Complete", startTime);\r
275     }\r
276 \r
277     if (af != null && af.featureSettings != null)\r
278     {\r
279       af.featureSettings.setTableData();\r
280     }\r
281 \r
282     if (getFeatSettings() != null)\r
283     {\r
284       fsettings.complete();\r
285     }\r
286   }\r
287 \r
288   void featuresAdded(SequenceI seq)\r
289   {\r
290     if (af==null)\r
291     {\r
292       // no gui to update with features.\r
293       return;\r
294     }\r
295     af.getFeatureRenderer().featuresAdded();\r
296 \r
297     int start = af.getViewport().getStartSeq();\r
298     int end = af.getViewport().getEndSeq();\r
299     int index;\r
300     for (index = start; index < end; index++)\r
301     {\r
302       if (seq == af.getViewport().getAlignment().getSequenceAt(index)\r
303               .getDatasetSequence())\r
304       {\r
305         af.alignPanel.paintAlignment(true);\r
306         break;\r
307       }\r
308     }\r
309   }\r
310 \r
311   void nextSequence(DasSource dasSource, SequenceI seq)\r
312   {\r
313     if (cancelled)\r
314       return;\r
315     DBRefEntry[] uprefs = jalview.util.DBRefUtils.selectRefs(\r
316             seq.getDBRef(), new String[]\r
317             {\r
318             //  jalview.datamodel.DBRefSource.PDB,\r
319             jalview.datamodel.DBRefSource.UNIPROT,\r
320             //  jalview.datamodel.DBRefSource.EMBL - not tested on any EMBL coord sys sources\r
321             });\r
322     // TODO: minimal list of DAS queries to make by querying with untyped ID if distinct from any typed IDs\r
323 \r
324     boolean dasCoordSysFound = false;\r
325 \r
326     if (uprefs != null)\r
327     {\r
328       // do any of these ids match the source's coordinate system ?\r
329       for (int j = 0; !dasCoordSysFound && j < uprefs.length; j++)\r
330       {\r
331         DasCoordinateSystem cs[] = dasSource.getCoordinateSystem();\r
332 \r
333         for (int csIndex = 0; csIndex < cs.length && !dasCoordSysFound; csIndex++)\r
334         {\r
335           if (cs.length > 0\r
336                   && jalview.util.DBRefUtils.isDasCoordinateSystem(\r
337                           cs[csIndex].getName(), uprefs[j]))\r
338           {\r
339             Cache.log.debug("Launched fetcher for coordinate system "\r
340                     + cs[0].getName());\r
341             //  Will have to pass any mapping information to the fetcher\r
342             //- the start/end for the DBRefEntry may not be the same as the sequence's start/end\r
343 \r
344             System.out.println(seq.getName() + " "\r
345                     + (seq.getDatasetSequence() == null) + " "\r
346                     + dasSource.getUrl());\r
347 \r
348             dasCoordSysFound = true; // break's out of the loop\r
349             createFeatureFetcher(seq, dasSource, uprefs[j]);\r
350           }\r
351           else\r
352             System.out.println("IGNORE " + cs[csIndex].getName());\r
353         }\r
354       }\r
355     }\r
356 \r
357     if (!dasCoordSysFound)\r
358     {\r
359       String id = null;\r
360       // try and use the name as the sequence id\r
361       if (seq.getName().indexOf("|") > -1)\r
362       {\r
363         id = seq.getName().substring(seq.getName().lastIndexOf("|") + 1);\r
364         if (id.trim().length()<4)\r
365         {\r
366           // hack - we regard a significant ID as being at least 4 non-whitespace characters\r
367           id = seq.getName().substring(0, seq.getName().lastIndexOf("|"));\r
368           if (id.indexOf("|")>-1)\r
369           {\r
370             id = id.substring(id.lastIndexOf("|")+1);\r
371           }\r
372         }\r
373       }\r
374       else\r
375       {\r
376         id = seq.getName();\r
377       }\r
378       if (id != null)\r
379       {\r
380         // Should try to call a general feature fetcher that\r
381         // queries many sources with name to discover applicable ID references\r
382         createFeatureFetcher(seq, dasSource, id);\r
383       }\r
384     }\r
385 \r
386   }\r
387 \r
388   /**\r
389    * fetch and add das features to a sequence using the given source URL and compatible DbRef id.\r
390    * new features are mapped using the DbRef mapping to the local coordinate system.\r
391    * @param seq\r
392    * @param SourceUrl\r
393    * @param dbref\r
394    */\r
395   protected void createFeatureFetcher(final SequenceI seq,\r
396           final DasSource dasSource, final DBRefEntry dbref)\r
397   {\r
398 \r
399     //////////////\r
400     /// fetch DAS features\r
401     final Das1Source source = new Das1Source();\r
402     source.setUrl(dasSource.getUrl());\r
403     source.setNickname(dasSource.getNickname());\r
404     if (dbref == null || dbref.getAccessionId() == null\r
405             || dbref.getAccessionId().length() < 1)\r
406     {\r
407       responseComplete(dasSource, seq); // reduce thread count anyhow\r
408       return;\r
409     }\r
410     Cache.log.debug("new Das Feature Fetcher for " + dbref.getSource()\r
411             + ":" + dbref.getAccessionId() + " querying "\r
412             + dasSource.getUrl());\r
413     FeatureThread fetcher = new FeatureThread(dbref.getAccessionId()\r
414     //  +  ":" + start + "," + end,\r
415             , source);\r
416 \r
417     fetcher.addFeatureListener(new FeatureListener()\r
418     {\r
419       public void comeBackLater(FeatureEvent e)\r
420       {\r
421         responseComplete(dasSource, seq);\r
422         Cache.log.debug("das source " + e.getDasSource().getNickname()\r
423                 + " asked us to come back in " + e.getComeBackLater()\r
424                 + " secs.");\r
425       }\r
426 \r
427       public void newFeatures(FeatureEvent e)\r
428       {\r
429 \r
430         Das1Source ds = e.getDasSource();\r
431 \r
432         Map[] features = e.getFeatures();\r
433         // add features to sequence\r
434         Cache.log.debug("das source " + ds.getUrl() + " returned "\r
435                 + features.length + " features");\r
436 \r
437         if (features.length > 0)\r
438         {\r
439           for (int i = 0; i < features.length; i++)\r
440           {\r
441             SequenceFeature f = newSequenceFeature(features[i], source\r
442                     .getNickname());\r
443             if (dbref.getMap() != null && f.getBegin() > 0\r
444                     && f.getEnd() > 0)\r
445             {\r
446               Cache.log.debug("mapping from " + f.getBegin() + " - "\r
447                       + f.getEnd());\r
448               SequenceFeature vf[] = null;\r
449 \r
450               try\r
451               {\r
452                 vf = dbref.getMap().locateFeature(f);\r
453               } catch (Exception ex)\r
454               {\r
455                 Cache.log\r
456                         .info("Error in 'experimental' mapping of features. Please try to reproduce and then report info to help@jalview.org.");\r
457                 Cache.log.info("Mapping feature from " + f.getBegin()\r
458                         + " to " + f.getEnd() + " in dbref "\r
459                         + dbref.getAccessionId() + " in "\r
460                         + dbref.getSource());\r
461                 Cache.log.info("using das Source " + ds.getUrl());\r
462                 Cache.log.info("Exception", ex);\r
463               }\r
464 \r
465               if (vf != null)\r
466               {\r
467                 for (int v = 0; v < vf.length; v++)\r
468                 {\r
469                   Cache.log.debug("mapping to " + v + ": "\r
470                           + vf[v].getBegin() + " - " + vf[v].getEnd());\r
471                   seq.addSequenceFeature(vf[v]);\r
472                 }\r
473               }\r
474             }\r
475             else\r
476             {\r
477               seq.addSequenceFeature(f);\r
478             }\r
479           }\r
480 \r
481           featuresAdded(seq);\r
482         }\r
483         else\r
484         {\r
485           //  System.out.println("No features found for " + seq.getName()\r
486           //                     + " from: " + e.getDasSource().getNickname());\r
487         }\r
488         responseComplete(dasSource, seq);\r
489 \r
490       }\r
491     }\r
492 \r
493     );\r
494 \r
495     fetcher.start();\r
496   }\r
497 \r
498   protected void createFeatureFetcher(final SequenceI seq,\r
499           final DasSource dasSource, String id)\r
500   {\r
501     //////////////\r
502     /// fetch DAS features\r
503     final Das1Source source = new Das1Source();\r
504     source.setUrl(dasSource.getUrl());\r
505     source.setNickname(dasSource.getNickname());\r
506     \r
507     if (id!=null)\r
508     {\r
509       id = id.trim();\r
510     }\r
511     if (id != null && id.length() > 0)\r
512     {\r
513       Cache.log.debug("new Das Feature Fetcher for " + id + " querying "\r
514               + dasSource.getUrl());\r
515       FeatureThread fetcher = new FeatureThread(id\r
516       //  +  ":" + start + "," + end,\r
517               , source);\r
518 \r
519       fetcher.addFeatureListener(new FeatureListener()\r
520       {\r
521         public void comeBackLater(FeatureEvent e)\r
522         {\r
523           responseComplete(dasSource, seq);\r
524           Cache.log.debug("das source " + e.getDasSource().getNickname()\r
525                   + " asked us to come back in " + e.getComeBackLater()\r
526                   + " secs.");\r
527         }\r
528 \r
529         public void newFeatures(FeatureEvent e)\r
530         {\r
531 \r
532           Das1Source ds = e.getDasSource();\r
533 \r
534           Map[] features = e.getFeatures();\r
535           // add features to sequence\r
536           Cache.log.debug("das source " + ds.getUrl() + " returned "\r
537                   + features.length + " features");\r
538 \r
539           if (features.length > 0)\r
540           {\r
541             for (int i = 0; i < features.length; i++)\r
542             {\r
543               SequenceFeature f = newSequenceFeature(features[i], source\r
544                       .getNickname());\r
545 \r
546               seq.addSequenceFeature(f);\r
547             }\r
548 \r
549             featuresAdded(seq);\r
550           }\r
551           else\r
552           {\r
553             //  System.out.println("No features found for " + seq.getName()\r
554             //                     + " from: " + e.getDasSource().getNickname());\r
555           }\r
556           responseComplete(dasSource, seq);\r
557 \r
558         }\r
559       }\r
560 \r
561       );\r
562 \r
563       fetcher.start();\r
564     } else {\r
565       // invalid fetch - indicate it is finished.\r
566       Cache.log.debug("Skipping empty ID for querying "\r
567               + dasSource.getUrl());\r
568       responseComplete(dasSource, seq);\r
569     }\r
570   }\r
571 \r
572   /**\r
573    * creates a jalview sequence feature from a das feature document\r
574    * @param dasfeature\r
575    * @return sequence feature object created using dasfeature information\r
576    */\r
577   SequenceFeature newSequenceFeature(Map dasfeature, String nickname)\r
578   {\r
579     if (dasfeature == null)\r
580     {\r
581       return null;\r
582     }\r
583     try\r
584     {\r
585       /**\r
586        * Different qNames for a DAS Feature - are string keys to the HashMaps in features\r
587        * "METHOD") ||\r
588                   qName.equals("TYPE") ||\r
589                   qName.equals("START") ||\r
590                   qName.equals("END") ||\r
591                   qName.equals("NOTE") ||\r
592                   qName.equals("LINK") ||\r
593                   qName.equals("SCORE")\r
594        */\r
595       String desc = new String();\r
596       if (dasfeature.containsKey("NOTE"))\r
597       {\r
598         desc += (String) dasfeature.get("NOTE");\r
599       }\r
600 \r
601       int start = 0, end = 0;\r
602       float score = 0f;\r
603 \r
604       try\r
605       {\r
606         start = Integer.parseInt(dasfeature.get("START").toString());\r
607       } catch (Exception ex)\r
608       {\r
609       }\r
610       try\r
611       {\r
612         end = Integer.parseInt(dasfeature.get("END").toString());\r
613       } catch (Exception ex)\r
614       {\r
615       }\r
616       try\r
617       {\r
618         score = Integer.parseInt(dasfeature.get("SCORE").toString());\r
619       } catch (Exception ex)\r
620       {\r
621       }\r
622 \r
623       SequenceFeature f = new SequenceFeature((String) dasfeature\r
624               .get("TYPE"), desc, start, end, score, nickname);\r
625 \r
626       if (dasfeature.containsKey("LINK"))\r
627       {\r
628         f.addLink(f.getType() + " " + f.begin + "_" + f.end + "|"\r
629                 + dasfeature.get("LINK"));\r
630       }\r
631 \r
632       return f;\r
633     } catch (Exception e)\r
634     {\r
635       System.out.println("ERRR " + e);\r
636       e.printStackTrace();\r
637       System.out.println("############");\r
638       Cache.log.debug("Failed to parse " + dasfeature.toString(), e);\r
639       return null;\r
640     }\r
641   }\r
642   /**\r
643    * query the default DAS Source Registry for sources.\r
644    * Uses value of jalview property DAS_REGISTRY_URL and the DasSourceBrowser.DEFAULT_REGISTRY if that doesn't exist. \r
645    * @return list of sources\r
646    */\r
647   public static DasSource[] getDASSources()\r
648   {\r
649 \r
650     String registryURL = jalview.bin.Cache.getDefault("DAS_REGISTRY_URL",\r
651             DasSourceBrowser.DEFAULT_REGISTRY);\r
652     return getDASSources(registryURL);\r
653   }\r
654   /**\r
655    * query the given URL for DasSources.\r
656    * @param registryURL\r
657    * return sources from registryURL\r
658    */\r
659   public static DasSource[] getDASSources(String registryURL)\r
660   {\r
661     DasSourceReaderImpl reader = new DasSourceReaderImpl();\r
662 \r
663     try\r
664     {\r
665       URL url = new URL(registryURL);\r
666 \r
667       DasSource[] sources = reader.readDasSource(url);\r
668 \r
669       List das1sources = new ArrayList();\r
670       for (int i = 0; i < sources.length; i++)\r
671       {\r
672         DasSource ds = sources[i];\r
673         if (ds instanceof Das2Source)\r
674         {\r
675           Das2Source d2s = (Das2Source) ds;\r
676           if (d2s.hasDas1Capabilities())\r
677           {\r
678             Das1Source d1s = DasSourceConverter.toDas1Source(d2s);\r
679             das1sources.add(d1s);\r
680           }\r
681 \r
682         }\r
683         else if (ds instanceof Das1Source)\r
684         {\r
685           das1sources.add((Das1Source) ds);\r
686         }\r
687       }\r
688 \r
689       return (Das1Source[]) das1sources.toArray(new Das1Source[das1sources\r
690               .size()]);\r
691     } catch (Exception ex)\r
692     {\r
693       ex.printStackTrace();\r
694       return null;\r
695     }\r
696   }\r
697 \r
698 }\r