2 * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
\r
3 * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
\r
5 * This program is free software; you can redistribute it and/or
\r
6 * modify it under the terms of the GNU General Public License
\r
7 * as published by the Free Software Foundation; either version 2
\r
8 * of the License, or (at your option) any later version.
\r
10 * This program is distributed in the hope that it will be useful,
\r
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 * GNU General Public License for more details.
\r
15 * You should have received a copy of the GNU General Public License
\r
16 * along with this program; if not, write to the Free Software
\r
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
\r
19 package jalview.analysis;
\r
21 import java.util.Enumeration;
\r
22 import java.util.Hashtable;
\r
23 import java.util.Vector;
\r
25 import jalview.datamodel.AlignedCodonFrame;
\r
26 import jalview.datamodel.Alignment;
\r
27 import jalview.datamodel.AlignmentAnnotation;
\r
28 import jalview.datamodel.AlignmentI;
\r
29 import jalview.datamodel.Annotation;
\r
30 import jalview.datamodel.ColumnSelection;
\r
31 import jalview.datamodel.DBRefEntry;
\r
32 import jalview.datamodel.FeatureProperties;
\r
33 import jalview.datamodel.Mapping;
\r
34 import jalview.datamodel.Sequence;
\r
35 import jalview.datamodel.SequenceFeature;
\r
36 import jalview.datamodel.SequenceI;
\r
37 import jalview.schemes.ResidueProperties;
\r
38 import jalview.util.MapList;
\r
39 import jalview.util.ShiftList;
\r
47 * @return -1 if cdp1 aligns before cdp2, 0 if in the same column or cdp2 is
\r
48 * null, +1 if after cdp2
\r
50 private static int compare_codonpos(int[] cdp1, int[] cdp2)
\r
53 || (cdp1[0] == cdp2[0] && cdp1[1] == cdp2[1] && cdp1[2] == cdp2[2]))
\r
55 if (cdp1[0] < cdp2[0] || cdp1[1] < cdp2[1] || cdp1[2] < cdp2[2])
\r
56 return -1; // one base in cdp1 precedes the corresponding base in the
\r
58 return 1; // one base in cdp1 appears after the corresponding base in the
\r
63 * DNA->mapped protein sequence alignment translation given set of sequences
\r
64 * 1. id distinct coding regions within selected region for each sequence 2.
\r
65 * generate peptides based on inframe (or given) translation or (optionally
\r
66 * and where specified) out of frame translations (annotated appropriately) 3.
\r
67 * align peptides based on codon alignment
\r
70 * id potential products from dna 1. search for distinct products within
\r
71 * selected region for each selected sequence 2. group by associated DB type.
\r
72 * 3. return as form for input into above function
\r
78 * create a new alignment of protein sequences by an inframe translation of
\r
79 * the provided NA sequences
\r
84 * @param gapCharacter
\r
85 * @param annotations
\r
88 * destination dataset for translated sequences and mappings
\r
91 public static AlignmentI CdnaTranslate(SequenceI[] selection,
\r
92 String[] seqstring, int viscontigs[], char gapCharacter,
\r
93 AlignmentAnnotation[] annotations, int aWidth, Alignment dataset)
\r
95 return CdnaTranslate(selection, seqstring, null, viscontigs,
\r
96 gapCharacter, annotations, aWidth, dataset);
\r
104 * array of DbRefEntry objects from which exon map in seqstring
\r
106 * @param viscontigs
\r
107 * @param gapCharacter
\r
108 * @param annotations
\r
113 public static AlignmentI CdnaTranslate(SequenceI[] selection,
\r
114 String[] seqstring, DBRefEntry[] product, int viscontigs[],
\r
115 char gapCharacter, AlignmentAnnotation[] annotations, int aWidth,
\r
118 AlignedCodonFrame codons = new AlignedCodonFrame(aWidth); // stores hash of
\r
124 int s, sSize = selection.length;
\r
125 Vector pepseqs = new Vector();
\r
126 for (s = 0; s < sSize; s++)
\r
128 SequenceI newseq = translateCodingRegion(selection[s], seqstring[s],
\r
129 viscontigs, codons, gapCharacter,
\r
130 (product != null) ? product[s] : null); // possibly anonymous
\r
132 if (newseq != null)
\r
134 pepseqs.addElement(newseq);
\r
135 SequenceI ds = newseq;
\r
136 while (ds.getDatasetSequence() != null)
\r
138 ds = ds.getDatasetSequence();
\r
140 dataset.addSequence(ds);
\r
143 if (codons.aaWidth == 0)
\r
145 SequenceI[] newseqs = new SequenceI[pepseqs.size()];
\r
146 pepseqs.copyInto(newseqs);
\r
147 AlignmentI al = new Alignment(newseqs);
\r
148 al.padGaps(); // ensure we look aligned.
\r
149 al.setDataset(dataset);
\r
150 translateAlignedAnnotations(annotations, al, codons);
\r
151 al.addCodonFrame(codons);
\r
156 * fake the collection of DbRefs with associated exon mappings to identify if
\r
157 * a translation would generate distinct product in the currently selected
\r
161 * @param viscontigs
\r
164 public static boolean canTranslate(SequenceI[] selection,
\r
167 for (int gd = 0; gd < selection.length; gd++)
\r
169 SequenceI dna = selection[gd];
\r
170 jalview.datamodel.DBRefEntry[] dnarefs = jalview.util.DBRefUtils
\r
171 .selectRefs(dna.getDBRef(),
\r
172 jalview.datamodel.DBRefSource.DNACODINGDBS);
\r
173 if (dnarefs != null)
\r
175 // intersect with pep
\r
176 // intersect with pep
\r
177 Vector mappedrefs = new Vector();
\r
178 DBRefEntry[] refs = dna.getDBRef();
\r
179 for (int d = 0; d < refs.length; d++)
\r
181 if (refs[d].getMap() != null && refs[d].getMap().getMap() != null
\r
182 && refs[d].getMap().getMap().getFromRatio() == 3
\r
183 && refs[d].getMap().getMap().getToRatio() == 1)
\r
185 mappedrefs.addElement(refs[d]); // add translated protein maps
\r
188 dnarefs = new DBRefEntry[mappedrefs.size()];
\r
189 mappedrefs.copyInto(dnarefs);
\r
190 for (int d = 0; d < dnarefs.length; d++)
\r
192 Mapping mp = dnarefs[d].getMap();
\r
195 for (int vc = 0; vc < viscontigs.length; vc += 2)
\r
197 int[] mpr = mp.locateMappedRange(viscontigs[vc],
\r
198 viscontigs[vc + 1]);
\r
212 * generate a set of translated protein products from annotated sequenceI
\r
215 * @param viscontigs
\r
216 * @param gapCharacter
\r
218 * destination dataset for translated sequences
\r
219 * @param annotations
\r
223 public static AlignmentI CdnaTranslate(SequenceI[] selection,
\r
224 int viscontigs[], char gapCharacter, Alignment dataset)
\r
227 Vector cdnasqs = new Vector();
\r
228 Vector cdnasqi = new Vector();
\r
229 Vector cdnaprod = new Vector();
\r
230 for (int gd = 0; gd < selection.length; gd++)
\r
232 SequenceI dna = selection[gd];
\r
233 jalview.datamodel.DBRefEntry[] dnarefs = jalview.util.DBRefUtils
\r
234 .selectRefs(dna.getDBRef(),
\r
235 jalview.datamodel.DBRefSource.DNACODINGDBS);
\r
236 if (dnarefs != null)
\r
238 // intersect with pep
\r
239 Vector mappedrefs = new Vector();
\r
240 DBRefEntry[] refs = dna.getDBRef();
\r
241 for (int d = 0; d < refs.length; d++)
\r
243 if (refs[d].getMap() != null && refs[d].getMap().getMap() != null
\r
244 && refs[d].getMap().getMap().getFromRatio() == 3
\r
245 && refs[d].getMap().getMap().getToRatio() == 1)
\r
247 mappedrefs.addElement(refs[d]); // add translated protein maps
\r
250 dnarefs = new DBRefEntry[mappedrefs.size()];
\r
251 mappedrefs.copyInto(dnarefs);
\r
252 for (int d = 0; d < dnarefs.length; d++)
\r
254 Mapping mp = dnarefs[d].getMap();
\r
255 StringBuffer sqstr = new StringBuffer();
\r
258 Mapping intersect = mp.intersectVisContigs(viscontigs);
\r
259 // generate seqstring for this sequence based on mapping
\r
261 if (sqstr.length() > alwidth)
\r
262 alwidth = sqstr.length();
\r
263 cdnasqs.addElement(sqstr.toString());
\r
264 cdnasqi.addElement(dna);
\r
265 cdnaprod.addElement(intersect);
\r
269 SequenceI[] cdna = new SequenceI[cdnasqs.size()];
\r
270 DBRefEntry[] prods = new DBRefEntry[cdnaprod.size()];
\r
271 String[] xons = new String[cdnasqs.size()];
\r
272 cdnasqs.copyInto(xons);
\r
273 cdnaprod.copyInto(prods);
\r
274 cdnasqi.copyInto(cdna);
\r
275 return CdnaTranslate(cdna, xons, prods, viscontigs, gapCharacter,
\r
276 null, alwidth, dataset);
\r
282 * translate na alignment annotations onto translated amino acid alignment al
\r
283 * using codon mapping codons
\r
285 * @param annotations
\r
289 public static void translateAlignedAnnotations(
\r
290 AlignmentAnnotation[] annotations, AlignmentI al,
\r
291 AlignedCodonFrame codons)
\r
293 // //////////////////////////////
\r
294 // Copy annotations across
\r
296 // Can only do this for columns with consecutive codons, or where
\r
297 // annotation is sequence associated.
\r
300 if (annotations != null)
\r
302 for (int i = 0; i < annotations.length; i++)
\r
304 // Skip any autogenerated annotation
\r
305 if (annotations[i].autoCalculated)
\r
310 aSize = codons.getaaWidth(); // aa alignment width.
\r
311 jalview.datamodel.Annotation[] anots = (annotations[i].annotations == null) ? null
\r
312 : new jalview.datamodel.Annotation[aSize];
\r
315 for (a = 0; a < aSize; a++)
\r
317 // process through codon map.
\r
318 if (codons.codons[a] != null
\r
319 && codons.codons[a][0] == (codons.codons[a][2] - 2))
\r
321 anots[a] = getCodonAnnotation(codons.codons[a],
\r
322 annotations[i].annotations);
\r
327 jalview.datamodel.AlignmentAnnotation aa = new jalview.datamodel.AlignmentAnnotation(
\r
328 annotations[i].label, annotations[i].description, anots);
\r
329 aa.graph = annotations[i].graph;
\r
330 aa.graphGroup = annotations[i].graphGroup;
\r
331 aa.graphHeight = annotations[i].graphHeight;
\r
332 if (annotations[i].getThreshold() != null)
\r
334 aa.setThreshold(new jalview.datamodel.GraphLine(annotations[i]
\r
337 if (annotations[i].hasScore)
\r
339 aa.setScore(annotations[i].getScore());
\r
341 if (annotations[i].sequenceRef != null)
\r
343 SequenceI aaSeq = codons
\r
344 .getAaForDnaSeq(annotations[i].sequenceRef);
\r
347 // aa.compactAnnotationArray(); // throw away alignment annotation
\r
349 aa.setSequenceRef(aaSeq);
\r
350 aa.createSequenceMapping(aaSeq, aaSeq.getStart(), true); // rebuild
\r
352 aa.adjustForAlignment();
\r
353 aaSeq.addAlignmentAnnotation(aa);
\r
357 al.addAnnotation(aa);
\r
362 private static Annotation getCodonAnnotation(int[] is,
\r
363 Annotation[] annotations)
\r
365 // Have a look at all the codon positions for annotation and put the first
\r
366 // one found into the translated annotation pos.
\r
368 Annotation annot = null;
\r
369 for (int p = 0; p < 3; p++)
\r
371 if (annotations[is[p]] != null)
\r
374 annot = new Annotation(annotations[is[p]]);
\r
378 Annotation cpy = new Annotation(annotations[is[p]]);
\r
379 if (annot.colour==null)
\r
381 annot.colour = cpy.colour;
\r
383 if (annot.description==null || annot.description.length()==0)
\r
385 annot.description = cpy.description;
\r
387 if (annot.displayCharacter==null)
\r
389 annot.displayCharacter = cpy.displayCharacter;
\r
391 if (annot.secondaryStructure==0)
\r
393 annot.secondaryStructure = cpy.secondaryStructure;
\r
395 annot.value+=cpy.value;
\r
402 annot.value/=(float)contrib;
\r
408 * Translate a na sequence
\r
411 * sequence displayed under viscontigs visible columns
\r
413 * ORF read in some global alignment reference frame
\r
414 * @param viscontigs
\r
415 * mapping from global reference frame to visible seqstring ORF
\r
418 * Definition of global ORF alignment reference frame
\r
419 * @param gapCharacter
\r
421 * @return sequence ready to be added to alignment.
\r
423 public static SequenceI translateCodingRegion(SequenceI selection,
\r
424 String seqstring, int[] viscontigs, AlignedCodonFrame codons,
\r
425 char gapCharacter, DBRefEntry product)
\r
427 Vector skip = new Vector();
\r
428 int skipint[] = null;
\r
429 ShiftList vismapping = new ShiftList(); // map from viscontigs to seqstring
\r
431 int vc, scontigs[] = new int[viscontigs.length];
\r
433 for (vc = 0; vc < viscontigs.length; vc += 2)
\r
437 vismapping.addShift(npos, viscontigs[vc]);
\r
442 vismapping.addShift(npos, viscontigs[vc] - viscontigs[vc - 1] + 1);
\r
444 scontigs[vc] = viscontigs[vc];
\r
445 scontigs[vc + 1] = viscontigs[vc + 1];
\r
448 StringBuffer protein = new StringBuffer();
\r
449 String seq = seqstring.replace('U', 'T');
\r
450 char codon[] = new char[3];
\r
451 int cdp[] = new int[3], rf = 0, lastnpos = 0, nend;
\r
454 for (npos = 0, nend = seq.length(); npos < nend; npos++)
\r
456 if (!jalview.util.Comparison.isGap(seq.charAt(npos)))
\r
458 cdp[rf] = npos; // store position
\r
459 codon[rf++] = seq.charAt(npos); // store base
\r
461 // filled an RF yet ?
\r
464 String aa = ResidueProperties.codonTranslate(new String(codon));
\r
468 aa = String.valueOf(gapCharacter);
\r
469 if (skipint == null)
\r
471 skipint = new int[]
\r
472 { cdp[0], cdp[2] };
\r
474 skipint[1] = cdp[2];
\r
478 if (skipint != null)
\r
481 skipint[0] = vismapping.shift(skipint[0]);
\r
482 skipint[1] = vismapping.shift(skipint[1]);
\r
483 for (vc = 0; vc < scontigs.length; vc += 2)
\r
485 if (scontigs[vc + 1] < skipint[0])
\r
489 if (scontigs[vc] <= skipint[0])
\r
491 if (skipint[0] == scontigs[vc])
\r
497 int[] t = new int[scontigs.length + 2];
\r
498 System.arraycopy(scontigs, 0, t, 0, vc - 1);
\r
499 // scontigs[vc]; //
\r
503 skip.addElement(skipint);
\r
506 if (aa.equals("STOP"))
\r
512 // insert/delete gaps prior to this codon - if necessary
\r
513 boolean findpos = true;
\r
516 // first ensure that the codons array is long enough.
\r
517 codons.checkCodonFrameWidth(aspos);
\r
518 // now check to see if we place the aa at the current aspos in the
\r
519 // protein alignment
\r
520 switch (Dna.compare_codonpos(cdp, codons.codons[aspos]))
\r
523 codons.insertAAGap(aspos, gapCharacter);
\r
527 // this aa appears after the aligned codons at aspos, so prefix it
\r
529 aa = "" + gapCharacter + aa;
\r
531 // if (aspos >= codons.aaWidth)
\r
532 // codons.aaWidth = aspos + 1;
\r
533 break; // check the next position for alignment
\r
535 // codon aligns at aspos position.
\r
539 // codon aligns with all other sequence residues found at aspos
\r
540 protein.append(aa);
\r
542 if (codons.codons[aspos] == null)
\r
544 // mark this column as aligning to this aligned reading frame
\r
545 codons.codons[aspos] = new int[]
\r
546 { cdp[0], cdp[1], cdp[2] };
\r
548 if (aspos >= codons.aaWidth)
\r
550 // update maximum alignment width
\r
551 // (we can do this without calling checkCodonFrameWidth because it was already done above)
\r
552 codons.setAaWidth(aspos);
\r
554 // ready for next translated reading frame alignment position (if any)
\r
560 SequenceI newseq = new Sequence(selection.getName(), protein
\r
564 jalview.bin.Cache.log
\r
565 .debug("trimming contigs for incomplete terminal codon.");
\r
566 // map and trim contigs to ORF region
\r
567 vc = scontigs.length - 1;
\r
568 lastnpos = vismapping.shift(lastnpos); // place npos in context of
\r
569 // whole dna alignment (rather
\r
570 // than visible contigs)
\r
571 // incomplete ORF could be broken over one or two visible contig
\r
573 while (vc >= 0 && scontigs[vc] > lastnpos)
\r
575 if (vc > 0 && scontigs[vc - 1] > lastnpos)
\r
581 // correct last interval in list.
\r
582 scontigs[vc] = lastnpos;
\r
586 if (vc > 0 && (vc + 1) < scontigs.length)
\r
588 // truncate map list to just vc elements
\r
589 int t[] = new int[vc + 1];
\r
590 System.arraycopy(scontigs, 0, t, 0, vc + 1);
\r
596 if (scontigs != null)
\r
599 // map scontigs to actual sequence positions on selection
\r
600 for (vc = 0; vc < scontigs.length; vc += 2)
\r
602 scontigs[vc] = selection.findPosition(scontigs[vc]); // not from 1!
\r
603 scontigs[vc + 1] = selection.findPosition(scontigs[vc + 1]); // exclusive
\r
604 if (scontigs[vc + 1] == selection.getEnd())
\r
607 // trim trailing empty intervals.
\r
608 if ((vc + 2) < scontigs.length)
\r
610 int t[] = new int[vc + 2];
\r
611 System.arraycopy(scontigs, 0, t, 0, vc + 2);
\r
615 * delete intervals in scontigs which are not translated. 1. map skip
\r
616 * into sequence position intervals 2. truncate existing ranges and add
\r
617 * new ranges to exclude untranslated regions. if (skip.size()>0) {
\r
618 * Vector narange = new Vector(); for (vc=0; vc<scontigs.length; vc++) {
\r
619 * narange.addElement(new int[] {scontigs[vc]}); } int sint=0,iv[]; vc =
\r
620 * 0; while (sint<skip.size()) { skipint = (int[])
\r
621 * skip.elementAt(sint); do { iv = (int[]) narange.elementAt(vc); if
\r
622 * (iv[0]>=skipint[0] && iv[0]<=skipint[1]) { if (iv[0]==skipint[0]) { //
\r
623 * delete beginning of range } else { // truncate range and create new
\r
624 * one if necessary iv = (int[]) narange.elementAt(vc+1); if (iv[0]<=skipint[1]) { //
\r
625 * truncate range iv[0] = skipint[1]; } else {
\r
626 * } } } else if (iv[0]<skipint[0]) { iv = (int[])
\r
627 * narange.elementAt(vc+1); } } while (iv[0]) } }
\r
629 MapList map = new MapList(scontigs, new int[]
\r
630 { 1, resSize }, 3, 1);
\r
632 // update newseq as if it was generated as mapping from product
\r
634 if (product != null)
\r
636 newseq.setName(product.getSource() + "|"
\r
637 + product.getAccessionId());
\r
638 if (product.getMap() != null)
\r
640 // Mapping mp = product.getMap();
\r
641 // newseq.setStart(mp.getPosition(scontigs[0]));
\r
642 // newseq.setEnd(mp
\r
643 // .getPosition(scontigs[scontigs.length - 1]));
\r
646 transferCodedFeatures(selection, newseq, map, null, null);
\r
647 SequenceI rseq = newseq.deriveSequence(); // construct a dataset
\r
648 // sequence for our new
\r
649 // peptide, regardless.
\r
650 // store a mapping (this actually stores a mapping between the dataset
\r
651 // sequences for the two sequences
\r
652 codons.addMap(selection, rseq, map);
\r
656 // register the mapping somehow
\r
662 * Given a peptide newly translated from a dna sequence, copy over and set any
\r
663 * features on the peptide from the DNA. If featureTypes is null, all features
\r
664 * on the dna sequence are searched (rather than just the displayed ones), and
\r
665 * similarly for featureGroups.
\r
670 * @param featureTypes
\r
671 * hash who's keys are the displayed feature type strings
\r
672 * @param featureGroups
\r
673 * hash where keys are feature groups and values are Boolean
\r
674 * objects indicating if they are displayed.
\r
676 private static void transferCodedFeatures(SequenceI dna, SequenceI pep,
\r
677 MapList map, Hashtable featureTypes, Hashtable featureGroups)
\r
679 SequenceFeature[] sf = dna.getDatasetSequence().getSequenceFeatures();
\r
681 jalview.datamodel.DBRefEntry[] dnarefs = jalview.util.DBRefUtils
\r
682 .selectRefs(dna.getDBRef(),
\r
683 jalview.datamodel.DBRefSource.DNACODINGDBS);
\r
684 if (dnarefs != null)
\r
686 // intersect with pep
\r
687 for (int d = 0; d < dnarefs.length; d++)
\r
689 Mapping mp = dnarefs[d].getMap();
\r
697 for (int f = 0; f < sf.length; f++)
\r
699 fgstate = (featureGroups == null) ? null : ((Boolean) featureGroups
\r
700 .get(sf[f].featureGroup));
\r
701 if ((featureTypes == null || featureTypes.containsKey(sf[f]
\r
703 && (fgstate == null || fgstate.booleanValue()))
\r
705 if (FeatureProperties.isCodingFeature(null, sf[f].getType()))
\r
707 // if (map.intersectsFrom(sf[f].begin, sf[f].end))
\r