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