formatting and ensure that dataset is actually searched for sequences with dbrefs...
[jalview.git] / src / jalview / analysis / CrossRef.java
1 package jalview.analysis;\r
2 \r
3 import java.util.Enumeration;\r
4 import java.util.Vector;\r
5 import java.util.Hashtable;\r
6 \r
7 import jalview.datamodel.AlignedCodonFrame;\r
8 import jalview.datamodel.Alignment;\r
9 import jalview.datamodel.AlignmentI;\r
10 import jalview.datamodel.DBRefSource;\r
11 import jalview.datamodel.DBRefEntry;\r
12 import jalview.datamodel.Sequence;\r
13 import jalview.datamodel.SequenceI;\r
14 import jalview.ws.ASequenceFetcher;\r
15 import jalview.ws.SequenceFetcher;\r
16 \r
17 /**\r
18  * Functions for cross-referencing sequence databases. user must first specify\r
19  * if cross-referencing from protein or dna (set dna==true)\r
20  * \r
21  * @author JimP\r
22  * \r
23  */\r
24 public class CrossRef\r
25 {\r
26   /**\r
27    * get the DNA or protein references for a protein or dna sequence\r
28    * \r
29    * @param dna\r
30    * @param rfs\r
31    * @return\r
32    */\r
33   public static DBRefEntry[] findXDbRefs(boolean dna, DBRefEntry[] rfs)\r
34   {\r
35     if (dna)\r
36     {\r
37       rfs = jalview.util.DBRefUtils.selectRefs(rfs, DBRefSource.PROTEINDBS);\r
38     }\r
39     else\r
40     {\r
41       rfs = jalview.util.DBRefUtils.selectRefs(rfs,\r
42               DBRefSource.DNACODINGDBS); // could attempt to find other cross\r
43                                           // refs and return here - ie PDB xrefs\r
44                                           // (not dna, not protein seq)\r
45     }\r
46     return rfs;\r
47   }\r
48 \r
49   public static Hashtable classifyDbRefs(DBRefEntry[] rfs)\r
50   {\r
51     Hashtable classes = new Hashtable();\r
52     classes.put(DBRefSource.PROTEINDBS, jalview.util.DBRefUtils.selectRefs(\r
53             rfs, DBRefSource.PROTEINDBS));\r
54     classes.put(DBRefSource.DNACODINGDBS, jalview.util.DBRefUtils\r
55             .selectRefs(rfs, DBRefSource.DNACODINGDBS));\r
56     classes.put(DBRefSource.DOMAINDBS, jalview.util.DBRefUtils.selectRefs(\r
57             rfs, DBRefSource.DOMAINDBS));\r
58     // classes.put(OTHER, )\r
59     return classes;\r
60   }\r
61 \r
62   /**\r
63    * @param dna\r
64    *                true if seqs are DNA seqs\r
65    * @param seqs\r
66    * @return a list of sequence database cross reference source types\r
67    */\r
68   public static String[] findSequenceXrefTypes(boolean dna, SequenceI[] seqs)\r
69   {\r
70     return findSequenceXrefTypes(dna, seqs, null);\r
71   }\r
72 \r
73   /**\r
74    * Indirect references are references from other sequences from the dataset to\r
75    * any of the direct DBRefEntrys on the given sequences.\r
76    * \r
77    * @param dna\r
78    *                true if seqs are DNA seqs\r
79    * @param seqs\r
80    * @return a list of sequence database cross reference source types\r
81    */\r
82   public static String[] findSequenceXrefTypes(boolean dna,\r
83           SequenceI[] seqs, AlignmentI dataset)\r
84   {\r
85     String[] dbrefs = null;\r
86     Vector refs = new Vector();\r
87     for (int s = 0; s < seqs.length; s++)\r
88     {\r
89       SequenceI dss = seqs[s];\r
90       while (dss.getDatasetSequence() != null)\r
91       {\r
92         dss = dss.getDatasetSequence();\r
93       }\r
94       DBRefEntry[] rfs = findXDbRefs(dna, dss.getDBRef());\r
95       for (int r = 0; rfs != null && r < rfs.length; r++)\r
96       {\r
97         if (!refs.contains(rfs[r].getSource()))\r
98         {\r
99           refs.addElement(rfs[r].getSource());\r
100         }\r
101       }\r
102       if (dataset != null)\r
103       {\r
104         // search for references to this sequence's direct references.\r
105         DBRefEntry[] lrfs = CrossRef.findXDbRefs(!dna, seqs[s].getDBRef());\r
106         Vector rseqs = new Vector();\r
107         CrossRef.searchDatasetXrefs(seqs[s], !dna, lrfs, dataset, rseqs,\r
108                 null); // don't need to specify codon frame for mapping here\r
109         Enumeration lr = rseqs.elements();\r
110         while (lr.hasMoreElements())\r
111         {\r
112           SequenceI rs = (SequenceI) lr.nextElement();\r
113           DBRefEntry[] xrs = findXDbRefs(dna, rs.getDBRef());\r
114           for (int r = 0; rfs != null && r < rfs.length; r++)\r
115           {\r
116             if (!refs.contains(rfs[r].getSource()))\r
117             {\r
118               refs.addElement(rfs[r].getSource());\r
119             }\r
120           }\r
121         }\r
122       }\r
123     }\r
124     if (refs.size() > 0)\r
125     {\r
126       dbrefs = new String[refs.size()];\r
127       refs.copyInto(dbrefs);\r
128     }\r
129     return dbrefs;\r
130   }\r
131 \r
132   /*\r
133    * if (dna) { if (rfs[r].hasMap()) { // most likely this is a protein cross\r
134    * reference if (!refs.contains(rfs[r].getSource())) {\r
135    * refs.addElement(rfs[r].getSource()); } } }\r
136    */\r
137   public static boolean hasCdnaMap(SequenceI[] seqs)\r
138   {\r
139     String[] reftypes = findSequenceXrefTypes(false, seqs);\r
140     for (int s = 0; s < reftypes.length; s++)\r
141     {\r
142       if (reftypes.equals(DBRefSource.EMBLCDS))\r
143       {\r
144         return true;\r
145         // no map\r
146       }\r
147     }\r
148     return false;\r
149   }\r
150 \r
151   public static SequenceI[] getCdnaMap(SequenceI[] seqs)\r
152   {\r
153     Vector cseqs = new Vector();\r
154     for (int s = 0; s < seqs.length; s++)\r
155     {\r
156       DBRefEntry[] cdna = findXDbRefs(true, seqs[s].getDBRef());\r
157       for (int c = 0; c < cdna.length; c++)\r
158       {\r
159         if (cdna[c].getSource().equals(DBRefSource.EMBLCDS))\r
160         {\r
161           // retrieve CDS dataset sequences\r
162           // need global dataset sequence retriever/resolver to reuse refs\r
163           // and construct Mapping entry.\r
164           // insert gaps in CDS according to peptide gaps.\r
165           // add gapped sequence to cseqs\r
166         }\r
167       }\r
168     }\r
169     if (cseqs.size() > 0)\r
170     {\r
171       SequenceI[] rsqs = new SequenceI[cseqs.size()];\r
172       cseqs.copyInto(rsqs);\r
173       return rsqs;\r
174     }\r
175     return null;\r
176 \r
177   }\r
178 \r
179   /**\r
180    * \r
181    * @param dna\r
182    * @param seqs\r
183    * @return\r
184    */\r
185   public static Alignment findXrefSequences(SequenceI[] seqs, boolean dna,\r
186           String source)\r
187   {\r
188     return findXrefSequences(seqs, dna, source, null);\r
189   }\r
190 \r
191   /**\r
192    * \r
193    * @param seqs\r
194    * @param dna\r
195    * @param source\r
196    * @param dataset\r
197    *                alignment to search for product sequences.\r
198    * @return products (as dataset sequences)\r
199    */\r
200   public static Alignment findXrefSequences(SequenceI[] seqs, boolean dna,\r
201           String source, AlignmentI dataset)\r
202   {\r
203     Vector rseqs = new Vector();\r
204     Alignment ral = null;\r
205     AlignedCodonFrame cf = new AlignedCodonFrame(0); // nominal width\r
206     for (int s = 0; s < seqs.length; s++)\r
207     {\r
208       SequenceI dss = seqs[s];\r
209       while (dss.getDatasetSequence() != null)\r
210       {\r
211         dss = dss.getDatasetSequence();\r
212       }\r
213       boolean found = false;\r
214       DBRefEntry[] xrfs = CrossRef.findXDbRefs(dna, dss.getDBRef());\r
215       if ((xrfs == null || xrfs.length == 0) && dataset != null)\r
216       {\r
217         System.out.println("Attempting to find ds Xrefs refs.");\r
218         DBRefEntry[] lrfs = CrossRef.findXDbRefs(!dna, seqs[s].getDBRef()); // less\r
219                                                                             // ambiguous\r
220                                                                             // would\r
221                                                                             // be a\r
222                                                                             // 'find\r
223                                                                             // primary\r
224                                                                             // dbRefEntry'\r
225                                                                             // method.\r
226         // filter for desired source xref here\r
227         found = CrossRef.searchDatasetXrefs(dss, !dna, lrfs, dataset,\r
228                 rseqs, cf);\r
229       }\r
230       for (int r = 0; xrfs != null && r < xrfs.length; r++)\r
231       {\r
232         if (source != null && !source.equals(xrfs[r].getSource()))\r
233           continue;\r
234         if (xrfs[r].hasMap())\r
235         {\r
236           if (xrfs[r].getMap().getTo() != null)\r
237           {\r
238             Sequence rsq = new Sequence(xrfs[r].getMap().getTo());\r
239             rseqs.addElement(rsq);\r
240             if (xrfs[r].getMap().getMap().getFromRatio() != xrfs[r]\r
241                     .getMap().getMap().getToRatio())\r
242             {\r
243               // get sense of map correct for adding to product alignment.\r
244               if (dna)\r
245               {\r
246                 // map is from dna seq to a protein product\r
247                 cf.addMap(dss, rsq, xrfs[r].getMap().getMap());\r
248               }\r
249               else\r
250               {\r
251                 // map should be from protein seq to its coding dna\r
252                 cf.addMap(rsq, dss, xrfs[r].getMap().getMap().getInverse());\r
253               }\r
254             }\r
255             found = true;\r
256           }\r
257         }\r
258         if (!found)\r
259         {\r
260           // do a bit more work - search for sequences with references matching\r
261           // xrefs on this sequence.\r
262           if (dataset != null)\r
263           {\r
264             found |= searchDataset(dss, xrfs[r], dataset, rseqs, cf);\r
265             if (found)\r
266               xrfs[r] = null; // we've recovered seqs for this one.\r
267           }\r
268         }\r
269       }\r
270       if (!found)\r
271       {\r
272         if (xrfs != null && xrfs.length > 0)\r
273         {\r
274           // Try and get the sequence reference...\r
275           /*\r
276            * Ideal world - we ask for a sequence fetcher implementation here if\r
277            * (jalview.io.RunTimeEnvironment.getSequenceFetcher()) (\r
278            */\r
279           ASequenceFetcher sftch = new SequenceFetcher();\r
280           SequenceI[] retrieved = null;\r
281           int l = xrfs.length;\r
282           for (int r = 0; r < xrfs.length; r++)\r
283           {\r
284             // filter out any irrelevant or irretrievable references\r
285             if (xrfs[r] == null\r
286                     || ((source != null && !source.equals(xrfs[r]\r
287                             .getSource())) || !sftch.isFetchable(xrfs[r]\r
288                             .getSource())))\r
289             {\r
290               l--;\r
291               xrfs[r] = null;\r
292             }\r
293           }\r
294           if (l > 0)\r
295           {\r
296             System.out\r
297                     .println("Attempting to retrieve cross referenced sequences.");\r
298             DBRefEntry[] t = new DBRefEntry[l];\r
299             l = 0;\r
300             for (int r = 0; r < xrfs.length; r++)\r
301             {\r
302               if (xrfs[r] != null)\r
303                 t[l++] = xrfs[r];\r
304             }\r
305             xrfs = t;\r
306             try\r
307             {\r
308               retrieved = sftch.getSequences(xrfs);\r
309             } catch (Exception e)\r
310             {\r
311               System.err\r
312                       .println("Problem whilst retrieving cross references for Sequence : "\r
313                               + seqs[s].getName());\r
314               e.printStackTrace();\r
315             }\r
316             if (retrieved != null)\r
317             {\r
318               for (int rs = 0; rs < retrieved.length; rs++)\r
319               {\r
320                 rseqs.addElement(retrieved[rs]);\r
321               }\r
322             }\r
323           }\r
324         }\r
325       }\r
326     }\r
327     if (rseqs.size() > 0)\r
328     {\r
329       SequenceI[] rsqs = new SequenceI[rseqs.size()];\r
330       rseqs.copyInto(rsqs);\r
331       ral = new Alignment(rsqs);\r
332       if (cf != null && cf.getProtMappings() != null)\r
333       {\r
334         ral.addCodonFrame(cf);\r
335       }\r
336     }\r
337     return ral;\r
338   }\r
339 \r
340   /**\r
341    * find references to lrfs in the cross-reference set of each sequence in\r
342    * dataset (that is not equal to sequenceI) Identifies matching DBRefEntry\r
343    * based on source and accession string only - Map and Version are nulled.\r
344    * \r
345    * @param sequenceI\r
346    * @param lrfs\r
347    * @param dataset\r
348    * @param rseqs\r
349    * @return true if matches were found.\r
350    */\r
351   private static boolean searchDatasetXrefs(SequenceI sequenceI,\r
352           boolean dna, DBRefEntry[] lrfs, AlignmentI dataset, Vector rseqs,\r
353           AlignedCodonFrame cf)\r
354   {\r
355     boolean found = false;\r
356     if (lrfs == null)\r
357       return false;\r
358     for (int i = 0; i < lrfs.length; i++)\r
359     {\r
360       DBRefEntry xref = new DBRefEntry(lrfs[i]);\r
361       // add in wildcards\r
362       xref.setVersion(null);\r
363       xref.setMap(null);\r
364       found = searchDataset(sequenceI, xref, dataset, rseqs, cf, false, dna);\r
365     }\r
366     return found;\r
367   }\r
368 \r
369   /**\r
370    * search a given sequence dataset for references matching cross-references to\r
371    * the given sequence\r
372    * \r
373    * @param sequenceI\r
374    * @param xrf\r
375    * @param dataset\r
376    * @param rseqs\r
377    *                set of unique sequences\r
378    * @param cf\r
379    * @return true if one or more unique sequences were found and added\r
380    */\r
381   public static boolean searchDataset(SequenceI sequenceI, DBRefEntry xrf,\r
382           AlignmentI dataset, Vector rseqs, AlignedCodonFrame cf)\r
383   {\r
384     return searchDataset(sequenceI, xrf, dataset, rseqs, cf, true, false);\r
385   }\r
386 \r
387   /**\r
388    * TODO: generalise to different protein classifications Search dataset for\r
389    * DBRefEntrys matching the given one (xrf) and add the associated sequence to\r
390    * rseq.\r
391    * \r
392    * @param sequenceI\r
393    * @param xrf\r
394    * @param dataset\r
395    * @param rseqs\r
396    * @param direct -\r
397    *                search all references or only subset\r
398    * @param dna\r
399    *                search dna or protein xrefs (if direct=false)\r
400    * @return true if relationship found and sequence added.\r
401    */\r
402   public static boolean searchDataset(SequenceI sequenceI, DBRefEntry xrf,\r
403           AlignmentI dataset, Vector rseqs, AlignedCodonFrame cf,\r
404           boolean direct, boolean dna)\r
405   {\r
406     boolean found = false;\r
407     if (dataset == null)\r
408       return false;\r
409     if (dataset.getSequences() == null)\r
410     {\r
411       System.err.println("Empty dataset sequence set - NO VECTOR");\r
412       return false;\r
413     }\r
414     Enumeration e = dataset.getSequences().elements();\r
415     while (e.hasMoreElements())\r
416     {\r
417       SequenceI nxt = (SequenceI) e.nextElement();\r
418       if (nxt != null)\r
419       {\r
420         if (nxt.getDatasetSequence() != null)\r
421         {\r
422           System.err\r
423                   .println("Implementation warning: getProducts passed a dataset alignment without dataset sequences in it!");\r
424         }\r
425         if (nxt != sequenceI && nxt != sequenceI.getDatasetSequence())\r
426         {\r
427           // look for direct or indirect references in common\r
428           DBRefEntry[] poss = null, cands = null;\r
429           if (direct)\r
430           {\r
431             cands = jalview.util.DBRefUtils.searchRefs(poss = nxt\r
432                     .getDBRef(), xrf);\r
433           }\r
434           else\r
435           {\r
436             cands = jalview.util.DBRefUtils.searchRefs(poss = CrossRef\r
437                     .findXDbRefs(dna, nxt.getDBRef()), xrf);\r
438           }\r
439           if (cands != null)\r
440           {\r
441             if (!rseqs.contains(nxt))\r
442             {\r
443               rseqs.addElement(nxt);\r
444               boolean foundmap = cf != null; // don't search if we aren't given\r
445                                               // a codon map object\r
446               for (int r = 0; foundmap && r < cands.length; r++)\r
447               {\r
448                 if (cands[r].hasMap())\r
449                 {\r
450                   if (cands[r].getMap().getTo() != null\r
451                           && cands[r].getMap().getMap().getFromRatio() != cands[r]\r
452                                   .getMap().getMap().getToRatio())\r
453                   {\r
454                     foundmap = true;\r
455                     // get sense of map correct for adding to product alignment.\r
456                     if (dna)\r
457                     {\r
458                       // map is from dna seq to a protein product\r
459                       cf.addMap(sequenceI, nxt, cands[r].getMap().getMap());\r
460                     }\r
461                     else\r
462                     {\r
463                       // map should be from protein seq to its coding dna\r
464                       cf.addMap(nxt, sequenceI, cands[r].getMap().getMap()\r
465                               .getInverse());\r
466                     }\r
467                   }\r
468                 }\r
469               }\r
470               // TODO: add mapping between sequences if necessary\r
471               found = true;\r
472             }\r
473           }\r
474 \r
475         }\r
476       }\r
477     }\r
478     return found;\r
479   }\r
480 \r
481   /**\r
482    * precalculate different products that can be found for seqs in dataset and\r
483    * return them.\r
484    * \r
485    * @param dna\r
486    * @param seqs\r
487    * @param dataset\r
488    * @param fake -\r
489    *                don't actually build lists - just get types\r
490    * @return public static Object[] buildXProductsList(boolean dna, SequenceI[]\r
491    *         seqs, AlignmentI dataset, boolean fake) { String types[] =\r
492    *         jalview.analysis.CrossRef.findSequenceXrefTypes( dna, seqs,\r
493    *         dataset); if (types != null) { System.out.println("Xref Types for:\r
494    *         "+(dna ? "dna" : "prot")); for (int t = 0; t < types.length; t++) {\r
495    *         System.out.println("Type: " + types[t]); SequenceI[] prod =\r
496    *         jalview.analysis.CrossRef.findXrefSequences(seqs, dna, types[t]);\r
497    *         System.out.println("Found " + ((prod == null) ? "no" : "" +\r
498    *         prod.length) + " products"); if (prod!=null) { for (int p=0; p<prod.length;\r
499    *         p++) { System.out.println("Prod "+p+":\r
500    *         "+prod[p].getDisplayId(true)); } } }\r
501    *  } else { System.out.println("Trying getProducts for\r
502    * "+al.getSequenceAt(0).getDisplayId(true)); System.out.println("Search DS\r
503    * Xref for: "+(dna ? "dna" : "prot")); // have a bash at finding the products\r
504    * amongst all the retrieved sequences. SequenceI[] prod =\r
505    * jalview.analysis.CrossRef.findXrefSequences(al .getSequencesArray(), dna,\r
506    * null, ds); System.out.println("Found " + ((prod == null) ? "no" : "" +\r
507    * prod.length) + " products"); if (prod!=null) { // select non-equivalent\r
508    * sequences from dataset list for (int p=0; p<prod.length; p++) {\r
509    * System.out.println("Prod "+p+": "+prod[p].getDisplayId(true)); } }\r
510    *  } }\r
511    */\r
512 }