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