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