87e2789ae94db5f2e51556cb3281eb1edd03d537
[jalview.git] / src / jalview / datamodel / xdb / embl / EmblEntry.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.datamodel.xdb.embl;
22
23 import jalview.datamodel.DBRefEntry;
24 import jalview.datamodel.DBRefSource;
25 import jalview.datamodel.FeatureProperties;
26 import jalview.datamodel.Mapping;
27 import jalview.datamodel.Sequence;
28 import jalview.datamodel.SequenceFeature;
29 import jalview.datamodel.SequenceI;
30
31 import java.util.Hashtable;
32 import java.util.Map.Entry;
33 import java.util.Vector;
34
35 /**
36  * Data model for one entry returned from an EMBL query, as marshalled by a
37  * Castor binding file
38  * 
39  * For example: http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/embl/x53828/emblxml
40  * 
41  * @see embl_mapping.xml
42  */
43 public class EmblEntry
44 {
45   String accession;
46
47   String version;
48
49   String taxDivision;
50
51   String desc;
52
53   String rCreated;
54
55   String rLastUpdated;
56
57   String lastUpdated;
58
59   Vector<String> keywords;
60
61   Vector<DBRefEntry> dbRefs;
62
63   Vector<EmblFeature> features;
64
65   EmblSequence sequence;
66
67   /**
68    * @return the accession
69    */
70   public String getAccession()
71   {
72     return accession;
73   }
74
75   /**
76    * @param accession
77    *          the accession to set
78    */
79   public void setAccession(String accession)
80   {
81     this.accession = accession;
82   }
83
84   /**
85    * @return the dbRefs
86    */
87   public Vector<DBRefEntry> getDbRefs()
88   {
89     return dbRefs;
90   }
91
92   /**
93    * @param dbRefs
94    *          the dbRefs to set
95    */
96   public void setDbRefs(Vector<DBRefEntry> dbRefs)
97   {
98     this.dbRefs = dbRefs;
99   }
100
101   /**
102    * @return the desc
103    */
104   public String getDesc()
105   {
106     return desc;
107   }
108
109   /**
110    * @param desc
111    *          the desc to set
112    */
113   public void setDesc(String desc)
114   {
115     this.desc = desc;
116   }
117
118   /**
119    * @return the features
120    */
121   public Vector<EmblFeature> getFeatures()
122   {
123     return features;
124   }
125
126   /**
127    * @param features
128    *          the features to set
129    */
130   public void setFeatures(Vector<EmblFeature> features)
131   {
132     this.features = features;
133   }
134
135   /**
136    * @return the keywords
137    */
138   public Vector<String> getKeywords()
139   {
140     return keywords;
141   }
142
143   /**
144    * @param keywords
145    *          the keywords to set
146    */
147   public void setKeywords(Vector<String> keywords)
148   {
149     this.keywords = keywords;
150   }
151
152   /**
153    * @return the lastUpdated
154    */
155   public String getLastUpdated()
156   {
157     return lastUpdated;
158   }
159
160   /**
161    * @param lastUpdated
162    *          the lastUpdated to set
163    */
164   public void setLastUpdated(String lastUpdated)
165   {
166     this.lastUpdated = lastUpdated;
167   }
168
169   /**
170    * @return the releaseCreated
171    */
172   public String getRCreated()
173   {
174     return rCreated;
175   }
176
177   /**
178    * @param releaseCreated
179    *          the releaseCreated to set
180    */
181   public void setRCreated(String releaseCreated)
182   {
183     this.rCreated = releaseCreated;
184   }
185
186   /**
187    * @return the releaseLastUpdated
188    */
189   public String getRLastUpdated()
190   {
191     return rLastUpdated;
192   }
193
194   /**
195    * @param releaseLastUpdated
196    *          the releaseLastUpdated to set
197    */
198   public void setRLastUpdated(String releaseLastUpdated)
199   {
200     this.rLastUpdated = releaseLastUpdated;
201   }
202
203   /**
204    * @return the sequence
205    */
206   public EmblSequence getSequence()
207   {
208     return sequence;
209   }
210
211   /**
212    * @param sequence
213    *          the sequence to set
214    */
215   public void setSequence(EmblSequence sequence)
216   {
217     this.sequence = sequence;
218   }
219
220   /**
221    * @return the taxDivision
222    */
223   public String getTaxDivision()
224   {
225     return taxDivision;
226   }
227
228   /**
229    * @param taxDivision
230    *          the taxDivision to set
231    */
232   public void setTaxDivision(String taxDivision)
233   {
234     this.taxDivision = taxDivision;
235   }
236
237   /**
238    * @return the version
239    */
240   public String getVersion()
241   {
242     return version;
243   }
244
245   /**
246    * @param version
247    *          the version to set
248    */
249   public void setVersion(String version)
250   {
251     this.version = version;
252   }
253
254   /*
255    * EMBL Feature support is limited. The text below is included for the benefit
256    * of any developer working on improving EMBL feature import in Jalview.
257    * Extract from EMBL feature specification see
258    * http://www.embl-ebi.ac.uk/embl/Documentation
259    * /FT_definitions/feature_table.html 3.5 Location 3.5.1 Purpose
260    * 
261    * The location indicates the region of the presented sequence which
262    * corresponds to a feature.
263    * 
264    * 3.5.2 Format and conventions The location contains at least one sequence
265    * location descriptor and may contain one or more operators with one or more
266    * sequence location descriptors. Base numbers refer to the numbering in the
267    * entry. This numbering designates the first base (5' end) of the presented
268    * sequence as base 1. Base locations beyond the range of the presented
269    * sequence may not be used in location descriptors, the only exception being
270    * location in a remote entry (see 3.5.2.1, e).
271    * 
272    * Location operators and descriptors are discussed in more detail below.
273    * 
274    * 3.5.2.1 Location descriptors
275    * 
276    * The location descriptor can be one of the following: (a) a single base
277    * number (b) a site between two indicated adjoining bases (c) a single base
278    * chosen from within a specified range of bases (not allowed for new entries)
279    * (d) the base numbers delimiting a sequence span (e) a remote entry
280    * identifier followed by a local location descriptor (i.e., a-d)
281    * 
282    * A site between two adjoining nucleotides, such as endonucleolytic cleavage
283    * site, is indicated by listing the two points separated by a carat (^). The
284    * permitted formats for this descriptor are n^n+1 (for example 55^56), or,
285    * for circular molecules, n^1, where "n" is the full length of the molecule,
286    * ie 1000^1 for circular molecule with length 1000.
287    * 
288    * A single base chosen from a range of bases is indicated by the first base
289    * number and the last base number of the range separated by a single period
290    * (e.g., '12.21' indicates a single base taken from between the indicated
291    * points). From October 2006 the usage of this descriptor is restricted : it
292    * is illegal to use "a single base from a range" (c) either on its own or in
293    * combination with the "sequence span" (d) descriptor for newly created
294    * entries. The existing entries where such descriptors exist are going to be
295    * retrofitted.
296    * 
297    * Sequence spans are indicated by the starting base number and the ending
298    * base number separated by two periods (e.g., '34..456'). The '<' and '>'
299    * symbols may be used with the starting and ending base numbers to indicate
300    * that an end point is beyond the specified base number. The starting and
301    * ending base positions can be represented as distinct base numbers
302    * ('34..456') or a site between two indicated adjoining bases.
303    * 
304    * A location in a remote entry (not the entry to which the feature table
305    * belongs) can be specified by giving the accession-number and sequence
306    * version of the remote entry, followed by a colon ":", followed by a
307    * location descriptor which applies to that entry's sequence (i.e.
308    * J12345.1:1..15, see also examples below)
309    * 
310    * 3.5.2.2 Operators
311    * 
312    * The location operator is a prefix that specifies what must be done to the
313    * indicated sequence to find or construct the location corresponding to the
314    * feature. A list of operators is given below with their definitions and most
315    * common format.
316    * 
317    * complement(location) Find the complement of the presented sequence in the
318    * span specified by " location" (i.e., read the complement of the presented
319    * strand in its 5'-to-3' direction)
320    * 
321    * join(location,location, ... location) The indicated elements should be
322    * joined (placed end-to-end) to form one contiguous sequence
323    * 
324    * order(location,location, ... location) The elements can be found in the
325    * specified order (5' to 3' direction), but nothing is implied about the
326    * reasonableness about joining them
327    * 
328    * Note : location operator "complement" can be used in combination with
329    * either " join" or "order" within the same location; combinations of "join"
330    * and "order" within the same location (nested operators) are illegal.
331    * 
332    * 
333    * 
334    * 3.5.3 Location examples
335    * 
336    * The following is a list of common location descriptors with their meanings:
337    * 
338    * Location Description
339    * 
340    * 467 Points to a single base in the presented sequence
341    * 
342    * 340..565 Points to a continuous range of bases bounded by and including the
343    * starting and ending bases
344    * 
345    * <345..500 Indicates that the exact lower boundary point of a feature is
346    * unknown. The location begins at some base previous to the first base
347    * specified (which need not be contained in the presented sequence) and
348    * continues to and includes the ending base
349    * 
350    * <1..888 The feature starts before the first sequenced base and continues to
351    * and includes base 888
352    * 
353    * 1..>888 The feature starts at the first sequenced base and continues beyond
354    * base 888
355    * 
356    * 102.110 Indicates that the exact location is unknown but that it is one of
357    * the bases between bases 102 and 110, inclusive
358    * 
359    * 123^124 Points to a site between bases 123 and 124
360    * 
361    * join(12..78,134..202) Regions 12 to 78 and 134 to 202 should be joined to
362    * form one contiguous sequence
363    * 
364    * 
365    * complement(34..126) Start at the base complementary to 126 and finish at
366    * the base complementary to base 34 (the feature is on the strand
367    * complementary to the presented strand)
368    * 
369    * 
370    * complement(join(2691..4571,4918..5163)) Joins regions 2691 to 4571 and 4918
371    * to 5163, then complements the joined segments (the feature is on the strand
372    * complementary to the presented strand)
373    * 
374    * join(complement(4918..5163),complement(2691..4571)) Complements regions
375    * 4918 to 5163 and 2691 to 4571, then joins the complemented segments (the
376    * feature is on the strand complementary to the presented strand)
377    * 
378    * J00194.1:100..202 Points to bases 100 to 202, inclusive, in the entry (in
379    * this database) with primary accession number 'J00194'
380    * 
381    * join(1..100,J00194.1:100..202) Joins region 1..100 of the existing entry
382    * with the region 100..202 of remote entry J00194
383    */
384   /**
385    * Recover annotated sequences from EMBL file
386    * 
387    * @param noNa
388    *          don't return nucleic acid sequences
389    * @param sourceDb
390    *          TODO
391    * @param noProtein
392    *          don't return any translated protein sequences marked in features
393    * @return dataset sequences with DBRefs and features - DNA always comes first
394    */
395   public jalview.datamodel.SequenceI[] getSequences(boolean noNa,
396           boolean noPeptide, String sourceDb)
397   { // TODO: ensure emblEntry.getSequences behaves correctly for returning all
398     // cases of noNa and noPeptide
399     Vector<SequenceI> seqs = new Vector<SequenceI>();
400     Sequence dna = null;
401     if (!noNa)
402     {
403       // In theory we still need to create this if noNa is set to avoid a null
404       // pointer exception
405       dna = new Sequence(sourceDb + "|" + accession, sequence.getSequence());
406       dna.setDescription(desc);
407       DBRefEntry retrievedref = new DBRefEntry(sourceDb, version, accession);
408       dna.addDBRef(retrievedref);
409       // add map to indicate the sequence is a valid coordinate frame for the
410       // dbref
411       retrievedref.setMap(new Mapping(null,
412               new int[] { 1, dna.getLength() }, new int[] { 1,
413                   dna.getLength() }, 1, 1));
414       // TODO: transform EMBL Database refs to canonical form
415       if (dbRefs != null)
416       {
417         for (DBRefEntry dbref : dbRefs)
418         {
419           dna.addDBRef(dbref);
420         }
421       }
422     }
423     try
424     {
425       for (EmblFeature feature : features)
426       {
427         if (!noNa)
428         {
429           if (feature.dbRefs != null)
430           {
431             for (DBRefEntry dbref : feature.dbRefs)
432             {
433               dna.addDBRef(dbref);
434             }
435           }
436         }
437         if (FeatureProperties.isCodingFeature(sourceDb, feature.getName()))
438         {
439           parseCodingFeature(feature, sourceDb, seqs, dna, noPeptide);
440         }
441         else
442         {
443           // General feature type.
444           // TODO this is just duplicated code ??
445           if (!noNa)
446           {
447             if (feature.dbRefs != null)
448             {
449               for (DBRefEntry dbref : feature.dbRefs)
450               {
451                 dna.addDBRef(dbref);
452               }
453             }
454           }
455         }
456       }
457     } catch (Exception e)
458     {
459       System.err.println("EMBL Record Features parsing error!");
460       System.err
461               .println("Please report the following to help@jalview.org :");
462       System.err.println("EMBL Record " + accession);
463       System.err.println("Resulted in exception: " + e.getMessage());
464       e.printStackTrace(System.err);
465     }
466     if (!noNa && dna != null)
467     {
468       seqs.add(dna);
469     }
470     SequenceI[] sqs = new SequenceI[seqs.size()];
471     for (int i = 0, j = seqs.size(); i < j; i++)
472     {
473       sqs[i] = seqs.elementAt(i);
474       seqs.set(i, null);
475     }
476     return sqs;
477   }
478
479   /**
480    * attempt to extract coding region and product from a feature and properly
481    * decorate it with annotations.
482    * 
483    * @param feature
484    *          coding feature
485    * @param sourceDb
486    *          source database for the EMBLXML
487    * @param seqs
488    *          place where sequences go
489    * @param dna
490    *          parent dna sequence for this record
491    * @param noPeptide
492    *          flag for generation of Peptide sequence objects
493    */
494   private void parseCodingFeature(EmblFeature feature, String sourceDb,
495           Vector<SequenceI> seqs, Sequence dna, boolean noPeptide)
496   {
497     boolean isEmblCdna = sourceDb.equals(DBRefSource.EMBLCDS);
498     // extract coding region(s)
499     jalview.datamodel.Mapping map = null;
500     int[] exon = null;
501     if (feature.locations != null)
502     {
503       for (EmblFeatureLocations loc : feature.locations)
504       {
505         int[] se = loc.getElementRanges(accession);
506         if (exon == null)
507         {
508           exon = se;
509         }
510         else
511         {
512           int[] t = new int[exon.length + se.length];
513           System.arraycopy(exon, 0, t, 0, exon.length);
514           System.arraycopy(se, 0, t, exon.length, se.length);
515           exon = t;
516         }
517       }
518     }
519     String prseq = null;
520     String prname = new String();
521     String prid = null;
522     Hashtable<String, String> vals = new Hashtable<String, String>();
523     int prstart = 1;
524     // get qualifiers
525     if (feature.getQualifiers() != null)
526     {
527       for (Qualifier q : feature.getQualifiers())
528       {
529         String qname = q.getName();
530         if (qname.equals("translation"))
531         {
532           StringBuilder prsq = new StringBuilder(q.getValues()[0]);
533           int p = prsq.indexOf(" ");
534           while (p > -1)
535           {
536             prsq.deleteCharAt(p);
537             p = prsq.indexOf(" ", p);
538           }
539           prseq = prsq.toString();
540           prsq = null;
541
542         }
543         else if (qname.equals("protein_id"))
544         {
545           prid = q.getValues()[0];
546         }
547         else if (qname.equals("codon_start"))
548         {
549           prstart = Integer.parseInt(q.getValues()[0]);
550         }
551         else if (qname.equals("product"))
552         {
553           prname = q.getValues()[0];
554         }
555         else
556         {
557           // throw anything else into the additional properties hash
558           String[] s = q.getValues();
559           StringBuilder sb = new StringBuilder();
560           if (s != null)
561           {
562             for (int i = 0; i < s.length; i++)
563             {
564               sb.append(s[i]);
565               sb.append("\n");
566             }
567           }
568           vals.put(qname, sb.toString());
569         }
570       }
571     }
572     Sequence product = null;
573     DBRefEntry protEMBLCDS = null;
574     exon = adjustForPrStart(prstart, exon);
575     boolean noProteinDbref = true;
576
577     if (prseq != null && prname != null && prid != null)
578     {
579       // extract proteins.
580       product = new Sequence(prid, prseq, 1, prseq.length());
581       product.setDescription(((prname.length() == 0) ? "Protein Product from "
582               + sourceDb
583               : prname));
584       if (!noPeptide)
585       {
586         // Protein is also added to vector of sequences returned
587         seqs.add(product);
588       }
589       // we have everything - create the mapping and perhaps the protein
590       // sequence
591       if (exon == null || exon.length == 0)
592       {
593         System.err
594                 .println("Implementation Notice: EMBLCDS records not properly supported yet - Making up the CDNA region of this sequence... may be incorrect ("
595                         + sourceDb + ":" + getAccession() + ")");
596         if (prseq.length() * 3 == (1 - prstart + dna.getSequence().length))
597         {
598           System.err
599                   .println("Not allowing for additional stop codon at end of cDNA fragment... !");
600           // this might occur for CDS sequences where no features are
601           // marked.
602           exon = new int[] { dna.getStart() + (prstart - 1), dna.getEnd() };
603           map = new jalview.datamodel.Mapping(product, exon, new int[] { 1,
604               prseq.length() }, 3, 1);
605         }
606         if ((prseq.length() + 1) * 3 == (1 - prstart + dna.getSequence().length))
607         {
608           System.err
609                   .println("Allowing for additional stop codon at end of cDNA fragment... will probably cause an error in VAMSAs!");
610           exon = new int[] { dna.getStart() + (prstart - 1),
611               dna.getEnd() - 3 };
612           map = new jalview.datamodel.Mapping(product, exon, new int[] { 1,
613               prseq.length() }, 3, 1);
614         }
615       }
616       else
617       {
618         // Trim the exon mapping if necessary - the given product may only be a
619         // fragment of a larger protein. (EMBL:AY043181 is an example)
620
621         if (isEmblCdna)
622         {
623           // TODO: Add a DbRef back to the parent EMBL sequence with the exon
624           // map
625           // if given a dataset reference, search dataset for parent EMBL
626           // sequence if it exists and set its map
627           // make a new feature annotating the coding contig
628         }
629         else
630         {
631           // final product length trunctation check
632
633           map = new jalview.datamodel.Mapping(product,
634                   adjustForProteinLength(prseq.length(), exon), new int[] {
635                       1, prseq.length() }, 3, 1);
636           // reconstruct the EMBLCDS entry
637           // TODO: this is only necessary when there codon annotation is
638           // complete (I think JBPNote)
639           DBRefEntry pcdnaref = new DBRefEntry();
640           pcdnaref.setAccessionId(prid);
641           pcdnaref.setSource(DBRefSource.EMBLCDS);
642           pcdnaref.setVersion(getVersion()); // same as parent EMBL version.
643           jalview.util.MapList mp = new jalview.util.MapList(new int[] { 1,
644               prseq.length() }, new int[] { 1 + (prstart - 1),
645               (prstart - 1) + 3 * prseq.length() }, 1, 3);
646           // { 1 + (prstart - 1) * 3,
647           // 1 + (prstart - 1) * 3 + prseq.length() * 3 - 1 }, new int[]
648           // { 1prstart, prstart + prseq.length() - 1 }, 3, 1);
649           pcdnaref.setMap(new Mapping(mp));
650           if (product != null)
651           {
652             product.addDBRef(pcdnaref);
653             protEMBLCDS = new DBRefEntry(pcdnaref);
654             protEMBLCDS.setSource(DBRefSource.EMBLCDSProduct);
655             product.addDBRef(protEMBLCDS);
656
657           }
658
659         }
660       }
661       // add cds feature to dna seq - this may include the stop codon
662       for (int xint = 0; exon != null && xint < exon.length; xint += 2)
663       {
664         SequenceFeature sf = new SequenceFeature();
665         sf.setBegin(exon[xint]);
666         sf.setEnd(exon[xint + 1]);
667         sf.setType(feature.getName());
668         sf.setFeatureGroup(sourceDb);
669         sf.setDescription("Exon " + (1 + xint / 2) + " for protein '"
670                 + prname + "' EMBLCDS:" + prid);
671         sf.setValue(FeatureProperties.EXONPOS, new Integer(1 + xint));
672         sf.setValue(FeatureProperties.EXONPRODUCT, prname);
673         if (vals != null)
674         {
675           for (Entry<String, String> val : vals.entrySet())
676           {
677             sf.setValue(val.getKey(), val.getValue());
678           }
679         }
680         dna.addSequenceFeature(sf);
681       }
682     }
683     // add dbRefs to sequence
684     if (feature.dbRefs != null)
685     {
686       for (DBRefEntry ref : feature.dbRefs)
687       {
688         ref.setSource(jalview.util.DBRefUtils.getCanonicalName(ref
689                 .getSource()));
690         // Hard code the kind of protein product accessions that EMBL cite
691         if (ref.getSource().equals(jalview.datamodel.DBRefSource.UNIPROT))
692         {
693           ref.setMap(map);
694           if (map != null && map.getTo() != null)
695           {
696             map.getTo().addDBRef(
697                     new DBRefEntry(ref.getSource(), ref.getVersion(), ref
698                             .getAccessionId())); // don't copy map over.
699             if (map.getTo().getName().indexOf(prid) == 0)
700             {
701               map.getTo().setName(
702                       jalview.datamodel.DBRefSource.UNIPROT + "|"
703                               + ref.getAccessionId());
704             }
705           }
706           noProteinDbref = false;
707         }
708         if (product != null)
709         {
710           DBRefEntry pref = new DBRefEntry(ref.getSource(),
711                   ref.getVersion(), ref.getAccessionId());
712           pref.setMap(null); // reference is direct
713           product.addDBRef(pref);
714           // Add converse mapping reference
715           if (map != null)
716           {
717             Mapping pmap = new Mapping(dna, map.getMap().getInverse());
718             pref = new DBRefEntry(sourceDb, getVersion(),
719                     this.getAccession());
720             pref.setMap(pmap);
721             if (map.getTo() != null)
722             {
723               map.getTo().addDBRef(pref);
724             }
725           }
726         }
727         dna.addDBRef(ref);
728       }
729       if (noProteinDbref && product != null)
730       {
731         // add protein coding reference to dna sequence so xref matches
732         if (protEMBLCDS == null)
733         {
734           protEMBLCDS = new DBRefEntry();
735           protEMBLCDS.setAccessionId(prid);
736           protEMBLCDS.setSource(DBRefSource.EMBLCDSProduct);
737           protEMBLCDS.setVersion(getVersion());
738           protEMBLCDS
739                   .setMap(new Mapping(product, map.getMap().getInverse()));
740         }
741         product.addDBRef(protEMBLCDS);
742
743         // Add converse mapping reference
744         if (map != null)
745         {
746           Mapping pmap = new Mapping(product, protEMBLCDS.getMap().getMap()
747                   .getInverse());
748           DBRefEntry ncMap = new DBRefEntry(protEMBLCDS);
749           ncMap.setMap(pmap);
750           if (map.getTo() != null)
751           {
752             dna.addDBRef(ncMap);
753           }
754         }
755       }
756     }
757   }
758
759   private int[] adjustForPrStart(int prstart, int[] exon)
760   {
761
762     int origxon[], sxpos = -1;
763     int sxstart, sxstop; // unnecessary variables used for debugging
764     // first adjust range for codon start attribute
765     if (prstart > 1)
766     {
767       origxon = new int[exon.length];
768       System.arraycopy(exon, 0, origxon, 0, exon.length);
769       int cdspos = 0;
770       for (int x = 0; x < exon.length && sxpos == -1; x += 2)
771       {
772         cdspos += exon[x + 1] - exon[x] + 1;
773         if (prstart <= cdspos)
774         {
775           sxpos = x;
776           sxstart = exon[x];
777           sxstop = exon[x + 1];
778           // and adjust start boundary of first exon.
779           exon[x] = exon[x + 1] - cdspos + prstart;
780           break;
781         }
782       }
783
784       if (sxpos > 0)
785       {
786         int[] nxon = new int[exon.length - sxpos];
787         System.arraycopy(exon, sxpos, nxon, 0, exon.length - sxpos);
788         exon = nxon;
789       }
790     }
791     return exon;
792   }
793
794   /**
795    * truncate the last exon interval to the prlength'th codon
796    * 
797    * @param prlength
798    * @param exon
799    * @return new exon
800    */
801   private int[] adjustForProteinLength(int prlength, int[] exon)
802   {
803
804     int origxon[], sxpos = -1, endxon = 0, cdslength = prlength * 3;
805     int sxstart, sxstop; // unnecessary variables used for debugging
806     // first adjust range for codon start attribute
807     if (prlength >= 1 && exon != null)
808     {
809       origxon = new int[exon.length];
810       System.arraycopy(exon, 0, origxon, 0, exon.length);
811       int cdspos = 0;
812       for (int x = 0; x < exon.length && sxpos == -1; x += 2)
813       {
814         cdspos += exon[x + 1] - exon[x] + 1;
815         if (cdslength <= cdspos)
816         {
817           // advanced beyond last codon.
818           sxpos = x;
819           sxstart = exon[x];
820           sxstop = exon[x + 1];
821           if (cdslength != cdspos)
822           {
823             System.err
824                     .println("Truncating final exon interval on region by "
825                             + (cdspos - cdslength));
826           }
827           // locate the new end boundary of final exon as endxon
828           endxon = exon[x + 1] - cdspos + cdslength;
829           break;
830         }
831       }
832
833       if (sxpos != -1)
834       {
835         // and trim the exon interval set if necessary
836         int[] nxon = new int[sxpos + 2];
837         System.arraycopy(exon, 0, nxon, 0, sxpos + 2);
838         nxon[sxpos + 1] = endxon; // update the end boundary for the new exon
839                                   // set
840         exon = nxon;
841       }
842     }
843     return exon;
844   }
845 }