JAL-1619 'align protein as cDNA' now leaves unmapped protein unchanged
[jalview.git] / test / jalview / analysis / AlignmentUtilsTests.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.analysis;
22
23
24 import static org.testng.AssertJUnit.assertEquals;
25 import static org.testng.AssertJUnit.assertFalse;
26 import static org.testng.AssertJUnit.assertNull;
27 import static org.testng.AssertJUnit.assertSame;
28 import static org.testng.AssertJUnit.assertTrue;
29
30 import jalview.datamodel.AlignedCodonFrame;
31 import jalview.datamodel.Alignment;
32 import jalview.datamodel.AlignmentAnnotation;
33 import jalview.datamodel.AlignmentI;
34 import jalview.datamodel.Annotation;
35 import jalview.datamodel.DBRefEntry;
36 import jalview.datamodel.Mapping;
37 import jalview.datamodel.SearchResults;
38 import jalview.datamodel.SearchResults.Match;
39 import jalview.datamodel.Sequence;
40 import jalview.datamodel.SequenceI;
41 import jalview.io.AppletFormatAdapter;
42 import jalview.io.FormatAdapter;
43 import jalview.util.MapList;
44 import jalview.util.MappingUtils;
45
46 import java.io.IOException;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.Collections;
50 import java.util.HashSet;
51 import java.util.Iterator;
52 import java.util.LinkedHashSet;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.Set;
56
57 import org.testng.annotations.Test;
58
59 public class AlignmentUtilsTests
60 {
61   // @formatter:off
62   private static final String TEST_DATA = 
63           "# STOCKHOLM 1.0\n" +
64           "#=GS D.melanogaster.1 AC AY119185.1/838-902\n" +
65           "#=GS D.melanogaster.2 AC AC092237.1/57223-57161\n" +
66           "#=GS D.melanogaster.3 AC AY060611.1/560-627\n" +
67           "D.melanogaster.1          G.AGCC.CU...AUGAUCGA\n" +
68           "#=GR D.melanogaster.1 SS  ................((((\n" +
69           "D.melanogaster.2          C.AUUCAACU.UAUGAGGAU\n" +
70           "#=GR D.melanogaster.2 SS  ................((((\n" +
71           "D.melanogaster.3          G.UGGCGCU..UAUGACGCA\n" +
72           "#=GR D.melanogaster.3 SS  (.(((...(....(((((((\n" +
73           "//";
74
75   private static final String AA_SEQS_1 = 
76           ">Seq1Name\n" +
77           "K-QY--L\n" +
78           ">Seq2Name\n" +
79           "-R-FP-W-\n";
80
81   private static final String CDNA_SEQS_1 = 
82           ">Seq1Name\n" +
83           "AC-GG--CUC-CAA-CT\n" +
84           ">Seq2Name\n" +
85           "-CG-TTA--ACG---AAGT\n";
86
87   private static final String CDNA_SEQS_2 = 
88           ">Seq1Name\n" +
89           "GCTCGUCGTACT\n" +
90           ">Seq2Name\n" +
91           "GGGTCAGGCAGT\n";
92   // @formatter:on
93
94   // public static Sequence ts=new
95   // Sequence("short","ASDASDASDASDASDASDASDASDASDASDASDASDASD");
96   public static Sequence ts = new Sequence("short",
97           "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm");
98
99   @Test(groups ={ "Functional" })
100   public void testExpandContext()
101   {
102     AlignmentI al = new Alignment(new Sequence[] {});
103     for (int i = 4; i < 14; i += 2)
104     {
105       SequenceI s1=ts.deriveSequence().getSubSequence(i, i+7);
106       al.addSequence(s1);
107     }
108     System.out.println(new AppletFormatAdapter().formatSequences("Clustal", al, true));
109     for (int flnk=-1;flnk<25; flnk++)
110     {
111       AlignmentI exp = AlignmentUtils.expandContext(al, flnk);
112       System.out.println("\nFlank size: " + flnk);
113       System.out.println(new AppletFormatAdapter().formatSequences(
114               "Clustal", exp, true));
115       if (flnk == -1)
116       {
117         /*
118          * Full expansion to complete sequences
119          */
120         for (SequenceI sq : exp.getSequences())
121         {
122           String ung = sq.getSequenceAsString().replaceAll("-+", "");
123           final String errorMsg = "Flanking sequence not the same as original dataset sequence.\n"
124                   + ung
125                   + "\n"
126                   + sq.getDatasetSequence().getSequenceAsString();
127           assertTrue(errorMsg, ung.equalsIgnoreCase(sq.getDatasetSequence()
128                   .getSequenceAsString()));
129         }
130       }
131       else if (flnk == 24)
132       {
133         /*
134          * Last sequence is fully expanded, others have leading gaps to match
135          */
136         assertTrue(exp.getSequenceAt(4).getSequenceAsString()
137                 .startsWith("abc"));
138         assertTrue(exp.getSequenceAt(3).getSequenceAsString()
139                 .startsWith("--abc"));
140         assertTrue(exp.getSequenceAt(2).getSequenceAsString()
141                 .startsWith("----abc"));
142         assertTrue(exp.getSequenceAt(1).getSequenceAsString()
143                 .startsWith("------abc"));
144         assertTrue(exp.getSequenceAt(0).getSequenceAsString()
145                 .startsWith("--------abc"));
146       }
147     }
148   }
149
150   /**
151    * Test that annotations are correctly adjusted by expandContext
152    */
153   @Test(groups ={ "Functional" })
154   public void testExpandContext_annotation()
155   {
156     AlignmentI al = new Alignment(new Sequence[]
157     {});
158     SequenceI ds = new Sequence("Seq1", "ABCDEFGHI");
159     // subsequence DEF:
160     SequenceI seq1 = ds.deriveSequence().getSubSequence(3, 6);
161     al.addSequence(seq1);
162
163     /*
164      * Annotate DEF with 4/5/6 respectively
165      */
166     Annotation[] anns = new Annotation[]
167     { new Annotation(4), new Annotation(5), new Annotation(6) };
168     AlignmentAnnotation ann = new AlignmentAnnotation("SS",
169             "secondary structure", anns);
170     seq1.addAlignmentAnnotation(ann);
171
172     /*
173      * The annotations array should match aligned positions
174      */
175     assertEquals(3, ann.annotations.length);
176     assertEquals(4, ann.annotations[0].value, 0.001);
177     assertEquals(5, ann.annotations[1].value, 0.001);
178     assertEquals(6, ann.annotations[2].value, 0.001);
179
180     /*
181      * Check annotation to sequence position mappings before expanding the
182      * sequence; these are set up in Sequence.addAlignmentAnnotation ->
183      * Annotation.setSequenceRef -> createSequenceMappings
184      */
185     assertNull(ann.getAnnotationForPosition(1));
186     assertNull(ann.getAnnotationForPosition(2));
187     assertNull(ann.getAnnotationForPosition(3));
188     assertEquals(4, ann.getAnnotationForPosition(4).value, 0.001);
189     assertEquals(5, ann.getAnnotationForPosition(5).value, 0.001);
190     assertEquals(6, ann.getAnnotationForPosition(6).value, 0.001);
191     assertNull(ann.getAnnotationForPosition(7));
192     assertNull(ann.getAnnotationForPosition(8));
193     assertNull(ann.getAnnotationForPosition(9));
194
195     /*
196      * Expand the subsequence to the full sequence abcDEFghi
197      */
198     AlignmentI expanded = AlignmentUtils.expandContext(al, -1);
199     assertEquals("abcDEFghi", expanded.getSequenceAt(0)
200             .getSequenceAsString());
201
202     /*
203      * Confirm the alignment and sequence have the same SS annotation,
204      * referencing the expanded sequence
205      */
206     ann = expanded.getSequenceAt(0).getAnnotation()[0];
207     assertSame(ann, expanded.getAlignmentAnnotation()[0]);
208     assertSame(expanded.getSequenceAt(0), ann.sequenceRef);
209
210     /*
211      * The annotations array should have null values except for annotated
212      * positions
213      */
214     assertNull(ann.annotations[0]);
215     assertNull(ann.annotations[1]);
216     assertNull(ann.annotations[2]);
217     assertEquals(4, ann.annotations[3].value, 0.001);
218     assertEquals(5, ann.annotations[4].value, 0.001);
219     assertEquals(6, ann.annotations[5].value, 0.001);
220     assertNull(ann.annotations[6]);
221     assertNull(ann.annotations[7]);
222     assertNull(ann.annotations[8]);
223
224     /*
225      * sequence position mappings should be unchanged
226      */
227     assertNull(ann.getAnnotationForPosition(1));
228     assertNull(ann.getAnnotationForPosition(2));
229     assertNull(ann.getAnnotationForPosition(3));
230     assertEquals(4, ann.getAnnotationForPosition(4).value, 0.001);
231     assertEquals(5, ann.getAnnotationForPosition(5).value, 0.001);
232     assertEquals(6, ann.getAnnotationForPosition(6).value, 0.001);
233     assertNull(ann.getAnnotationForPosition(7));
234     assertNull(ann.getAnnotationForPosition(8));
235     assertNull(ann.getAnnotationForPosition(9));
236   }
237
238   /**
239    * Test method that returns a map of lists of sequences by sequence name.
240    * 
241    * @throws IOException
242    */
243   @Test(groups ={ "Functional" })
244   public void testGetSequencesByName() throws IOException
245   {
246     final String data = ">Seq1Name\nKQYL\n" + ">Seq2Name\nRFPW\n"
247             + ">Seq1Name\nABCD\n";
248     AlignmentI al = loadAlignment(data, "FASTA");
249     Map<String, List<SequenceI>> map = AlignmentUtils
250             .getSequencesByName(al);
251     assertEquals(2, map.keySet().size());
252     assertEquals(2, map.get("Seq1Name").size());
253     assertEquals("KQYL", map.get("Seq1Name").get(0).getSequenceAsString());
254     assertEquals("ABCD", map.get("Seq1Name").get(1).getSequenceAsString());
255     assertEquals(1, map.get("Seq2Name").size());
256     assertEquals("RFPW", map.get("Seq2Name").get(0).getSequenceAsString());
257   }
258   /**
259    * Helper method to load an alignment and ensure dataset sequences are set up.
260    * 
261    * @param data
262    * @param format TODO
263    * @return
264    * @throws IOException
265    */
266   protected AlignmentI loadAlignment(final String data, String format) throws IOException
267   {
268     AlignmentI a = new FormatAdapter().readFile(data,
269             AppletFormatAdapter.PASTE, format);
270     a.setDataset(null);
271     return a;
272   }
273   
274   /**
275    * Test mapping of protein to cDNA, for the case where we have no sequence
276    * cross-references, so mappings are made first-served 1-1 where sequences
277    * translate.
278    * 
279    * @throws IOException
280    */
281   @Test(groups ={ "Functional" })
282   public void testMapProteinToCdna_noXrefs() throws IOException
283   {
284     List<SequenceI> protseqs = new ArrayList<SequenceI>();
285     protseqs.add(new Sequence("UNIPROT|V12345", "EIQ"));
286     protseqs.add(new Sequence("UNIPROT|V12346", "EIQ"));
287     protseqs.add(new Sequence("UNIPROT|V12347", "SAR"));
288     AlignmentI protein = new Alignment(protseqs.toArray(new SequenceI[3]));
289     protein.setDataset(null);
290
291     List<SequenceI> dnaseqs = new ArrayList<SequenceI>();
292     dnaseqs.add(new Sequence("EMBL|A11111", "TCAGCACGC")); // = SAR
293     dnaseqs.add(new Sequence("EMBL|A22222", "GAGATACAA")); // = EIQ
294     dnaseqs.add(new Sequence("EMBL|A33333", "GAAATCCAG")); // = EIQ
295     dnaseqs.add(new Sequence("EMBL|A44444", "GAAATTCAG")); // = EIQ
296     AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[4]));
297     cdna.setDataset(null);
298
299     assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna));
300
301     // 3 mappings made, each from 1 to 1 sequence
302     assertEquals(3, protein.getCodonFrames().size());
303     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size());
304     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size());
305     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(2)).size());
306
307     // V12345 mapped to A22222
308     AlignedCodonFrame acf = protein.getCodonFrame(
309             protein.getSequenceAt(0)).get(0);
310     assertEquals(1, acf.getdnaSeqs().length);
311     assertEquals(cdna.getSequenceAt(1).getDatasetSequence(),
312             acf.getdnaSeqs()[0]);
313     Mapping[] protMappings = acf.getProtMappings();
314     assertEquals(1, protMappings.length);
315     MapList mapList = protMappings[0].getMap();
316     assertEquals(3, mapList.getFromRatio());
317     assertEquals(1, mapList.getToRatio());
318     assertTrue(Arrays.equals(new int[]
319     { 1, 9 }, mapList.getFromRanges().get(0)));
320     assertEquals(1, mapList.getFromRanges().size());
321     assertTrue(Arrays.equals(new int[]
322     { 1, 3 }, mapList.getToRanges().get(0)));
323     assertEquals(1, mapList.getToRanges().size());
324
325     // V12346 mapped to A33333
326     acf = protein.getCodonFrame(protein.getSequenceAt(1)).get(0);
327     assertEquals(1, acf.getdnaSeqs().length);
328     assertEquals(cdna.getSequenceAt(2).getDatasetSequence(),
329             acf.getdnaSeqs()[0]);
330
331     // V12347 mapped to A11111
332     acf = protein.getCodonFrame(protein.getSequenceAt(2)).get(0);
333     assertEquals(1, acf.getdnaSeqs().length);
334     assertEquals(cdna.getSequenceAt(0).getDatasetSequence(),
335             acf.getdnaSeqs()[0]);
336
337     // no mapping involving the 'extra' A44444
338     assertTrue(protein.getCodonFrame(cdna.getSequenceAt(3)).isEmpty());
339   }
340
341   /**
342    * Test for the alignSequenceAs method that takes two sequences and a mapping.
343    */
344   @Test(groups ={ "Functional" })
345   public void testAlignSequenceAs_withMapping_noIntrons()
346   {
347     MapList map = new MapList(new int[]
348     { 1, 6 }, new int[]
349     { 1, 2 }, 3, 1);
350
351     /*
352      * No existing gaps in dna:
353      */
354     checkAlignSequenceAs("GGGAAA", "-A-L-", false, false, map,
355             "---GGG---AAA");
356
357     /*
358      * Now introduce gaps in dna but ignore them when realigning.
359      */
360     checkAlignSequenceAs("-G-G-G-A-A-A-", "-A-L-", false, false, map,
361             "---GGG---AAA");
362
363     /*
364      * Now include gaps in dna when realigning. First retaining 'mapped' gaps
365      * only, i.e. those within the exon region.
366      */
367     checkAlignSequenceAs("-G-G--G-A--A-A-", "-A-L-", true, false, map,
368             "---G-G--G---A--A-A");
369
370     /*
371      * Include all gaps in dna when realigning (within and without the exon
372      * region). The leading gap, and the gaps between codons, are subsumed by
373      * the protein alignment gap.
374      */
375     checkAlignSequenceAs("-G-GG--AA-A-", "-A-L-", true, true, map,
376             "---G-GG---AA-A-");
377
378     /*
379      * Include only unmapped gaps in dna when realigning (outside the exon
380      * region). The leading gap, and the gaps between codons, are subsumed by
381      * the protein alignment gap.
382      */
383     checkAlignSequenceAs("-G-GG--AA-A-", "-A-L-", false, true, map,
384             "---GGG---AAA-");
385   }
386
387   /**
388    * Test for the alignSequenceAs method that takes two sequences and a mapping.
389    */
390   @Test(groups ={ "Functional" })
391   public void testAlignSequenceAs_withMapping_withIntrons()
392   {
393     /*
394      * Exons at codon 2 (AAA) and 4 (TTT)
395      */
396     MapList map = new MapList(new int[]
397     { 4, 6, 10, 12 }, new int[]
398     { 1, 2 }, 3, 1);
399
400     /*
401      * Simple case: no gaps in dna
402      */
403     checkAlignSequenceAs("GGGAAACCCTTTGGG", "--A-L-", false, false, map,
404             "GGG---AAACCCTTTGGG");
405
406     /*
407      * Add gaps to dna - but ignore when realigning.
408      */
409     checkAlignSequenceAs("-G-G-G--A--A---AC-CC-T-TT-GG-G-", "--A-L-",
410             false, false, map, "GGG---AAACCCTTTGGG");
411
412     /*
413      * Add gaps to dna - include within exons only when realigning.
414      */
415     checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
416             true, false, map, "GGG---A--A---ACCCT-TTGGG");
417
418     /*
419      * Include gaps outside exons only when realigning.
420      */
421     checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
422             false, true, map, "-G-G-GAAAC-CCTTT-GG-G-");
423
424     /*
425      * Include gaps following first intron if we are 'preserving mapped gaps'
426      */
427     checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
428             true, true, map, "-G-G-G--A--A---A-C-CC-T-TT-GG-G-");
429
430     /*
431      * Include all gaps in dna when realigning.
432      */
433     checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
434             true, true, map, "-G-G-G--A--A---A-C-CC-T-TT-GG-G-");
435   }
436
437   /**
438    * Test for the case where not all of the protein sequence is mapped to cDNA.
439    */
440   @Test(groups ={ "Functional" })
441   public void testAlignSequenceAs_withMapping_withUnmappedProtein()
442   {
443     
444     /*
445      * Exons at codon 2 (AAA) and 4 (TTT) mapped to A and P
446      */
447     final MapList map = new MapList(new int[]
448     { 4, 6, 10, 12 }, new int[]
449     { 1, 1, 3, 3 }, 3, 1);
450     
451
452     /*
453      * Expect alignment does nothing (aborts realignment). Change this test
454      * first if different behaviour wanted.
455      */
456     checkAlignSequenceAs("GGGAAACCCTTTGGG", "-A-L-P-", false,
457             false, map, "GGGAAACCCTTTGGG");
458   }
459
460   /**
461    * Helper method that performs and verifies the method under test.
462    * 
463    * @param dnaSeq
464    * @param proteinSeq
465    * @param preserveMappedGaps
466    * @param preserveUnmappedGaps
467    * @param map
468    * @param expected
469    */
470   protected void checkAlignSequenceAs(final String dnaSeq,
471           final String proteinSeq, final boolean preserveMappedGaps,
472           final boolean preserveUnmappedGaps, MapList map,
473           final String expected)
474   {
475     SequenceI dna = new Sequence("Seq1", dnaSeq);
476     dna.createDatasetSequence();
477     SequenceI protein = new Sequence("Seq1", proteinSeq);
478     protein.createDatasetSequence();
479     AlignedCodonFrame acf = new AlignedCodonFrame();
480     acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map);
481
482     AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-',
483             preserveMappedGaps, preserveUnmappedGaps);
484     assertEquals(expected, dna.getSequenceAsString());
485   }
486
487   /**
488    * Test for the alignSequenceAs method where we preserve gaps in introns only.
489    */
490   @Test(groups ={ "Functional" })
491   public void testAlignSequenceAs_keepIntronGapsOnly()
492   {
493
494     /*
495      * Intron GGGAAA followed by exon CCCTTT
496      */
497     MapList map = new MapList(new int[]
498     { 7, 12 }, new int[]
499     { 1, 2 }, 3, 1);
500     
501     checkAlignSequenceAs("GG-G-AA-A-C-CC-T-TT", "AL",
502             false, true, map, "GG-G-AA-ACCCTTT");
503   }
504
505   /**
506    * Test for the method that generates an aligned translated sequence from one
507    * mapping.
508    */
509   @Test(groups ={ "Functional" })
510   public void testGetAlignedTranslation_dnaLikeProtein()
511   {
512     // dna alignment will be replaced
513     SequenceI dna = new Sequence("Seq1", "T-G-CC-A--T-TAC-CAG-");
514     dna.createDatasetSequence();
515     // protein alignment will be 'applied' to dna
516     SequenceI protein = new Sequence("Seq1", "-CH-Y--Q-");
517     protein.createDatasetSequence();
518     MapList map = new MapList(new int[]
519     { 1, 12 }, new int[]
520     { 1, 4 }, 3, 1);
521     AlignedCodonFrame acf = new AlignedCodonFrame();
522     acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map);
523
524     final SequenceI aligned = AlignmentUtils
525                 .getAlignedTranslation(protein, '-', acf);
526     assertEquals("---TGCCAT---TAC------CAG---", aligned.getSequenceAsString());
527     assertSame(aligned.getDatasetSequence(), dna.getDatasetSequence());
528   }
529
530   /**
531    * Test the method that realigns protein to match mapped codon alignment.
532    */
533   @Test(groups ={ "Functional" })
534   public void testAlignProteinAsDna()
535   {
536     // seq1 codons are [1,2,3] [4,5,6] [7,8,9] [10,11,12]
537     SequenceI dna1 = new Sequence("Seq1", "TGCCATTACCAG-");
538     // seq2 codons are [1,3,4] [5,6,7] [8,9,10] [11,12,13]
539     SequenceI dna2 = new Sequence("Seq2", "T-GCCATTACCAG");
540     // seq3 codons are [1,2,3] [4,5,7] [8,9,10] [11,12,13]
541     SequenceI dna3 = new Sequence("Seq3", "TGCCA-TTACCAG");
542     AlignmentI dna = new Alignment(new SequenceI[]
543     { dna1, dna2, dna3 });
544     dna.setDataset(null);
545
546     // protein alignment will be realigned like dna
547     SequenceI prot1 = new Sequence("Seq1", "CHYQ");
548     SequenceI prot2 = new Sequence("Seq2", "CHYQ");
549     SequenceI prot3 = new Sequence("Seq3", "CHYQ");
550     SequenceI prot4 = new Sequence("Seq4", "R-QSV"); // unmapped, unchanged
551     AlignmentI protein = new Alignment(new SequenceI[]
552  { prot1, prot2,
553         prot3, prot4 });
554     protein.setDataset(null);
555
556     MapList map = new MapList(new int[]
557     { 1, 12 }, new int[]
558     { 1, 4 }, 3, 1);
559     AlignedCodonFrame acf = new AlignedCodonFrame();
560     acf.addMap(dna1.getDatasetSequence(), prot1.getDatasetSequence(), map);
561     acf.addMap(dna2.getDatasetSequence(), prot2.getDatasetSequence(), map);
562     acf.addMap(dna3.getDatasetSequence(), prot3.getDatasetSequence(), map);
563     protein.setCodonFrames(Collections.singleton(acf));
564
565     /*
566      * Translated codon order is [1,2,3] [1,3,4] [4,5,6] [4,5,7] [5,6,7] [7,8,9]
567      * [8,9,10] [10,11,12] [11,12,13]
568      */
569     AlignmentUtils.alignProteinAsDna(protein, dna);
570     assertEquals("C-H--Y-Q-", prot1.getSequenceAsString());
571     assertEquals("-C--H-Y-Q", prot2.getSequenceAsString());
572     assertEquals("C--H--Y-Q", prot3.getSequenceAsString());
573     assertEquals("R-QSV", prot4.getSequenceAsString());
574   }
575
576   /**
577    * Test the method that tests whether a CDNA sequence translates to a protein
578    * sequence
579    */
580   @Test(groups ={ "Functional" })
581   public void testTranslatesAs()
582   {
583     assertTrue(AlignmentUtils.translatesAs("tttcccaaaggg".toCharArray(), 0,
584             "FPKG".toCharArray()));
585     // with start codon (not in protein)
586     assertTrue(AlignmentUtils.translatesAs("atgtttcccaaaggg".toCharArray(),
587             3, "FPKG".toCharArray()));
588     // with stop codon1 (not in protein)
589     assertTrue(AlignmentUtils.translatesAs("tttcccaaagggtaa".toCharArray(),
590             0, "FPKG".toCharArray()));
591     // with stop codon1 (in protein as *)
592     assertTrue(AlignmentUtils.translatesAs("tttcccaaagggtaa".toCharArray(),
593             0, "FPKG*".toCharArray()));
594     // with stop codon2 (not in protein)
595     assertTrue(AlignmentUtils.translatesAs("tttcccaaagggtag".toCharArray(),
596             0, "FPKG".toCharArray()));
597     // with stop codon3 (not in protein)
598     assertTrue(AlignmentUtils.translatesAs("tttcccaaagggtga".toCharArray(),
599             0, "FPKG".toCharArray()));
600     // with start and stop codon1
601     assertTrue(AlignmentUtils.translatesAs(
602             "atgtttcccaaagggtaa".toCharArray(), 3, "FPKG".toCharArray()));
603     // with start and stop codon1 (in protein as *)
604     assertTrue(AlignmentUtils.translatesAs(
605             "atgtttcccaaagggtaa".toCharArray(), 3, "FPKG*".toCharArray()));
606     // with start and stop codon2
607     assertTrue(AlignmentUtils.translatesAs(
608             "atgtttcccaaagggtag".toCharArray(), 3, "FPKG".toCharArray()));
609     // with start and stop codon3
610     assertTrue(AlignmentUtils.translatesAs(
611             "atgtttcccaaagggtga".toCharArray(), 3, "FPKG".toCharArray()));
612
613     // with embedded stop codon
614     assertTrue(AlignmentUtils.translatesAs(
615             "atgtttTAGcccaaaTAAgggtga".toCharArray(), 3,
616             "F*PK*G".toCharArray()));
617
618     // wrong protein
619     assertFalse(AlignmentUtils.translatesAs("tttcccaaaggg".toCharArray(),
620             0,
621             "FPMG".toCharArray()));
622   }
623
624   /**
625    * Test mapping of protein to cDNA, for cases where the cDNA has start and/or
626    * stop codons in addition to the protein coding sequence.
627    * 
628    * @throws IOException
629    */
630   @Test(groups ={ "Functional" })
631   public void testMapProteinToCdna_withStartAndStopCodons()
632           throws IOException
633   {
634     List<SequenceI> protseqs = new ArrayList<SequenceI>();
635     protseqs.add(new Sequence("UNIPROT|V12345", "EIQ"));
636     protseqs.add(new Sequence("UNIPROT|V12346", "EIQ"));
637     protseqs.add(new Sequence("UNIPROT|V12347", "SAR"));
638     AlignmentI protein = new Alignment(protseqs.toArray(new SequenceI[3]));
639     protein.setDataset(null);
640   
641     List<SequenceI> dnaseqs = new ArrayList<SequenceI>();
642     // start + SAR:
643     dnaseqs.add(new Sequence("EMBL|A11111", "ATGTCAGCACGC"));
644     // = EIQ + stop
645     dnaseqs.add(new Sequence("EMBL|A22222", "GAGATACAATAA"));
646     // = start +EIQ + stop
647     dnaseqs.add(new Sequence("EMBL|A33333", "ATGGAAATCCAGTAG"));
648     dnaseqs.add(new Sequence("EMBL|A44444", "GAAATTCAG"));
649     AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[4]));
650     cdna.setDataset(null);
651   
652     assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna));
653
654     // 3 mappings made, each from 1 to 1 sequence
655     assertEquals(3, protein.getCodonFrames().size());
656     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size());
657     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size());
658     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(2)).size());
659   
660     // V12345 mapped from A22222
661     AlignedCodonFrame acf = protein.getCodonFrame(
662             protein.getSequenceAt(0)).get(0);
663     assertEquals(1, acf.getdnaSeqs().length);
664     assertEquals(cdna.getSequenceAt(1).getDatasetSequence(),
665             acf.getdnaSeqs()[0]);
666     Mapping[] protMappings = acf.getProtMappings();
667     assertEquals(1, protMappings.length);
668     MapList mapList = protMappings[0].getMap();
669     assertEquals(3, mapList.getFromRatio());
670     assertEquals(1, mapList.getToRatio());
671     assertTrue(Arrays.equals(new int[]
672     { 1, 9 }, mapList.getFromRanges().get(0)));
673     assertEquals(1, mapList.getFromRanges().size());
674     assertTrue(Arrays.equals(new int[]
675     { 1, 3 }, mapList.getToRanges().get(0)));
676     assertEquals(1, mapList.getToRanges().size());
677
678     // V12346 mapped from A33333 starting position 4
679     acf = protein.getCodonFrame(protein.getSequenceAt(1)).get(0);
680     assertEquals(1, acf.getdnaSeqs().length);
681     assertEquals(cdna.getSequenceAt(2).getDatasetSequence(),
682             acf.getdnaSeqs()[0]);
683     protMappings = acf.getProtMappings();
684     assertEquals(1, protMappings.length);
685     mapList = protMappings[0].getMap();
686     assertEquals(3, mapList.getFromRatio());
687     assertEquals(1, mapList.getToRatio());
688     assertTrue(Arrays.equals(new int[]
689     { 4, 12 }, mapList.getFromRanges().get(0)));
690     assertEquals(1, mapList.getFromRanges().size());
691     assertTrue(Arrays.equals(new int[]
692     { 1, 3 }, mapList.getToRanges().get(0)));
693     assertEquals(1, mapList.getToRanges().size());
694   
695     // V12347 mapped to A11111 starting position 4
696     acf = protein.getCodonFrame(protein.getSequenceAt(2)).get(0);
697     assertEquals(1, acf.getdnaSeqs().length);
698     assertEquals(cdna.getSequenceAt(0).getDatasetSequence(),
699             acf.getdnaSeqs()[0]);
700     protMappings = acf.getProtMappings();
701     assertEquals(1, protMappings.length);
702     mapList = protMappings[0].getMap();
703     assertEquals(3, mapList.getFromRatio());
704     assertEquals(1, mapList.getToRatio());
705     assertTrue(Arrays.equals(new int[]
706     { 4, 12 }, mapList.getFromRanges().get(0)));
707     assertEquals(1, mapList.getFromRanges().size());
708     assertTrue(Arrays.equals(new int[]
709     { 1, 3 }, mapList.getToRanges().get(0)));
710     assertEquals(1, mapList.getToRanges().size());
711   
712     // no mapping involving the 'extra' A44444
713     assertTrue(protein.getCodonFrame(cdna.getSequenceAt(3)).isEmpty());
714   }
715
716   /**
717    * Test mapping of protein to cDNA, for the case where we have some sequence
718    * cross-references. Verify that 1-to-many mappings are made where
719    * cross-references exist and sequences are mappable.
720    * 
721    * @throws IOException
722    */
723   @Test(groups ={ "Functional" })
724   public void testMapProteinToCdna_withXrefs() throws IOException
725   {
726     List<SequenceI> protseqs = new ArrayList<SequenceI>();
727     protseqs.add(new Sequence("UNIPROT|V12345", "EIQ"));
728     protseqs.add(new Sequence("UNIPROT|V12346", "EIQ"));
729     protseqs.add(new Sequence("UNIPROT|V12347", "SAR"));
730     AlignmentI protein = new Alignment(protseqs.toArray(new SequenceI[3]));
731     protein.setDataset(null);
732   
733     List<SequenceI> dnaseqs = new ArrayList<SequenceI>();
734     dnaseqs.add(new Sequence("EMBL|A11111", "TCAGCACGC")); // = SAR
735     dnaseqs.add(new Sequence("EMBL|A22222", "ATGGAGATACAA")); // = start + EIQ
736     dnaseqs.add(new Sequence("EMBL|A33333", "GAAATCCAG")); // = EIQ
737     dnaseqs.add(new Sequence("EMBL|A44444", "GAAATTCAG")); // = EIQ
738     dnaseqs.add(new Sequence("EMBL|A55555", "GAGATTCAG")); // = EIQ
739     AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[5]));
740     cdna.setDataset(null);
741   
742     // Xref A22222 to V12345 (should get mapped)
743     dnaseqs.get(1).addDBRef(new DBRefEntry("UNIPROT", "1", "V12345"));
744     // Xref V12345 to A44444 (should get mapped)
745     protseqs.get(0).addDBRef(new DBRefEntry("EMBL", "1", "A44444"));
746     // Xref A33333 to V12347 (sequence mismatch - should not get mapped)
747     dnaseqs.get(2).addDBRef(new DBRefEntry("UNIPROT", "1", "V12347"));
748     // as V12345 is mapped to A22222 and A44444, this leaves V12346 unmapped.
749     // it should get paired up with the unmapped A33333
750     // A11111 should be mapped to V12347
751     // A55555 is spare and has no xref so is not mapped
752
753     assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna));
754
755     // 4 protein mappings made for 3 proteins, 2 to V12345, 1 each to V12346/7
756     assertEquals(3, protein.getCodonFrames().size());
757     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size());
758     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size());
759     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(2)).size());
760
761     // one mapping for each of the first 4 cDNA sequences
762     assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(0)).size());
763     assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(1)).size());
764     assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(2)).size());
765     assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(3)).size());
766   
767     // V12345 mapped to A22222 and A44444
768     AlignedCodonFrame acf = protein.getCodonFrame(
769             protein.getSequenceAt(0)).get(0);
770     assertEquals(2, acf.getdnaSeqs().length);
771     assertEquals(cdna.getSequenceAt(1).getDatasetSequence(),
772             acf.getdnaSeqs()[0]);
773     assertEquals(cdna.getSequenceAt(3).getDatasetSequence(),
774             acf.getdnaSeqs()[1]);
775   
776     // V12346 mapped to A33333
777     acf = protein.getCodonFrame(protein.getSequenceAt(1)).get(0);
778     assertEquals(1, acf.getdnaSeqs().length);
779     assertEquals(cdna.getSequenceAt(2).getDatasetSequence(),
780             acf.getdnaSeqs()[0]);
781   
782     // V12347 mapped to A11111
783     acf = protein.getCodonFrame(protein.getSequenceAt(2)).get(0);
784     assertEquals(1, acf.getdnaSeqs().length);
785     assertEquals(cdna.getSequenceAt(0).getDatasetSequence(),
786             acf.getdnaSeqs()[0]);
787   
788     // no mapping involving the 'extra' A55555
789     assertTrue(protein.getCodonFrame(cdna.getSequenceAt(4)).isEmpty());
790   }
791
792   /**
793    * Test mapping of protein to cDNA, for the case where we have some sequence
794    * cross-references. Verify that once we have made an xref mapping we don't
795    * also map un-xrefd sequeces.
796    * 
797    * @throws IOException
798    */
799   @Test(groups ={ "Functional" })
800   public void testMapProteinToCdna_prioritiseXrefs() throws IOException
801   {
802     List<SequenceI> protseqs = new ArrayList<SequenceI>();
803     protseqs.add(new Sequence("UNIPROT|V12345", "EIQ"));
804     protseqs.add(new Sequence("UNIPROT|V12346", "EIQ"));
805     AlignmentI protein = new Alignment(
806             protseqs.toArray(new SequenceI[protseqs.size()]));
807     protein.setDataset(null);
808   
809     List<SequenceI> dnaseqs = new ArrayList<SequenceI>();
810     dnaseqs.add(new Sequence("EMBL|A11111", "GAAATCCAG")); // = EIQ
811     dnaseqs.add(new Sequence("EMBL|A22222", "GAAATTCAG")); // = EIQ
812     AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[dnaseqs
813             .size()]));
814     cdna.setDataset(null);
815   
816     // Xref A22222 to V12345 (should get mapped)
817     // A11111 should then be mapped to the unmapped V12346
818     dnaseqs.get(1).addDBRef(new DBRefEntry("UNIPROT", "1", "V12345"));
819   
820     assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna));
821   
822     // 2 protein mappings made
823     assertEquals(2, protein.getCodonFrames().size());
824     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size());
825     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size());
826   
827     // one mapping for each of the cDNA sequences
828     assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(0)).size());
829     assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(1)).size());
830   
831     // V12345 mapped to A22222
832     AlignedCodonFrame acf = protein.getCodonFrame(protein.getSequenceAt(0))
833             .get(0);
834     assertEquals(1, acf.getdnaSeqs().length);
835     assertEquals(cdna.getSequenceAt(1).getDatasetSequence(),
836             acf.getdnaSeqs()[0]);
837   
838     // V12346 mapped to A11111
839     acf = protein.getCodonFrame(protein.getSequenceAt(1)).get(0);
840     assertEquals(1, acf.getdnaSeqs().length);
841     assertEquals(cdna.getSequenceAt(0).getDatasetSequence(),
842             acf.getdnaSeqs()[0]);
843   }
844
845   /**
846    * Test the method that shows or hides sequence annotations by type(s) and
847    * selection group.
848    */
849   @Test(groups ={ "Functional" })
850   public void testShowOrHideSequenceAnnotations()
851   {
852     SequenceI seq1 = new Sequence("Seq1", "AAA");
853     SequenceI seq2 = new Sequence("Seq2", "BBB");
854     SequenceI seq3 = new Sequence("Seq3", "CCC");
855     Annotation[] anns = new Annotation[]
856     { new Annotation(2f) };
857     AlignmentAnnotation ann1 = new AlignmentAnnotation("Structure", "ann1",
858             anns);
859     ann1.setSequenceRef(seq1);
860     AlignmentAnnotation ann2 = new AlignmentAnnotation("Structure", "ann2",
861             anns);
862     ann2.setSequenceRef(seq2);
863     AlignmentAnnotation ann3 = new AlignmentAnnotation("Structure", "ann3",
864             anns);
865     AlignmentAnnotation ann4 = new AlignmentAnnotation("Temp", "ann4", anns);
866     ann4.setSequenceRef(seq1);
867     AlignmentAnnotation ann5 = new AlignmentAnnotation("Temp", "ann5", anns);
868     ann5.setSequenceRef(seq2);
869     AlignmentAnnotation ann6 = new AlignmentAnnotation("Temp", "ann6", anns);
870     AlignmentI al = new Alignment(new SequenceI[] {seq1, seq2, seq3});
871     al.addAnnotation(ann1); // Structure for Seq1
872     al.addAnnotation(ann2); // Structure for Seq2
873     al.addAnnotation(ann3); // Structure for no sequence
874     al.addAnnotation(ann4); // Temp for seq1
875     al.addAnnotation(ann5); // Temp for seq2
876     al.addAnnotation(ann6); // Temp for no sequence
877     List<String> types = new ArrayList<String>();
878     List<SequenceI> scope = new ArrayList<SequenceI>();
879
880     /*
881      * Set all sequence related Structure to hidden (ann1, ann2)
882      */
883     types.add("Structure");
884     AlignmentUtils.showOrHideSequenceAnnotations(al, types, null, false,
885             false);
886     assertFalse(ann1.visible);
887     assertFalse(ann2.visible);
888     assertTrue(ann3.visible); // not sequence-related, not affected
889     assertTrue(ann4.visible); // not Structure, not affected
890     assertTrue(ann5.visible); // "
891     assertTrue(ann6.visible); // not sequence-related, not affected
892
893     /*
894      * Set Temp in {seq1, seq3} to hidden
895      */
896     types.clear();
897     types.add("Temp");
898     scope.add(seq1);
899     scope.add(seq3);
900     AlignmentUtils.showOrHideSequenceAnnotations(al, types, scope, false,
901             false);
902     assertFalse(ann1.visible); // unchanged
903     assertFalse(ann2.visible); // unchanged
904     assertTrue(ann3.visible); // not sequence-related, not affected
905     assertFalse(ann4.visible); // Temp for seq1 hidden
906     assertTrue(ann5.visible); // not in scope, not affected
907     assertTrue(ann6.visible); // not sequence-related, not affected
908
909     /*
910      * Set Temp in all sequences to hidden
911      */
912     types.clear();
913     types.add("Temp");
914     scope.add(seq1);
915     scope.add(seq3);
916     AlignmentUtils.showOrHideSequenceAnnotations(al, types, null, false,
917             false);
918     assertFalse(ann1.visible); // unchanged
919     assertFalse(ann2.visible); // unchanged
920     assertTrue(ann3.visible); // not sequence-related, not affected
921     assertFalse(ann4.visible); // Temp for seq1 hidden
922     assertFalse(ann5.visible); // Temp for seq2 hidden
923     assertTrue(ann6.visible); // not sequence-related, not affected
924
925     /*
926      * Set all types in {seq1, seq3} to visible
927      */
928     types.clear();
929     scope.clear();
930     scope.add(seq1);
931     scope.add(seq3);
932     AlignmentUtils.showOrHideSequenceAnnotations(al, types, scope, true,
933             true);
934     assertTrue(ann1.visible); // Structure for seq1 set visible
935     assertFalse(ann2.visible); // not in scope, unchanged
936     assertTrue(ann3.visible); // not sequence-related, not affected
937     assertTrue(ann4.visible); // Temp for seq1 set visible
938     assertFalse(ann5.visible); // not in scope, unchanged
939     assertTrue(ann6.visible); // not sequence-related, not affected
940
941     /*
942      * Set all types in all scope to hidden
943      */
944     AlignmentUtils.showOrHideSequenceAnnotations(al, types, null, true,
945             false);
946     assertFalse(ann1.visible);
947     assertFalse(ann2.visible);
948     assertTrue(ann3.visible); // not sequence-related, not affected
949     assertFalse(ann4.visible);
950     assertFalse(ann5.visible);
951     assertTrue(ann6.visible); // not sequence-related, not affected
952   }
953
954   /**
955    * Tests for the method that checks if one sequence cross-references another
956    */
957   @Test(groups ={ "Functional" })
958   public void testHasCrossRef()
959   {
960     assertFalse(AlignmentUtils.hasCrossRef(null, null));
961     SequenceI seq1 = new Sequence("EMBL|A12345", "ABCDEF");
962     assertFalse(AlignmentUtils.hasCrossRef(seq1, null));
963     assertFalse(AlignmentUtils.hasCrossRef(null, seq1));
964     SequenceI seq2 = new Sequence("UNIPROT|V20192", "ABCDEF");
965     assertFalse(AlignmentUtils.hasCrossRef(seq1, seq2));
966   
967     // different ref
968     seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "v20193"));
969     assertFalse(AlignmentUtils.hasCrossRef(seq1, seq2));
970   
971     // case-insensitive; version number is ignored
972     seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "v20192"));
973     assertTrue(AlignmentUtils.hasCrossRef(seq1, seq2));
974   
975     // right case!
976     seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "V20192"));
977     assertTrue(AlignmentUtils.hasCrossRef(seq1, seq2));
978     // test is one-way only
979     assertFalse(AlignmentUtils.hasCrossRef(seq2, seq1));
980   }
981
982   /**
983    * Tests for the method that checks if either sequence cross-references the
984    * other
985    */
986   @Test(groups ={ "Functional" })
987   public void testHaveCrossRef()
988   {
989     assertFalse(AlignmentUtils.hasCrossRef(null, null));
990     SequenceI seq1 = new Sequence("EMBL|A12345", "ABCDEF");
991     assertFalse(AlignmentUtils.haveCrossRef(seq1, null));
992     assertFalse(AlignmentUtils.haveCrossRef(null, seq1));
993     SequenceI seq2 = new Sequence("UNIPROT|V20192", "ABCDEF");
994     assertFalse(AlignmentUtils.haveCrossRef(seq1, seq2));
995   
996     seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "V20192"));
997     assertTrue(AlignmentUtils.haveCrossRef(seq1, seq2));
998     // next is true for haveCrossRef, false for hasCrossRef
999     assertTrue(AlignmentUtils.haveCrossRef(seq2, seq1));
1000   
1001     // now the other way round
1002     seq1.setDBRef(null);
1003     seq2.addDBRef(new DBRefEntry("EMBL", "1", "A12345"));
1004     assertTrue(AlignmentUtils.haveCrossRef(seq1, seq2));
1005     assertTrue(AlignmentUtils.haveCrossRef(seq2, seq1));
1006   
1007     // now both ways
1008     seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "V20192"));
1009     assertTrue(AlignmentUtils.haveCrossRef(seq1, seq2));
1010     assertTrue(AlignmentUtils.haveCrossRef(seq2, seq1));
1011   }
1012
1013   /**
1014    * Test the method that extracts the exon-only part of a dna alignment.
1015    */
1016   @Test(groups ={ "Functional" })
1017   public void testMakeExonAlignment()
1018   {
1019     SequenceI dna1 = new Sequence("dna1", "aaaGGGcccTTTaaa");
1020     SequenceI dna2 = new Sequence("dna2", "GGGcccTTTaaaCCC");
1021     SequenceI pep1 = new Sequence("pep1", "GF");
1022     SequenceI pep2 = new Sequence("pep2", "GFP");
1023     dna1.createDatasetSequence();
1024     dna2.createDatasetSequence();
1025     pep1.createDatasetSequence();
1026     pep2.createDatasetSequence();
1027
1028     Set<AlignedCodonFrame> mappings = new HashSet<AlignedCodonFrame>();
1029     MapList map = new MapList(new int[]
1030     { 4, 6, 10, 12 }, new int[]
1031     { 1, 2 }, 3, 1);
1032     AlignedCodonFrame acf = new AlignedCodonFrame();
1033     acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map);
1034     mappings.add(acf);
1035     map = new MapList(new int[]
1036     { 1, 3, 7, 9, 13, 15 }, new int[]
1037     { 1, 3 }, 3, 1);
1038     acf = new AlignedCodonFrame();
1039     acf.addMap(dna2.getDatasetSequence(), pep2.getDatasetSequence(), map);
1040     mappings.add(acf);
1041     
1042     AlignmentI exons = AlignmentUtils.makeExonAlignment(new SequenceI[]
1043     { dna1, dna2 }, mappings);
1044     assertEquals(2, exons.getSequences().size());
1045     assertEquals("GGGTTT", exons.getSequenceAt(0).getSequenceAsString());
1046     assertEquals("GGGTTTCCC", exons.getSequenceAt(1).getSequenceAsString());
1047
1048     /*
1049      * Verify updated mappings
1050      */
1051     assertEquals(2, mappings.size());
1052
1053     /*
1054      * Mapping from pep1 to GGGTTT in first new exon sequence
1055      */
1056     List<AlignedCodonFrame> pep1Mapping = MappingUtils
1057             .findMappingsForSequence(pep1, mappings);
1058     assertEquals(1, pep1Mapping.size());
1059     // map G to GGG
1060     SearchResults sr = MappingUtils.buildSearchResults(pep1, 1, mappings);
1061     assertEquals(1, sr.getResults().size());
1062     Match m = sr.getResults().get(0);
1063     assertEquals(exons.getSequenceAt(0).getDatasetSequence(),
1064             m.getSequence());
1065     assertEquals(1, m.getStart());
1066     assertEquals(3, m.getEnd());
1067     // map F to TTT
1068     sr = MappingUtils.buildSearchResults(pep1, 2, mappings);
1069     m = sr.getResults().get(0);
1070     assertEquals(exons.getSequenceAt(0).getDatasetSequence(),
1071             m.getSequence());
1072     assertEquals(4, m.getStart());
1073     assertEquals(6, m.getEnd());
1074
1075     /*
1076      * Mapping from pep2 to GGGTTTCCC in second new exon sequence
1077      */
1078     List<AlignedCodonFrame> pep2Mapping = MappingUtils
1079             .findMappingsForSequence(pep2, mappings);
1080     assertEquals(1, pep2Mapping.size());
1081     // map G to GGG
1082     sr = MappingUtils.buildSearchResults(pep2, 1, mappings);
1083     assertEquals(1, sr.getResults().size());
1084     m = sr.getResults().get(0);
1085     assertEquals(exons.getSequenceAt(1).getDatasetSequence(),
1086             m.getSequence());
1087     assertEquals(1, m.getStart());
1088     assertEquals(3, m.getEnd());
1089     // map F to TTT
1090     sr = MappingUtils.buildSearchResults(pep2, 2, mappings);
1091     m = sr.getResults().get(0);
1092     assertEquals(exons.getSequenceAt(1).getDatasetSequence(),
1093             m.getSequence());
1094     assertEquals(4, m.getStart());
1095     assertEquals(6, m.getEnd());
1096     // map P to CCC
1097     sr = MappingUtils.buildSearchResults(pep2, 3, mappings);
1098     m = sr.getResults().get(0);
1099     assertEquals(exons.getSequenceAt(1).getDatasetSequence(),
1100             m.getSequence());
1101     assertEquals(7, m.getStart());
1102     assertEquals(9, m.getEnd());
1103   }
1104
1105   /**
1106    * Test the method that makes an exon-only sequence from a DNA sequence and
1107    * its product mapping. Test includes the expected case that the DNA sequence
1108    * already has a protein product (Uniprot translation) which in turn has an
1109    * x-ref to the EMBLCDS record.
1110    */
1111   @Test(groups ={ "Functional" })
1112   public void testMakeExonSequences()
1113   {
1114     SequenceI dna1 = new Sequence("dna1", "aaaGGGcccTTTaaa");
1115     SequenceI pep1 = new Sequence("pep1", "GF");
1116     dna1.createDatasetSequence();
1117     pep1.createDatasetSequence();
1118     pep1.getDatasetSequence().addDBRef(
1119             new DBRefEntry("EMBLCDS", "2", "A12345"));
1120
1121     /*
1122      * Make the mapping from dna to protein. The protein sequence has a DBRef to
1123      * EMBLCDS|A12345.
1124      */
1125     Set<AlignedCodonFrame> mappings = new HashSet<AlignedCodonFrame>();
1126     MapList map = new MapList(new int[]
1127     { 4, 6, 10, 12 }, new int[]
1128     { 1, 2 }, 3, 1);
1129     AlignedCodonFrame acf = new AlignedCodonFrame();
1130     acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map);
1131     mappings.add(acf);
1132
1133     AlignedCodonFrame newMapping = new AlignedCodonFrame();
1134     List<SequenceI> exons = AlignmentUtils.makeExonSequences(dna1, acf,
1135             newMapping);
1136     assertEquals(1, exons.size());
1137     SequenceI exon = exons.get(0);
1138
1139     assertEquals("GGGTTT", exon.getSequenceAsString());
1140     assertEquals("dna1|A12345", exon.getName());
1141     assertEquals(1, exon.getDBRef().length);
1142     DBRefEntry cdsRef = exon.getDBRef()[0];
1143     assertEquals("EMBLCDS", cdsRef.getSource());
1144     assertEquals("2", cdsRef.getVersion());
1145     assertEquals("A12345", cdsRef.getAccessionId());
1146   }
1147
1148   /**
1149    * Test the method that makes an exon-only alignment from a DNA sequence and
1150    * its product mappings, for the case where there are multiple exon mappings
1151    * to different protein products.
1152    */
1153   @Test(groups ={ "Functional" })
1154   public void testMakeExonAlignment_multipleProteins()
1155   {
1156     SequenceI dna1 = new Sequence("dna1", "aaaGGGcccTTTaaa");
1157     SequenceI pep1 = new Sequence("pep1", "GF"); // GGGTTT
1158     SequenceI pep2 = new Sequence("pep2", "KP"); // aaaccc
1159     SequenceI pep3 = new Sequence("pep3", "KF"); // aaaTTT
1160     dna1.createDatasetSequence();
1161     pep1.createDatasetSequence();
1162     pep2.createDatasetSequence();
1163     pep3.createDatasetSequence();
1164     pep1.getDatasetSequence().addDBRef(
1165             new DBRefEntry("EMBLCDS", "2", "A12345"));
1166     pep2.getDatasetSequence().addDBRef(
1167             new DBRefEntry("EMBLCDS", "3", "A12346"));
1168     pep3.getDatasetSequence().addDBRef(
1169             new DBRefEntry("EMBLCDS", "4", "A12347"));
1170
1171     /*
1172      * Make the mappings from dna to protein. Using LinkedHashset is a
1173      * convenience so results are in the input order. There is no assertion that
1174      * the generated exon sequences are in any particular order.
1175      */
1176     Set<AlignedCodonFrame> mappings = new LinkedHashSet<AlignedCodonFrame>();
1177     // map ...GGG...TTT to GF
1178     MapList map = new MapList(new int[]
1179     { 4, 6, 10, 12 }, new int[]
1180     { 1, 2 }, 3, 1);
1181     AlignedCodonFrame acf = new AlignedCodonFrame();
1182     acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map);
1183     mappings.add(acf);
1184
1185     // map aaa...ccc to KP
1186     map = new MapList(new int[]
1187     { 1, 3, 7, 9 }, new int[]
1188     { 1, 2 }, 3, 1);
1189     acf = new AlignedCodonFrame();
1190     acf.addMap(dna1.getDatasetSequence(), pep2.getDatasetSequence(), map);
1191     mappings.add(acf);
1192
1193     // map aaa......TTT to KF
1194     map = new MapList(new int[]
1195     { 1, 3, 10, 12 }, new int[]
1196     { 1, 2 }, 3, 1);
1197     acf = new AlignedCodonFrame();
1198     acf.addMap(dna1.getDatasetSequence(), pep3.getDatasetSequence(), map);
1199     mappings.add(acf);
1200
1201     /*
1202      * Create the Exon alignment; also replaces the dna-to-protein mappings with
1203      * exon-to-protein and exon-to-dna mappings
1204      */
1205     AlignmentI exal = AlignmentUtils.makeExonAlignment(new SequenceI[]
1206     { dna1 }, mappings);
1207
1208     /*
1209      * Verify we have 3 exon sequences, mapped to pep1/2/3 respectively
1210      */
1211     List<SequenceI> exons = exal.getSequences();
1212     assertEquals(3, exons.size());
1213
1214     SequenceI exon = exons.get(0);
1215     assertEquals("GGGTTT", exon.getSequenceAsString());
1216     assertEquals("dna1|A12345", exon.getName());
1217     assertEquals(1, exon.getDBRef().length);
1218     DBRefEntry cdsRef = exon.getDBRef()[0];
1219     assertEquals("EMBLCDS", cdsRef.getSource());
1220     assertEquals("2", cdsRef.getVersion());
1221     assertEquals("A12345", cdsRef.getAccessionId());
1222
1223     exon = exons.get(1);
1224     assertEquals("aaaccc", exon.getSequenceAsString());
1225     assertEquals("dna1|A12346", exon.getName());
1226     assertEquals(1, exon.getDBRef().length);
1227     cdsRef = exon.getDBRef()[0];
1228     assertEquals("EMBLCDS", cdsRef.getSource());
1229     assertEquals("3", cdsRef.getVersion());
1230     assertEquals("A12346", cdsRef.getAccessionId());
1231
1232     exon = exons.get(2);
1233     assertEquals("aaaTTT", exon.getSequenceAsString());
1234     assertEquals("dna1|A12347", exon.getName());
1235     assertEquals(1, exon.getDBRef().length);
1236     cdsRef = exon.getDBRef()[0];
1237     assertEquals("EMBLCDS", cdsRef.getSource());
1238     assertEquals("4", cdsRef.getVersion());
1239     assertEquals("A12347", cdsRef.getAccessionId());
1240
1241     /*
1242      * Verify there are mappings from each exon sequence to its protein product
1243      * and also to its dna source
1244      */
1245     Iterator<AlignedCodonFrame> newMappingsIterator = mappings.iterator();
1246
1247     // mappings for dna1 - exon1 - pep1
1248     AlignedCodonFrame exonMapping = newMappingsIterator.next();
1249     List<Mapping> dnaMappings = exonMapping.getMappingsForSequence(dna1);
1250     assertEquals(1, dnaMappings.size());
1251     assertSame(exons.get(0).getDatasetSequence(), dnaMappings.get(0)
1252             .getTo());
1253     assertEquals("G(1) in CDS should map to G(4) in DNA", 4, dnaMappings
1254             .get(0).getMap().getToPosition(1));
1255     List<Mapping> peptideMappings = exonMapping
1256             .getMappingsForSequence(pep1);
1257     assertEquals(1, peptideMappings.size());
1258     assertSame(pep1.getDatasetSequence(), peptideMappings.get(0).getTo());
1259
1260     // mappings for dna1 - exon2 - pep2
1261     exonMapping = newMappingsIterator.next();
1262     dnaMappings = exonMapping.getMappingsForSequence(dna1);
1263     assertEquals(1, dnaMappings.size());
1264     assertSame(exons.get(1).getDatasetSequence(), dnaMappings.get(0)
1265             .getTo());
1266     assertEquals("c(4) in CDS should map to c(7) in DNA", 7, dnaMappings
1267             .get(0).getMap().getToPosition(4));
1268     peptideMappings = exonMapping.getMappingsForSequence(pep2);
1269     assertEquals(1, peptideMappings.size());
1270     assertSame(pep2.getDatasetSequence(), peptideMappings.get(0).getTo());
1271
1272     // mappings for dna1 - exon3 - pep3
1273     exonMapping = newMappingsIterator.next();
1274     dnaMappings = exonMapping.getMappingsForSequence(dna1);
1275     assertEquals(1, dnaMappings.size());
1276     assertSame(exons.get(2).getDatasetSequence(), dnaMappings.get(0)
1277             .getTo());
1278     assertEquals("T(4) in CDS should map to T(10) in DNA", 10, dnaMappings
1279             .get(0).getMap().getToPosition(4));
1280     peptideMappings = exonMapping.getMappingsForSequence(pep3);
1281     assertEquals(1, peptideMappings.size());
1282     assertSame(pep3.getDatasetSequence(), peptideMappings.get(0).getTo());
1283   }
1284
1285   @Test(groups = { "Functional" })
1286   public void testIsMappable()
1287   {
1288     SequenceI dna1 = new Sequence("dna1", "cgCAGtgGT");
1289     SequenceI aa1 = new Sequence("aa1", "RSG");
1290     AlignmentI al1 = new Alignment(new SequenceI[] { dna1 });
1291     AlignmentI al2 = new Alignment(new SequenceI[] { aa1 });
1292
1293     assertFalse(AlignmentUtils.isMappable(null, null));
1294     assertFalse(AlignmentUtils.isMappable(al1, null));
1295     assertFalse(AlignmentUtils.isMappable(null, al1));
1296     assertFalse(AlignmentUtils.isMappable(al1, al1));
1297     assertFalse(AlignmentUtils.isMappable(al2, al2));
1298
1299     assertTrue(AlignmentUtils.isMappable(al1, al2));
1300     assertTrue(AlignmentUtils.isMappable(al2, al1));
1301   }
1302 }