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