ba6d57fc69d36300c14cc0201c3af11a40259c57
[jalview.git] / src / jalview / ws / DBRefFetcher.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.io.*;\r
22 import java.util.*;\r
23 \r
24 import org.exolab.castor.mapping.*;\r
25 import org.exolab.castor.xml.*;\r
26 import jalview.analysis.*;\r
27 import jalview.datamodel.*;\r
28 import jalview.datamodel.Mapping;\r
29 import jalview.gui.*;\r
30 import jalview.ws.dbsources.Uniprot;\r
31 import jalview.ws.ebi.EBIFetchClient;\r
32 \r
33 /**\r
34  * DOCUMENT ME!\r
35  * \r
36  * @author $author$\r
37  * @version $Revision$\r
38  */\r
39 public class DBRefFetcher implements Runnable\r
40 {\r
41   SequenceI[] dataset;\r
42 \r
43   AlignFrame af;\r
44 \r
45   CutAndPasteTransfer output = new CutAndPasteTransfer();\r
46 \r
47   StringBuffer sbuffer = new StringBuffer();\r
48 \r
49   boolean running = false;\r
50 \r
51   // /This will be a collection of Vectors of sequenceI refs.\r
52   // The key will be the seq name or accession id of the seq\r
53   Hashtable seqRefs;\r
54 \r
55   String[] dbSources;\r
56 \r
57   SequenceFetcher sfetcher;\r
58 \r
59   public DBRefFetcher()\r
60   {\r
61   }\r
62 \r
63   /**\r
64    * Creates a new SequenceFeatureFetcher object.\r
65    * \r
66    * @param seqs\r
67    *                fetch references for these sequences\r
68    * @param af\r
69    *                the parent alignframe for progress bar monitoring.\r
70    */\r
71   public DBRefFetcher(SequenceI[] seqs, AlignFrame af)\r
72   {\r
73     this.af = af;\r
74     SequenceI[] ds = new SequenceI[seqs.length];\r
75     for (int i = 0; i < seqs.length; i++)\r
76     {\r
77       if (seqs[i].getDatasetSequence() != null)\r
78         ds[i] = seqs[i].getDatasetSequence();\r
79       else\r
80         ds[i] = seqs[i];\r
81     }\r
82     this.dataset = ds;\r
83     sfetcher = new SequenceFetcher();\r
84     // select appropriate databases based on alignFrame context.\r
85     if (af.getViewport().getAlignment().isNucleotide())\r
86     {\r
87       dbSources = DBRefSource.DNACODINGDBS;\r
88     }\r
89     else\r
90     {\r
91       dbSources = DBRefSource.PROTEINDBS;\r
92     }\r
93   }\r
94 \r
95   /**\r
96    * start the fetcher thread\r
97    * \r
98    * @param waitTillFinished\r
99    *                true to block until the fetcher has finished\r
100    */\r
101   public void fetchDBRefs(boolean waitTillFinished)\r
102   {\r
103     Thread thread = new Thread(this);\r
104     thread.start();\r
105     running = true;\r
106 \r
107     if (waitTillFinished)\r
108     {\r
109       while (running)\r
110       {\r
111         try\r
112         {\r
113           Thread.sleep(500);\r
114         } catch (Exception ex)\r
115         {\r
116         }\r
117       }\r
118     }\r
119   }\r
120 \r
121   /**\r
122    * The sequence will be added to a vector of sequences belonging to key which\r
123    * could be either seq name or dbref id\r
124    * \r
125    * @param seq\r
126    *                SequenceI\r
127    * @param key\r
128    *                String\r
129    */\r
130   void addSeqId(SequenceI seq, String key)\r
131   {\r
132     key = key.toUpperCase();\r
133 \r
134     Vector seqs;\r
135     if (seqRefs.containsKey(key))\r
136     {\r
137       seqs = (Vector) seqRefs.get(key);\r
138 \r
139       if (seqs != null && !seqs.contains(seq))\r
140       {\r
141         seqs.addElement(seq);\r
142       }\r
143       else if (seqs == null)\r
144       {\r
145         seqs = new Vector();\r
146         seqs.addElement(seq);\r
147       }\r
148 \r
149     }\r
150     else\r
151     {\r
152       seqs = new Vector();\r
153       seqs.addElement(seq);\r
154     }\r
155 \r
156     seqRefs.put(key, seqs);\r
157   }\r
158 \r
159   /**\r
160    * DOCUMENT ME!\r
161    */\r
162   public void run()\r
163   {\r
164     if (dbSources == null)\r
165     {\r
166       throw new Error("Implementation error. Must initialise dbSources");\r
167     }\r
168     long startTime = System.currentTimeMillis();\r
169     af.setProgressBar("Fetching db refs", startTime);\r
170     running = true;\r
171     int db = 0;\r
172     Vector sdataset = new Vector();\r
173     for (int s = 0; s < dataset.length; s++)\r
174     {\r
175       sdataset.addElement(dataset[s]);\r
176     }\r
177     while (sdataset.size() > 0 && db < dbSources.length)\r
178     {\r
179       int maxqlen = 1; // default number of queries made to at one time\r
180       System.err.println("Verifying against " + dbSources[db]);\r
181       jalview.ws.seqfetcher.DbSourceProxy dbsource = sfetcher\r
182               .getSourceProxy(dbSources[db]);\r
183       if (dbsource == null)\r
184       {\r
185         System.err.println("No proxy for " + dbSources[db]);\r
186         db++;\r
187         continue;\r
188       }\r
189       if (dbsource.getDbSourceProperties()\r
190               .containsKey(DBRefSource.MULTIACC))\r
191       {\r
192         maxqlen = ((Integer) dbsource.getDbSourceProperties().get(\r
193                 DBRefSource.MULTIACC)).intValue();\r
194       }\r
195       // iterate through db for each remaining un-verified sequence\r
196       SequenceI[] currSeqs = new SequenceI[sdataset.size()];\r
197       sdataset.copyInto(currSeqs);// seqs that are to be validated against\r
198       // dbSources[db]\r
199       Vector queries = new Vector(); // generated queries curSeq\r
200       seqRefs = new Hashtable();\r
201 \r
202       int seqIndex = 0;\r
203 \r
204       while (queries.size() > 0 || seqIndex < currSeqs.length)\r
205       {\r
206         if (queries.size() > 0)\r
207         {\r
208           // Still queries to make for current seqIndex\r
209           StringBuffer queryString = new StringBuffer("");\r
210           int nqSize = (maxqlen > queries.size()) ? queries.size()\r
211                   : maxqlen;\r
212           for (int nq = 0, numq = 0; nq < nqSize; nq++)\r
213           {\r
214             String query = (String) queries.elementAt(nq);\r
215             if (dbsource.isValidReference(query))\r
216             {\r
217               queryString.append((nq == 0) ? "" : dbsource\r
218                       .getAccessionSeparator());\r
219               queryString.append(query);\r
220               numq++;\r
221             }\r
222           }\r
223           for (int nq = 0; nq < nqSize; nq++)\r
224           {\r
225             queries.removeElementAt(0);\r
226           }\r
227           // make the queries and process the response\r
228           AlignmentI retrieved = null;\r
229           try\r
230           {\r
231             retrieved = dbsource.getSequenceRecords(queryString.toString());\r
232           } catch (Exception ex)\r
233           {\r
234             ex.printStackTrace();\r
235           }\r
236           if (retrieved != null)\r
237           {\r
238             transferReferences(sdataset, dbSources[db], retrieved);\r
239           }\r
240         }\r
241         else\r
242         {\r
243           // make some more strings for use as queries\r
244           for (int i = 0; (seqIndex < dataset.length) && (i < 50); seqIndex++, i++)\r
245           {\r
246             SequenceI sequence = dataset[seqIndex];\r
247             DBRefEntry[] uprefs = jalview.util.DBRefUtils.selectRefs(\r
248                     sequence.getDBRef(), new String[]\r
249                     { dbSources[db] }); // jalview.datamodel.DBRefSource.UNIPROT\r
250             // });\r
251             // check for existing dbrefs to use\r
252             if (uprefs != null)\r
253             {\r
254               for (int j = 0; j < uprefs.length; j++)\r
255               {\r
256                 addSeqId(sequence, uprefs[j].getAccessionId());\r
257                 queries\r
258                         .addElement(uprefs[j].getAccessionId()\r
259                                 .toUpperCase());\r
260               }\r
261             }\r
262             else\r
263             {\r
264               // generate queries from sequence ID string\r
265               StringTokenizer st = new StringTokenizer(sequence.getName(),\r
266                       "|");\r
267               while (st.hasMoreTokens())\r
268               {\r
269                 String token = st.nextToken();\r
270                 addSeqId(sequence, token);\r
271                 queries.addElement(token.toUpperCase());\r
272               }\r
273             }\r
274           }\r
275         }\r
276       }\r
277       // advance to next database\r
278       db++;\r
279     } // all databases have been queries.\r
280     if (sbuffer.length() > 0)\r
281     {\r
282       output\r
283               .setText("Your sequences have been verified against known sequence databases. Some of the ids have been\n"\r
284                       + "altered, most likely the start/end residue will have been updated.\n"\r
285                       + "Save your alignment to maintain the updated id.\n\n"\r
286                       + sbuffer.toString());\r
287       Desktop.addInternalFrame(output, "Sequence names updated ", 600, 300);\r
288       // The above is the dataset, we must now find out the index\r
289       // of the viewed sequence\r
290 \r
291     }\r
292 \r
293     af.setProgressBar("DBRef search completed", startTime);\r
294     // promptBeforeBlast();\r
295 \r
296     running = false;\r
297 \r
298   }\r
299 \r
300   /**\r
301    * Verify local sequences in seqRefs against the retrieved sequence database\r
302    * records.\r
303    * \r
304    */\r
305   void transferReferences(Vector sdataset, String dbSource,\r
306           AlignmentI retrievedAl) // File\r
307   // file)\r
308   {\r
309 \r
310     if (retrievedAl == null || retrievedAl.getHeight() == 0)\r
311     {\r
312       return;\r
313     }\r
314     SequenceI[] retrieved = retrievedAl.getSequencesArray();\r
315     SequenceI sequence = null;\r
316 \r
317     // Vector entries = new Uniprot().getUniprotEntries(file);\r
318 \r
319     int i, iSize = retrieved.length; // entries == null ? 0 : entries.size();\r
320     // UniprotEntry entry;\r
321     for (i = 0; i < iSize; i++)\r
322     {\r
323       SequenceI entry = retrieved[i]; // (UniprotEntry) entries.elementAt(i);\r
324 \r
325       // Work out which sequences this sequence matches,\r
326       // taking into account all accessionIds and names in the file\r
327       Vector sequenceMatches = new Vector();\r
328       // look for corresponding accession ids\r
329       DBRefEntry[] entryRefs = jalview.util.DBRefUtils.selectRefs(entry\r
330               .getDBRef(), new String[]\r
331       { dbSource });\r
332       for (int j = 0; j < entryRefs.length; j++)\r
333       {\r
334         String accessionId = entryRefs[j].getAccessionId(); // .getAccession().elementAt(j).toString();\r
335         // match up on accessionId\r
336         if (seqRefs.containsKey(accessionId.toUpperCase()))\r
337         {\r
338           Vector seqs = (Vector) seqRefs.get(accessionId);\r
339           for (int jj = 0; jj < seqs.size(); jj++)\r
340           {\r
341             sequence = (SequenceI) seqs.elementAt(jj);\r
342             if (!sequenceMatches.contains(sequence))\r
343             {\r
344               sequenceMatches.addElement(sequence);\r
345             }\r
346           }\r
347         }\r
348       }\r
349       if (sequenceMatches.size()==0)\r
350       {\r
351         // failed to match directly on accessionId==query so just compare all sequences to entry\r
352         Enumeration e = seqRefs.keys();\r
353         while (e.hasMoreElements())\r
354         {\r
355           Vector sqs = (Vector) seqRefs.get(e.nextElement());\r
356           if (sqs!=null && sqs.size()>0)\r
357           {\r
358             Enumeration sqe = sqs.elements();\r
359             while (sqe.hasMoreElements())\r
360             {\r
361               sequenceMatches.addElement(sqe.nextElement());\r
362             }\r
363           }\r
364         }\r
365       }\r
366       // look for corresponding names\r
367       // this is uniprot specific ?\r
368       // could be useful to extend this so we try to find any 'significant'\r
369       // information in common between two sequence objects.\r
370       /*\r
371        * DBRefEntry[] entryRefs =\r
372        * jalview.util.DBRefUtils.selectRefs(entry.getDBRef(), new String[] {\r
373        * dbSource }); for (int j = 0; j < entry.getName().size(); j++) { String\r
374        * name = entry.getName().elementAt(j).toString(); if\r
375        * (seqRefs.containsKey(name)) { Vector seqs = (Vector) seqRefs.get(name);\r
376        * for (int jj = 0; jj < seqs.size(); jj++) { sequence = (SequenceI)\r
377        * seqs.elementAt(jj); if (!sequenceMatches.contains(sequence)) {\r
378        * sequenceMatches.addElement(sequence); } } } }\r
379        */\r
380       // sequenceMatches now contains the set of all sequences associated with\r
381       // the returned db record\r
382       String entrySeq = entry.getSequenceAsString().toUpperCase();\r
383       for (int m = 0; m < sequenceMatches.size(); m++)\r
384       {\r
385         sequence = (SequenceI) sequenceMatches.elementAt(m);\r
386         // only update start and end positions and shift features if there are no existing references\r
387         // TODO: test for legacy where uniprot or EMBL refs exist but no mappings are made (but content matches retrieved set)\r
388         boolean updateRefFrame = sequence.getDBRef()==null || sequence.getDBRef().length==0;\r
389         // verify sequence against the entry sequence\r
390 \r
391         String nonGapped = AlignSeq.extractGaps("-. ",\r
392                 sequence.getSequenceAsString()).toUpperCase();\r
393 \r
394         int absStart = entrySeq.indexOf(nonGapped);\r
395         int mapStart = entry.getStart();\r
396         jalview.datamodel.Mapping mp;\r
397 \r
398         if (absStart == -1)\r
399         {\r
400           // Is local sequence contained in dataset sequence?\r
401           absStart = nonGapped.indexOf(entrySeq);\r
402           if (absStart == -1)\r
403           { // verification failed.\r
404             sbuffer.append(sequence.getName()\r
405                     + " SEQUENCE NOT %100 MATCH \n");\r
406             continue;\r
407           }\r
408           \r
409           sbuffer.append(sequence.getName() + " HAS " + absStart\r
410                 + " PREFIXED RESIDUES COMPARED TO " + dbSource+"\n");\r
411           //\r
412           //      + " - ANY SEQUENCE FEATURES"\r
413           //        + " HAVE BEEN ADJUSTED ACCORDINGLY \n");\r
414           // absStart = 0;\r
415           // create valid mapping between matching region of local sequence and\r
416           // the mapped sequence\r
417           mp = new Mapping(null, new int[]\r
418           { sequence.getStart()+absStart, sequence.getStart()+absStart+entrySeq.length()-1 }, new int[]\r
419           { entry.getStart(),\r
420               entry.getStart() + entrySeq.length() - 1 }, 1, 1);\r
421           updateRefFrame=false; // mapping is based on current start/end so don't modify start and end\r
422         }\r
423         else\r
424         {\r
425           // update start and end of local sequence to place it in entry's\r
426           // reference frame.\r
427           // apply identity map map from whole of local sequence to matching\r
428           // region of database\r
429           // sequence\r
430           mp = null; // Mapping.getIdentityMap();\r
431           // new Mapping(null,\r
432           // new int[] { absStart+sequence.getStart(),\r
433           // absStart+sequence.getStart()+entrySeq.length()-1},\r
434           // new int[] { entry.getStart(), entry.getEnd() }, 1, 1);\r
435           // relocate local features for updated start\r
436           if (updateRefFrame && sequence.getSequenceFeatures() != null)\r
437           {\r
438             SequenceFeature[] sf = sequence.getSequenceFeatures();\r
439             int start = sequence.getStart();\r
440             int end = sequence.getEnd();\r
441             int startShift = 1-absStart-start; // how much the features are to be shifted by\r
442             for (int sfi = 0; sfi < sf.length; sfi++)\r
443             {\r
444               if (sf[sfi].getBegin() >= start && sf[sfi].getEnd() <= end)\r
445               {\r
446                 // shift feature along by absstart\r
447                 sf[sfi].setBegin(sf[sfi].getBegin() + startShift);\r
448                 sf[sfi].setEnd(sf[sfi].getEnd() + startShift);\r
449               }\r
450             }\r
451           }\r
452         }\r
453 \r
454         System.out.println("Adding dbrefs to " + sequence.getName()\r
455                 + " from " + dbSource + " sequence : " + entry.getName());\r
456         sequence.transferAnnotation(entry, mp);\r
457         // unknownSequences.remove(sequence);\r
458         int absEnd = absStart + nonGapped.length();\r
459         absStart += 1;\r
460         if (updateRefFrame)\r
461         {\r
462           // finally, update local sequence reference frame if we're allowed\r
463           sequence.setStart(absStart);\r
464           sequence.setEnd(absEnd);\r
465         }\r
466         // and remove it from the rest\r
467         // TODO: decide if we should remove annotated sequence from set\r
468         sdataset.remove(sequence);\r
469       }\r
470     }\r
471   }\r
472 }\r