feature locations are retrieved by associated accession string and public debug repor...
[jalview.git] / src / jalview / datamodel / xdb / embl / EmblEntry.java
1 package jalview.datamodel.xdb.embl;
2
3 import jalview.datamodel.DBRefEntry;
4 import jalview.datamodel.Sequence;
5 import jalview.datamodel.SequenceFeature;
6 import jalview.datamodel.SequenceI;
7
8 import java.util.Enumeration;
9 import java.util.Hashtable;
10 import java.util.Iterator;
11 import java.util.Vector;
12
13 public class EmblEntry
14 {
15   String accession;
16
17   String version;
18
19   String taxDivision;
20
21   String desc;
22
23   String rCreated;
24
25   String rLastUpdated;
26
27   String lastUpdated;
28
29   Vector keywords;
30
31   Vector refs;
32
33   Vector dbRefs;
34
35   Vector features;
36
37   EmblSequence sequence;
38
39   /**
40    * @return the accession
41    */
42   public String getAccession()
43   {
44     return accession;
45   }
46
47   /**
48    * @param accession
49    *          the accession to set
50    */
51   public void setAccession(String accession)
52   {
53     this.accession = accession;
54   }
55
56   /**
57    * @return the dbRefs
58    */
59   public Vector getDbRefs()
60   {
61     return dbRefs;
62   }
63
64   /**
65    * @param dbRefs
66    *          the dbRefs to set
67    */
68   public void setDbRefs(Vector dbRefs)
69   {
70     this.dbRefs = dbRefs;
71   }
72
73   /**
74    * @return the desc
75    */
76   public String getDesc()
77   {
78     return desc;
79   }
80
81   /**
82    * @param desc
83    *          the desc to set
84    */
85   public void setDesc(String desc)
86   {
87     this.desc = desc;
88   }
89
90   /**
91    * @return the features
92    */
93   public Vector getFeatures()
94   {
95     return features;
96   }
97
98   /**
99    * @param features
100    *          the features to set
101    */
102   public void setFeatures(Vector features)
103   {
104     this.features = features;
105   }
106
107   /**
108    * @return the keywords
109    */
110   public Vector getKeywords()
111   {
112     return keywords;
113   }
114
115   /**
116    * @param keywords
117    *          the keywords to set
118    */
119   public void setKeywords(Vector keywords)
120   {
121     this.keywords = keywords;
122   }
123
124   /**
125    * @return the lastUpdated
126    */
127   public String getLastUpdated()
128   {
129     return lastUpdated;
130   }
131
132   /**
133    * @param lastUpdated
134    *          the lastUpdated to set
135    */
136   public void setLastUpdated(String lastUpdated)
137   {
138     this.lastUpdated = lastUpdated;
139   }
140
141   /**
142    * @return the refs
143    */
144   public Vector getRefs()
145   {
146     return refs;
147   }
148
149   /**
150    * @param refs
151    *          the refs to set
152    */
153   public void setRefs(Vector refs)
154   {
155     this.refs = refs;
156   }
157
158   /**
159    * @return the releaseCreated
160    */
161   public String getRCreated()
162   {
163     return rCreated;
164   }
165
166   /**
167    * @param releaseCreated
168    *          the releaseCreated to set
169    */
170   public void setRcreated(String releaseCreated)
171   {
172     this.rCreated = releaseCreated;
173   }
174
175   /**
176    * @return the releaseLastUpdated
177    */
178   public String getRLastUpdated()
179   {
180     return rLastUpdated;
181   }
182
183   /**
184    * @param releaseLastUpdated
185    *          the releaseLastUpdated to set
186    */
187   public void setRLastUpdated(String releaseLastUpdated)
188   {
189     this.rLastUpdated = releaseLastUpdated;
190   }
191
192   /**
193    * @return the sequence
194    */
195   public EmblSequence getSequence()
196   {
197     return sequence;
198   }
199
200   /**
201    * @param sequence
202    *          the sequence to set
203    */
204   public void setSequence(EmblSequence sequence)
205   {
206     this.sequence = sequence;
207   }
208
209   /**
210    * @return the taxDivision
211    */
212   public String getTaxDivision()
213   {
214     return taxDivision;
215   }
216
217   /**
218    * @param taxDivision
219    *          the taxDivision to set
220    */
221   public void setTaxDivision(String taxDivision)
222   {
223     this.taxDivision = taxDivision;
224   }
225
226   /**
227    * @return the version
228    */
229   public String getVersion()
230   {
231     return version;
232   }
233
234   /**
235    * @param version
236    *          the version to set
237    */
238   public void setVersion(String version)
239   {
240     this.version = version;
241   }
242
243   /*
244    * EMBL Feature support is limited. The text below is included for the benefit
245    * of any developer working on improving EMBL feature import in Jalview.
246    * Extract from EMBL feature specification see
247    * http://www.embl-ebi.ac.uk/embl/Documentation/FT_definitions/feature_table.html
248    * 3.5 Location 3.5.1 Purpose
249    * 
250    * The location indicates the region of the presented sequence which
251    * corresponds to a feature.
252    * 
253    * 3.5.2 Format and conventions The location contains at least one sequence
254    * location descriptor and may contain one or more operators with one or more
255    * sequence location descriptors. Base numbers refer to the numbering in the
256    * entry. This numbering designates the first base (5' end) of the presented
257    * sequence as base 1. Base locations beyond the range of the presented
258    * sequence may not be used in location descriptors, the only exception being
259    * location in a remote entry (see 3.5.2.1, e).
260    * 
261    * Location operators and descriptors are discussed in more detail below.
262    * 
263    * 3.5.2.1 Location descriptors
264    * 
265    * The location descriptor can be one of the following: (a) a single base
266    * number (b) a site between two indicated adjoining bases (c) a single base
267    * chosen from within a specified range of bases (not allowed for new entries)
268    * (d) the base numbers delimiting a sequence span (e) a remote entry
269    * identifier followed by a local location descriptor (i.e., a-d)
270    * 
271    * A site between two adjoining nucleotides, such as endonucleolytic cleavage
272    * site, is indicated by listing the two points separated by a carat (^). The
273    * permitted formats for this descriptor are n^n+1 (for example 55^56), or,
274    * for circular molecules, n^1, where "n" is the full length of the molecule,
275    * ie 1000^1 for circular molecule with length 1000.
276    * 
277    * A single base chosen from a range of bases is indicated by the first base
278    * number and the last base number of the range separated by a single period
279    * (e.g., '12.21' indicates a single base taken from between the indicated
280    * points). From October 2006 the usage of this descriptor is restricted : it
281    * is illegal to use "a single base from a range" (c) either on its own or in
282    * combination with the "sequence span" (d) descriptor for newly created
283    * entries. The existing entries where such descriptors exist are going to be
284    * retrofitted.
285    * 
286    * Sequence spans are indicated by the starting base number and the ending
287    * base number separated by two periods (e.g., '34..456'). The '<' and '>'
288    * symbols may be used with the starting and ending base numbers to indicate
289    * that an end point is beyond the specified base number. The starting and
290    * ending base positions can be represented as distinct base numbers
291    * ('34..456') or a site between two indicated adjoining bases.
292    * 
293    * A location in a remote entry (not the entry to which the feature table
294    * belongs) can be specified by giving the accession-number and sequence
295    * version of the remote entry, followed by a colon ":", followed by a
296    * location descriptor which applies to that entry's sequence (i.e.
297    * J12345.1:1..15, see also examples below)
298    * 
299    * 3.5.2.2 Operators
300    * 
301    * The location operator is a prefix that specifies what must be done to the
302    * indicated sequence to find or construct the location corresponding to the
303    * feature. A list of operators is given below with their definitions and most
304    * common format.
305    * 
306    * complement(location) Find the complement of the presented sequence in the
307    * span specified by " location" (i.e., read the complement of the presented
308    * strand in its 5'-to-3' direction)
309    * 
310    * join(location,location, ... location) The indicated elements should be
311    * joined (placed end-to-end) to form one contiguous sequence
312    * 
313    * order(location,location, ... location) The elements can be found in the
314    * specified order (5' to 3' direction), but nothing is implied about the
315    * reasonableness about joining them
316    * 
317    * Note : location operator "complement" can be used in combination with
318    * either " join" or "order" within the same location; combinations of "join"
319    * and "order" within the same location (nested operators) are illegal.
320    * 
321    * 
322    * 
323    * 3.5.3 Location examples
324    * 
325    * The following is a list of common location descriptors with their meanings:
326    * 
327    * Location Description
328    * 
329    * 467 Points to a single base in the presented sequence
330    * 
331    * 340..565 Points to a continuous range of bases bounded by and including the
332    * starting and ending bases
333    * 
334    * <345..500 Indicates that the exact lower boundary point of a feature is
335    * unknown. The location begins at some base previous to the first base
336    * specified (which need not be contained in the presented sequence) and
337    * continues to and includes the ending base
338    * 
339    * <1..888 The feature starts before the first sequenced base and continues to
340    * and includes base 888
341    * 
342    * 1..>888 The feature starts at the first sequenced base and continues beyond
343    * base 888
344    * 
345    * 102.110 Indicates that the exact location is unknown but that it is one of
346    * the bases between bases 102 and 110, inclusive
347    * 
348    * 123^124 Points to a site between bases 123 and 124
349    * 
350    * join(12..78,134..202) Regions 12 to 78 and 134 to 202 should be joined to
351    * form one contiguous sequence
352    * 
353    * 
354    * complement(34..126) Start at the base complementary to 126 and finish at
355    * the base complementary to base 34 (the feature is on the strand
356    * complementary to the presented strand)
357    * 
358    * 
359    * complement(join(2691..4571,4918..5163)) Joins regions 2691 to 4571 and 4918
360    * to 5163, then complements the joined segments (the feature is on the strand
361    * complementary to the presented strand)
362    * 
363    * join(complement(4918..5163),complement(2691..4571)) Complements regions
364    * 4918 to 5163 and 2691 to 4571, then joins the complemented segments (the
365    * feature is on the strand complementary to the presented strand)
366    * 
367    * J00194.1:100..202 Points to bases 100 to 202, inclusive, in the entry (in
368    * this database) with primary accession number 'J00194'
369    * 
370    * join(1..100,J00194.1:100..202) Joins region 1..100 of the existing entry
371    * with the region 100..202 of remote entry J00194
372    * 
373    */
374   /**
375    * Recover annotated sequences from EMBL file
376    * 
377    * @param noNa
378    *          don't return nucleic acid sequences
379    * @param sourceDb
380    *          TODO
381    * @param noProtein
382    *          don't return any translated protein sequences marked in features
383    * @return dataset sequences with DBRefs and features - DNA always comes first
384    */
385   public jalview.datamodel.SequenceI[] getSequences(boolean noNa,
386           boolean noPeptide, String sourceDb)
387   {
388     Vector seqs = new Vector();
389     Sequence dna = null;
390     if (!noNa)
391     {
392       dna = new Sequence(sourceDb + "|" + accession, sequence.getSequence());
393       dna.setDescription(desc);
394       dna.addDBRef(new DBRefEntry(sourceDb, version, accession));
395       // TODO: add mapping for parentAccession attribute
396       // TODO: transform EMBL Database refs to canonical form
397       if (dbRefs != null)
398         for (Iterator i = dbRefs.iterator(); i.hasNext(); dna
399                 .addDBRef((DBRefEntry) i.next()))
400           ;
401     }
402     try
403     {
404       for (Iterator i = features.iterator(); i.hasNext();)
405       {
406         boolean nextFeature=false;
407         EmblFeature feature = (EmblFeature) i.next();
408         if (!noNa)
409         {
410           if (feature.dbRefs != null && feature.dbRefs.size() > 0)
411           {
412             for (Iterator dbr = feature.dbRefs.iterator(); dbr.hasNext(); dna
413                     .addDBRef((DBRefEntry) dbr.next()))
414               ;
415           }
416         }
417         if (feature.getName().equalsIgnoreCase("CDS"))
418         {
419           // extract coding region(s)
420           jalview.datamodel.Mapping map = null;
421           int[] exon = null;
422           if (feature.locations != null && feature.locations.size() > 0)
423           {
424             for (Enumeration locs = feature.locations.elements(); locs
425                     .hasMoreElements();)
426             {
427               EmblFeatureLocations loc = (EmblFeatureLocations) locs
428                       .nextElement();
429               int[] se = loc.getElementRanges(accession);
430               if (exon == null)
431               {
432                 exon = se;
433               }
434               else
435               {
436                 int[] t = new int[exon.length + se.length];
437                 System.arraycopy(exon, 0, t, 0, exon.length);
438                 System.arraycopy(se, 0, t, exon.length, se.length);
439                 exon = t;
440               }
441             }
442           }
443           String prseq = null;
444           String prname = new String();
445           String prid = null;
446           Hashtable vals = new Hashtable();
447           int prstart = 1;
448           // get qualifiers
449           if (feature.getQualifiers() != null
450                   && feature.getQualifiers().size() > 0)
451           {
452             for (Iterator quals = feature.getQualifiers().iterator(); quals
453                     .hasNext();)
454             {
455               Qualifier q = (Qualifier) quals.next();
456               if (q.getName().equals("translation"))
457               {
458                 prseq = q.getValues()[0];
459               }
460               else if (q.getName().equals("protein_id"))
461               {
462                 prid = q.getValues()[0];
463               }
464               else if (q.getName().equals("codon_start"))
465               {
466                 prstart = Integer.parseInt(q.getValues()[0]);
467               }
468               else if (q.getName().equals("product"))
469               {
470                 prname = q.getValues()[0];
471               }
472               else
473               {
474                 // throw anything else into the additional properties hash
475                 vals.put(q.getName(), q.getValues().toString());
476               }
477             }
478           }
479           Sequence product = null;
480           if (prseq != null && prname != null && prid != null)
481           {
482             // extract proteins.
483             if (!noPeptide)
484             {
485               product = new Sequence(sourceDb + "|" + "EMBLCDS|" + prid
486                       + "|" + prname, prseq, prstart, prstart
487                       + prseq.length() - 1);
488               product.setDescription("Protein Product from " + sourceDb);
489               seqs.add(product);
490             }
491             // we have everything - create the mapping and perhaps the protein
492             // sequence
493             map = new jalview.datamodel.Mapping(product, exon, new int[]
494             { prstart, prstart + prseq.length() - 1 }, 3, 1);
495             // add cds feature to dna seq - this may include the stop codon
496             for (int xint = 0; xint < exon.length; xint += 2)
497             {
498               SequenceFeature sf = new SequenceFeature();
499               sf.setBegin(exon[xint]);
500               sf.setEnd(exon[xint + 1]);
501               sf.setType(feature.getName());
502               sf.setFeatureGroup(jalview.datamodel.DBRefSource.EMBL);
503               sf.setDescription("Exon " + (1 + xint) + " for protein '"
504                       + prname + "' EMBLCDS:" + prid);
505               if (vals != null && vals.size() > 0)
506               {
507                 Enumeration kv = vals.elements();
508                 while (kv.hasMoreElements())
509                 {
510                   Object key = kv.nextElement();
511                   if (key != null)
512                     sf.setValue(key.toString(), vals.get(key));
513                 }
514               }
515               dna.addSequenceFeature(sf);
516             }
517           }
518           // add dbRefs to sequence
519           if (feature.dbRefs != null && feature.dbRefs.size() > 0)
520           {
521             for (Iterator dbr = feature.dbRefs.iterator(); dbr.hasNext();)
522             {
523               DBRefEntry ref = (DBRefEntry) dbr.next();
524               ref.setSource(jalview.util.DBRefUtils.getCanonicalName(ref
525                       .getSource()));
526               if (ref.getSource().equals(
527                       jalview.datamodel.DBRefSource.UNIPROT))
528               {
529                 ref.setMap(map);
530               }
531               if (product != null)
532               {
533                 DBRefEntry pref = new DBRefEntry(ref.getSource(), ref
534                         .getVersion(), ref.getAccessionId());
535                 pref.setMap(null); // reference is direct
536               }
537               dna.addDBRef(ref);
538             }
539           }
540
541         }
542         else
543         {
544           // General feature type.
545           if (!noNa)
546           {
547             if (feature.dbRefs != null && feature.dbRefs.size() > 0)
548             {
549               for (Iterator dbr = feature.dbRefs.iterator(); dbr.hasNext(); dna
550                       .addDBRef((DBRefEntry) dbr.next()))
551                 ;
552             }
553           }
554         }
555       }
556     } catch (Exception e)
557     {
558       System.err.println("EMBL Record Features parsing error!");
559       System.err.println("Please report the following to help@jalview.org :");
560       System.err.println("EMBL Record "+accession);
561       System.err.println("Resulted in exception: "+e.getMessage());
562       e.printStackTrace(System.err);
563     }
564     if (!noNa && dna!=null)
565     {
566       seqs.add(dna);
567     }
568     SequenceI[] sqs = new SequenceI[seqs.size()];
569     for (int i = 0, j = seqs.size(); i < j; i++)
570     {
571       sqs[i] = (SequenceI) seqs.elementAt(i);
572       seqs.set(i, null);
573     }
574     return sqs;
575   }
576 }