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