ad2e37b7210729dea69f6922afe1b53ac8ffa6a4
[jalview.git] / src / jalview / io / DasSequenceFeatureFetcher.java
1 /*\r
2 * Jalview - A Sequence Alignment Editor and Viewer\r
3 * Copyright (C) 2006 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.io;\r
20 \r
21 import jalview.datamodel.*;\r
22 \r
23 import jalview.gui.*;\r
24 \r
25 import java.util.*;\r
26 \r
27 import java.net.URL;\r
28 \r
29 import org.biojava.dasobert.das.FeatureThread;\r
30 import org.biojava.dasobert.eventmodel.FeatureEvent;\r
31 import org.biojava.dasobert.eventmodel.FeatureListener;\r
32 import org.biojava.dasobert.dasregistry.DasSource;\r
33 \r
34 import org.biojava.dasobert.das2.io.DasSourceReaderImpl;\r
35 import org.biojava.dasobert.dasregistry.Das1Source;\r
36 import org.biojava.dasobert.dasregistry.DasSource;\r
37 import org.biojava.dasobert.das2.Das2Source;\r
38 import org.biojava.dasobert.das2.DasSourceConverter;\r
39 \r
40 \r
41 import jalview.bin.Cache;\r
42 import org.biojava.dasobert.dasregistry.DasCoordinateSystem;\r
43 \r
44 import javax.swing.*;\r
45 \r
46 /**\r
47  * DOCUMENT ME!\r
48  *\r
49  * @author $author$\r
50  * @version $Revision$\r
51  */\r
52 public class DasSequenceFeatureFetcher implements Runnable\r
53 {\r
54   SequenceI [] sequences;\r
55   AlignFrame af;\r
56   StringBuffer sbuffer = new StringBuffer();\r
57   Vector selectedSources;\r
58 \r
59   long startTime;\r
60   int threadsRunning = 0;\r
61   boolean allBatchesComplete = false;\r
62 \r
63 \r
64   /**\r
65    * Creates a new SequenceFeatureFetcher object.\r
66    * Uses default\r
67    *\r
68    * @param align DOCUMENT ME!\r
69    * @param ap DOCUMENT ME!\r
70    */\r
71   public DasSequenceFeatureFetcher(SequenceI [] sequences,\r
72                                    final AlignFrame af,\r
73                                    Vector selectedSources)\r
74   {\r
75     this.selectedSources = selectedSources;\r
76     this.sequences = sequences;\r
77     this.af = af;\r
78 \r
79     int uniprotCount = 0;\r
80     for (int i = 0; i < selectedSources.size(); i++)\r
81     {\r
82       DasSource source = (DasSource) selectedSources.elementAt(i);\r
83       DasCoordinateSystem[] coords = source.getCoordinateSystem();\r
84       for (int c = 0; c < coords.length; c++)\r
85       {\r
86         if (coords[c].getName().indexOf("UniProt")>-1)\r
87         {\r
88           uniprotCount++;\r
89           break;\r
90         }\r
91       }\r
92     }\r
93 \r
94     int refCount = 0;\r
95     for(int i=0; i<sequences.length; i++)\r
96     {\r
97       DBRefEntry [] dbref = sequences[i].getDBRef();\r
98       if(dbref!=null)\r
99       {\r
100         for(int j=0; j<dbref.length; j++)\r
101         {\r
102           if(dbref[j].getSource()\r
103              .equals(jalview.datamodel.DBRefSource.UNIPROT))\r
104           {\r
105             refCount++;\r
106             break;\r
107           }\r
108         }\r
109       }\r
110     }\r
111 \r
112     if(refCount<sequences.length && uniprotCount>0)\r
113     {\r
114 \r
115      int reply = JOptionPane.showInternalConfirmDialog(Desktop.desktop,\r
116           "Do you want Jalview to find\n"\r
117          +"Uniprot Accession ids for given sequence names?",\r
118          "Find Uniprot Accession Ids",\r
119          JOptionPane.YES_NO_OPTION,\r
120          JOptionPane.QUESTION_MESSAGE);\r
121 \r
122      if(reply == JOptionPane.YES_OPTION)\r
123      {\r
124        Thread thread = new Thread(new FetchDBRefs());\r
125        thread.start();\r
126      }\r
127      else\r
128       startFetching();\r
129     }\r
130     else\r
131       startFetching();\r
132 \r
133   /* System.out.println("User selection is "   +\r
134                        ( ( (float) uniprotCount / (float) selectedSources.size()) * 100)\r
135                       + " % Uniprot,  and "+refCount+" / " +sequences.length+" have uniprot accession");\r
136 */\r
137   }\r
138 \r
139   void startFetching()\r
140   {\r
141     Thread thread = new Thread(this);\r
142     thread.start();\r
143   }\r
144 \r
145   class FetchDBRefs implements Runnable\r
146   {\r
147     public void run()\r
148     {\r
149       new DBRefFetcher(\r
150              af.getViewport().getAlignment(), af).fetchDBRefs(true);\r
151       startFetching();\r
152     }\r
153   }\r
154 \r
155 \r
156   /**\r
157    * creates a jalview sequence feature from a das feature document\r
158    * @param dasfeature\r
159    * @return sequence feature object created using dasfeature information\r
160    */\r
161   SequenceFeature newSequenceFeature(Map dasfeature, String nickname)\r
162   {\r
163           try {\r
164                 /**\r
165            * Different qNames for a DAS Feature - are string keys to the HashMaps in features\r
166            * "METHOD") ||\r
167            qName.equals("TYPE") ||\r
168            qName.equals("START") ||\r
169            qName.equals("END") ||\r
170            qName.equals("NOTE") ||\r
171            qName.equals("LINK") ||\r
172            qName.equals("SCORE")\r
173            */\r
174                   String desc = new String();\r
175                   if (dasfeature.containsKey("NOTE"))\r
176                                 desc+=(String) dasfeature.get("NOTE");\r
177 \r
178 \r
179                   int start = 0, end = 0;\r
180                   float score = 0f;\r
181 \r
182                   try{ start = Integer.parseInt( dasfeature.get("START").toString()); }\r
183                   catch( Exception ex){}\r
184                   try{ end = Integer.parseInt( dasfeature.get("END").toString()); }\r
185                   catch (Exception ex){}\r
186                   try{ score = Integer.parseInt( dasfeature.get("SCORE").toString()); }\r
187                   catch (Exception ex){}\r
188 \r
189 \r
190                   SequenceFeature f = new SequenceFeature(\r
191                                   (String) dasfeature.get("TYPE"),\r
192                                   desc,\r
193                                   start,\r
194                                   end,\r
195                                   score,\r
196                                   nickname);\r
197 \r
198                   if (dasfeature.containsKey("LINK"))\r
199                  {\r
200                       f.addLink(f.getType()+" "+f.begin+"_"+f.end\r
201                                 +"|"+ dasfeature.get("LINK"));\r
202                   }\r
203                   // (String) dasfeature.get("ID"),\r
204                   ////  (String) dasfeature.get("METHOD"),\r
205                                 //  (String) dasfeature.get("SCORE"),\r
206                                 //  null\r
207                         // );\r
208 \r
209           return f;\r
210           }\r
211           catch (Exception e) {\r
212             System.out.println("ERRR "+e);\r
213             e.printStackTrace();\r
214             System.out.println("############");\r
215                   Cache.log.debug("Failed to parse "+dasfeature.toString(), e);\r
216                   return null;\r
217           }\r
218   }\r
219   /**\r
220    * fetch and add das features to a sequence using the given source URL and Id to create a feature request\r
221    * @param seq\r
222    * @param SourceUrl\r
223    * @param id\r
224    */\r
225   protected void createFeatureFetcher(final SequenceI seq,\r
226                                       final String sourceUrl,\r
227                                       String id,\r
228                                       String nickname)\r
229   {\r
230           //////////////\r
231           /// fetch DAS features\r
232           final Das1Source source = new Das1Source();\r
233           source.setUrl(sourceUrl);\r
234           source.setNickname(nickname);\r
235 \r
236 \r
237           Cache.log.debug("new Das Feature Fetcher for " + id + " querying " +\r
238                           sourceUrl);\r
239 \r
240           if (id != null && id.length() > 0)\r
241           {\r
242             setThreadsRunning(+1);\r
243 \r
244           //  int start=seq.getStart(), end = seq.getEnd();\r
245           /*  if(af.getViewport().getSelectionGroup()!=null)\r
246             {\r
247               SequenceI tmp = af.getViewport().getAlignment().findName(seq.getName());\r
248               start = tmp.findPosition(\r
249               af.getViewport().getSelectionGroup().getStartRes()\r
250                   );\r
251 \r
252               end = tmp.findPosition(\r
253                  af.getViewport().getSelectionGroup().getEndRes()\r
254                  );\r
255             }*/\r
256 \r
257             FeatureThread fetcher = new FeatureThread(id\r
258                                                    //  +  ":" + start + "," + end,\r
259                                                       , source);\r
260 \r
261 \r
262             fetcher.addFeatureListener(new FeatureListener()\r
263             {\r
264               public void comeBackLater(FeatureEvent e)\r
265               {\r
266                 setThreadsRunning(-1);\r
267                 Cache.log.debug("das source " + e.getDasSource().getNickname() +\r
268                                 " asked us to come back in " + e.getComeBackLater() +\r
269                                 " secs.");\r
270               }\r
271 \r
272               public void newFeatures(FeatureEvent e)\r
273               {\r
274                 setThreadsRunning(-1);\r
275                 Das1Source ds = e.getDasSource();\r
276 \r
277                 Map[] features = e.getFeatures();\r
278                 // add features to sequence\r
279                 Cache.log.debug("das source " + ds.getUrl() + " returned " +\r
280                                 features.length + " features");\r
281 \r
282                 if (features.length > 0)\r
283                 {\r
284                   for (int i = 0; i < features.length; i++)\r
285                   {\r
286                       SequenceFeature f = newSequenceFeature(features[i],\r
287                         source.getNickname());\r
288 \r
289                       seq.addSequenceFeature(f);\r
290                   }\r
291                 }\r
292                 else\r
293                 {\r
294                   System.out.println("No features found for sequence " +seq.getName()\r
295                                      +" from: "+ e.getDasSource().getNickname());\r
296                 }\r
297 \r
298               }\r
299             }\r
300 \r
301             );\r
302 \r
303             //NOTE alignPanel listener will be called after the previous\r
304             //anonymous listener!!!\r
305             fetcher.addFeatureListener(af);\r
306 \r
307             fetcher.start();\r
308           }\r
309   }\r
310 \r
311   synchronized void setThreadsRunning(int i)\r
312   {\r
313     threadsRunning += i;\r
314     if(threadsRunning<1 && allBatchesComplete)\r
315     {\r
316       af.setProgressBar("DAS Feature Fetching Complete", startTime);\r
317 \r
318       if(af.featureSettings!=null)\r
319         af.featureSettings.setTableData();\r
320     }\r
321   }\r
322 \r
323   /**\r
324    * Spawns a number of dasobert Fetcher threads to add features to sequences in the dataset\r
325    */\r
326   public void run()\r
327   {\r
328     startTime = System.currentTimeMillis();\r
329     af.setProgressBar("Fetching DAS Sequence Features", startTime);\r
330 \r
331     DasSource [] sources = new jalview.gui.DasSourceBrowser().getDASSource();\r
332 \r
333     if(selectedSources==null || selectedSources.size()==0)\r
334     {\r
335       String active = jalview.bin.Cache.getDefault("DAS_ACTIVE_SOURCE", "uniprot");\r
336       StringTokenizer st = new StringTokenizer(active, "\t");\r
337       Vector selectedSources = new Vector();\r
338       String token;\r
339       while (st.hasMoreTokens())\r
340       {\r
341         token = st.nextToken();\r
342         for (int i = 0; i < sources.length; i++)\r
343         {\r
344           if (sources[i].getNickname().equals(token))\r
345           {\r
346             selectedSources.addElement(sources[i]);\r
347             break;\r
348           }\r
349         }\r
350       }\r
351     }\r
352 \r
353     if(selectedSources == null || selectedSources.size()==0)\r
354     {\r
355       System.out.println("No DAS Sources active");\r
356       af.setProgressBar("No DAS Sources Active", startTime);\r
357       return;\r
358     }\r
359 \r
360     try\r
361     {\r
362       //We must limit the feature fetching to 20 to prevent\r
363       //Servers being overloaded with too many requests\r
364       int batchCount = 0;\r
365       int batchMaxSize = 10;\r
366       int seqIndex = 0;\r
367       while (seqIndex < sequences.length)\r
368       {\r
369           batchCount ++;\r
370           if(batchCount>=batchMaxSize)\r
371           {\r
372             waitTillBatchComplete();\r
373             batchCount = 0;\r
374           }\r
375 \r
376           DBRefEntry [] uprefs = jalview.util.DBRefUtils.selectRefs(sequences[seqIndex].getDBRef(),\r
377               new String[]  {\r
378               jalview.datamodel.DBRefSource.PDB,\r
379               jalview.datamodel.DBRefSource.UNIPROT});\r
380 \r
381           for(int sourceIndex=0; sourceIndex<selectedSources.size(); sourceIndex++)\r
382           {\r
383             DasSource dasSource = (DasSource)selectedSources.elementAt(sourceIndex);\r
384 \r
385             if (uprefs != null)\r
386             {\r
387               // we know the id for this entry, so don't note its ID in the unknownSequences list\r
388               for (int j = 0;  j < uprefs.length; j++)\r
389               {\r
390 \r
391                 // Will have to pass any mapping information to the fetcher - the start/end for the DBRefEntry may not be the same as the sequence's start/end\r
392                 DasCoordinateSystem cs[] = dasSource.getCoordinateSystem();\r
393                 for (int l=0; l<cs.length; l++)\r
394                 {\r
395                   if (jalview.util.DBRefUtils.isDasCoordinateSystem(cs[l].getName(), uprefs[j]))\r
396                   {\r
397                     Cache.log.debug("Launched fetcher for coordinate system " +\r
398                                     cs[l].getName());\r
399 \r
400 \r
401                     createFeatureFetcher(sequences[seqIndex],\r
402                                          dasSource.getUrl(),\r
403                                           uprefs[j].getAccessionId(),\r
404                                          dasSource.getNickname());\r
405                   }\r
406                 }\r
407               }\r
408             }\r
409             else\r
410             {\r
411               String id = null;\r
412               // try and use the name as the sequence id\r
413               if (sequences[seqIndex].getName().indexOf("|") > -1)\r
414               {\r
415                 id = sequences[seqIndex].getName().substring(\r
416                     sequences[seqIndex].getName().lastIndexOf("|") + 1);\r
417               }\r
418               else\r
419               {\r
420                 id = sequences[seqIndex].getName();\r
421               }\r
422               if (id != null)\r
423               {\r
424                 // Should try to call a general feature fetcher that queries many sources with name to discover applicable ID references\r
425                 createFeatureFetcher(sequences[seqIndex],\r
426                                      dasSource.getUrl(),\r
427                                      id,\r
428                                      dasSource.getNickname());\r
429               }\r
430             }\r
431           }\r
432 \r
433           seqIndex++;\r
434     }\r
435     }\r
436     catch (Exception ex)\r
437     {\r
438       ex.printStackTrace();\r
439     }\r
440     allBatchesComplete = true;\r
441   }\r
442 \r
443   void waitTillBatchComplete()\r
444   {\r
445     while( threadsRunning > 0 )\r
446     {\r
447       try{\r
448         Thread.sleep(500);\r
449 \r
450       }catch(Exception ex)\r
451       {\r
452         ex.printStackTrace();\r
453       }\r
454     }\r
455 \r
456   }\r
457 \r
458 \r
459   public static DasSource[] getDASSources()\r
460   {\r
461     DasSourceReaderImpl reader = new DasSourceReaderImpl();\r
462 \r
463     String registryURL =jalview.bin.Cache.getDefault("DAS_REGISTRY_URL",\r
464         "http://das.sanger.ac.uk/registry/das1/sources/"\r
465       );\r
466 \r
467     try\r
468     {\r
469       URL url = new URL(registryURL);\r
470 \r
471       DasSource[] sources = reader.readDasSource(url);\r
472 \r
473       List das1sources = new ArrayList();\r
474       for (int i = 0; i < sources.length; i++)\r
475       {\r
476         DasSource ds = sources[i];\r
477         if (ds instanceof Das2Source)\r
478         {\r
479           Das2Source d2s = (Das2Source) ds;\r
480           if (d2s.hasDas1Capabilities())\r
481           {\r
482             Das1Source d1s = DasSourceConverter.toDas1Source(d2s);\r
483             das1sources.add(d1s);\r
484           }\r
485 \r
486         }\r
487         else if (ds instanceof Das1Source)\r
488         {\r
489           das1sources.add( (Das1Source) ds);\r
490         }\r
491       }\r
492 \r
493       return (Das1Source[]) das1sources.toArray(new Das1Source[das1sources.size()]);\r
494     }\r
495     catch (Exception ex)\r
496     {\r
497       ex.printStackTrace();\r
498       return null;\r
499     }\r
500 \r
501   }\r
502 }\r
503 \r
504 \r