f475ecb4cc6c9fa5b96446ab44e562a42d2eb9b0
[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(0); // 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         // filter for desired source xref here\r
214         found = CrossRef.searchDatasetXrefs(dss, !dna, lrfs, dataset, rseqs, cf);\r
215       }\r
216       for (int r = 0; xrfs!=null && r < xrfs.length; r++)\r
217       {\r
218         if (source != null && !source.equals(xrfs[r].getSource()))\r
219           continue;\r
220         if (xrfs[r].hasMap())\r
221         {\r
222           if (xrfs[r].getMap().getTo() != null)\r
223           {\r
224             Sequence rsq = new Sequence(xrfs[r].getMap().getTo());\r
225             rseqs.addElement(rsq);\r
226             if (xrfs[r].getMap().getMap().getFromRatio()!=xrfs[r].getMap().getMap().getToRatio())\r
227             {\r
228               // get sense of map correct for adding to product alignment.\r
229               if (dna)\r
230               {\r
231                 // map is from dna seq to a protein product\r
232                 cf.addMap(dss, rsq, xrfs[r].getMap().getMap());\r
233               } else {\r
234                 // map should be from protein seq to its coding dna\r
235                 cf.addMap(rsq, dss, xrfs[r].getMap().getMap().getInverse());\r
236               }\r
237             }\r
238             found = true;\r
239           }\r
240         }\r
241         else\r
242         {\r
243           // do a bit more work - search for sequences with references matching\r
244           // xrefs on this sequence.\r
245           if (dataset != null)\r
246           {\r
247             found = searchDataset(dss, xrfs[r], dataset, rseqs, cf);\r
248             if (found)\r
249               xrfs[r] = null; // we've recovered seqs for this one.\r
250           }\r
251         }\r
252       }\r
253       if (!found)\r
254       {\r
255         if (xrfs != null && xrfs.length > 0)\r
256         {\r
257           // Try and get the sequence reference...\r
258           /*\r
259            * Ideal world - we ask for a sequence fetcher implementation here if\r
260            * (jalview.io.RunTimeEnvironment.getSequenceFetcher()) (\r
261            */\r
262           ASequenceFetcher sftch = new SequenceFetcher();\r
263           SequenceI[] retrieved = null;\r
264           int l = xrfs.length;\r
265           for (int r = 0; r < xrfs.length; r++)\r
266           {\r
267             // filter out any irrelevant or irretrievable references\r
268             if (xrfs[r]==null || ((source != null && !source.equals(xrfs[r].getSource()))\r
269                     || !sftch.isFetchable(xrfs[r].getSource())))\r
270             {\r
271               l--;\r
272               xrfs[r] = null;\r
273             }\r
274           }\r
275           if (l > 0)\r
276           {\r
277             System.out\r
278             .println("Attempting to retrieve cross referenced sequences.");\r
279             DBRefEntry[] t = new DBRefEntry[l];\r
280             l = 0;\r
281             for (int r = 0; r < xrfs.length; r++)\r
282             {\r
283               if (xrfs[r] != null)\r
284                 t[l++] = xrfs[r];\r
285             }\r
286             xrfs = t;\r
287             try\r
288             {\r
289               retrieved = sftch.getSequences(xrfs);\r
290             } catch (Exception e)\r
291             {\r
292               System.err\r
293               .println("Problem whilst retrieving cross references for Sequence : "\r
294                       + seqs[s].getName());\r
295               e.printStackTrace();\r
296             }\r
297             if (retrieved != null)\r
298             {\r
299               for (int rs = 0; rs < retrieved.length; rs++)\r
300               {\r
301                 rseqs.addElement(retrieved[rs]);\r
302               }\r
303             }\r
304           }\r
305         }\r
306       }\r
307     }\r
308     if (rseqs.size() > 0)\r
309     {\r
310       SequenceI[] rsqs = new SequenceI[rseqs.size()];\r
311       rseqs.copyInto(rsqs);\r
312       ral = new Alignment(rsqs);\r
313       if (cf!=null && cf.getProtMappings()!=null)\r
314       {\r
315         ral.addCodonFrame(cf);\r
316       }\r
317     }\r
318     return ral;\r
319   }\r
320 \r
321   /**\r
322    * find references to lrfs in the cross-reference set of each sequence in dataset (that is not equal to sequenceI)\r
323    * Identifies matching DBRefEntry based on source and accession string only - Map and Version are nulled.\r
324    * @param sequenceI\r
325    * @param lrfs\r
326    * @param dataset\r
327    * @param rseqs\r
328    * @return true if matches were found.\r
329    */\r
330   private static boolean searchDatasetXrefs(SequenceI sequenceI, boolean dna, DBRefEntry[] lrfs, AlignmentI dataset, Vector rseqs, AlignedCodonFrame cf)\r
331   {\r
332     boolean found=false;\r
333     if (lrfs==null)\r
334       return false;\r
335     for (int i=0;i<lrfs.length; i++)\r
336     {\r
337       DBRefEntry xref = new DBRefEntry(lrfs[i]);\r
338       // add in wildcards\r
339       xref.setVersion(null);\r
340       xref.setMap(null);\r
341       found = searchDataset(sequenceI, xref, dataset, rseqs, cf, false, dna);\r
342     }\r
343     return found;\r
344   }\r
345 \r
346 \r
347   /**\r
348    * search a given sequence dataset for references matching cross-references to\r
349    * the given sequence\r
350    * \r
351    * @param sequenceI\r
352    * @param xrf\r
353    * @param dataset\r
354    * @param rseqs\r
355    * @param cf\r
356    * @return true if sequences were found and added\r
357    */\r
358   public static boolean searchDataset(SequenceI sequenceI, DBRefEntry xrf,\r
359           AlignmentI dataset, Vector rseqs, AlignedCodonFrame cf)\r
360   {\r
361     return searchDataset(sequenceI, xrf,\r
362             dataset, rseqs, cf, true, false);\r
363   }\r
364   /**\r
365    * TODO: generalise to different protein classifications\r
366    * Search dataset for DBRefEntrys matching the given one (xrf) and add\r
367    * the associated sequence to rseq.\r
368    * @param sequenceI\r
369    * @param xrf\r
370    * @param dataset\r
371    * @param rseqs\r
372    * @param direct - search all references or only subset\r
373    * @param dna search dna or protein xrefs (if direct=false)\r
374    * @return true if relationship found and sequence added.\r
375    */\r
376   public static boolean searchDataset(SequenceI sequenceI, DBRefEntry xrf,\r
377           AlignmentI dataset, Vector rseqs, AlignedCodonFrame cf, boolean direct, boolean dna)\r
378   {\r
379     boolean found = false;\r
380     if (dataset==null) \r
381       return false;\r
382     if (dataset.getSequences()==null)\r
383     {\r
384       System.err.println("Empty dataset sequence set - NO VECTOR");\r
385       return false;\r
386     }\r
387     Enumeration e = dataset.getSequences().elements();\r
388     while (e.hasMoreElements())\r
389     {\r
390       SequenceI nxt = (SequenceI) e.nextElement();\r
391       if (nxt != null)\r
392       {\r
393         if (nxt.getDatasetSequence() != null)\r
394         {\r
395           System.err\r
396           .println("Implementation warning: getProducts passed a dataset alignment without dataset sequences in it!");\r
397         }\r
398         if (nxt != sequenceI && nxt != sequenceI.getDatasetSequence())\r
399         {\r
400           DBRefEntry[] poss=null, cands=null;\r
401           if (direct)\r
402           {\r
403             cands = jalview.util.DBRefUtils.searchRefs(poss=nxt\r
404                     .getDBRef(), xrf);\r
405           } else {\r
406             cands = jalview.util.DBRefUtils.searchRefs(\r
407                     poss=CrossRef.findXDbRefs(dna, nxt.getDBRef()), xrf);\r
408           }\r
409           if (cands != null)\r
410           {\r
411             rseqs.addElement(nxt);\r
412             boolean foundmap= cf!=null; // don't search if we aren't given a codon map object\r
413             for (int r=0; foundmap && r<cands.length; r++)\r
414             {\r
415               if (cands[r].hasMap())\r
416               {\r
417                 if (cands[r].getMap().getTo()!=null && cands[r].getMap().getMap().getFromRatio()!=cands[r].getMap().getMap().getToRatio())\r
418                 {\r
419                   foundmap=true;\r
420                   // get sense of map correct for adding to product alignment.\r
421                   if (dna)\r
422                   {\r
423                     // map is from dna seq to a protein product\r
424                     cf.addMap(sequenceI, nxt, cands[r].getMap().getMap()); \r
425                   } else {\r
426                     // map should be from protein seq to its coding dna\r
427                     cf.addMap(nxt, sequenceI, cands[r].getMap().getMap().getInverse());\r
428                   }\r
429                 }\r
430               }\r
431             }\r
432             // TODO: add mapping between sequences if necessary\r
433             found = true;\r
434           }\r
435           \r
436         }\r
437       }\r
438     }\r
439     return found;\r
440   }\r
441 \r
442   /**\r
443    * precalculate different products that can be found for seqs in dataset\r
444    * and return them.\r
445    * @param dna\r
446    * @param seqs\r
447    * @param dataset\r
448    * @param fake - don't actually build lists - just get types\r
449    * @return\r
450   public static Object[] buildXProductsList(boolean dna, SequenceI[] seqs, AlignmentI dataset, boolean fake)\r
451   {\r
452     String types[] = jalview.analysis.CrossRef.findSequenceXrefTypes(\r
453             dna, seqs, dataset);\r
454     if (types != null)\r
455     {\r
456       System.out.println("Xref Types for: "+(dna ? "dna" : "prot"));\r
457       for (int t = 0; t < types.length; t++)\r
458       {\r
459         System.out.println("Type: " + types[t]);\r
460         SequenceI[] prod = \r
461           jalview.analysis.CrossRef.findXrefSequences(seqs, dna, types[t]);\r
462         System.out.println("Found "\r
463                 + ((prod == null) ? "no" : "" + prod.length)\r
464                 + " products");\r
465         if (prod!=null)\r
466         {\r
467           for (int p=0; p<prod.length; p++)\r
468           {\r
469             System.out.println("Prod "+p+": "+prod[p].getDisplayId(true));\r
470           }\r
471         }\r
472       }\r
473 \r
474     } else {\r
475       System.out.println("Trying getProducts for "+al.getSequenceAt(0).getDisplayId(true));\r
476       System.out.println("Search DS Xref for: "+(dna ? "dna" : "prot"));\r
477       // have a bash at finding the products amongst all the retrieved sequences.\r
478       SequenceI[] prod = jalview.analysis.CrossRef.findXrefSequences(al\r
479               .getSequencesArray(), dna, null, ds);\r
480       System.out.println("Found "\r
481               + ((prod == null) ? "no" : "" + prod.length)\r
482               + " products");\r
483       if (prod!=null)\r
484       {\r
485         // select non-equivalent sequences from dataset list\r
486         for (int p=0; p<prod.length; p++)\r
487         {\r
488           System.out.println("Prod "+p+": "+prod[p].getDisplayId(true));\r
489         }\r
490       }\r
491 \r
492     }\r
493   }\r
494    */\r
495 }