JAL-1712 fixes/tests for Castor binding and 'show flanking regions'
[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
29 import java.io.IOException;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Collections;
33 import java.util.HashSet;
34 import java.util.Iterator;
35 import java.util.LinkedHashSet;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39
40 import org.junit.Test;
41
42 import jalview.datamodel.AlignedCodonFrame;
43 import jalview.datamodel.Alignment;
44 import jalview.datamodel.AlignmentAnnotation;
45 import jalview.datamodel.AlignmentI;
46 import jalview.datamodel.Annotation;
47 import jalview.datamodel.DBRefEntry;
48 import jalview.datamodel.Mapping;
49 import jalview.datamodel.SearchResults;
50 import jalview.datamodel.SearchResults.Match;
51 import jalview.datamodel.Sequence;
52 import jalview.datamodel.SequenceI;
53 import jalview.io.AppletFormatAdapter;
54 import jalview.io.FormatAdapter;
55 import jalview.util.MapList;
56 import jalview.util.MappingUtils;
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
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", al, true));
108     for (int flnk=-1;flnk<25; flnk++)
109     {
110       AlignmentI exp = AlignmentUtils.expandContext(al, flnk);
111       System.out.println("\nFlank size: " + flnk);
112       System.out.println(new AppletFormatAdapter().formatSequences(
113               "Clustal", exp, true));
114       if (flnk == -1)
115       {
116         /*
117          * Full expansion to complete sequences
118          */
119         for (SequenceI sq : exp.getSequences())
120         {
121           String ung = sq.getSequenceAsString().replaceAll("-+", "");
122           final String errorMsg = "Flanking sequence not the same as original dataset sequence.\n"
123                   + ung
124                   + "\n"
125                   + sq.getDatasetSequence().getSequenceAsString();
126           assertTrue(errorMsg, ung.equalsIgnoreCase(sq.getDatasetSequence()
127                   .getSequenceAsString()));
128         }
129       }
130       else if (flnk == 24)
131       {
132         /*
133          * Last sequence is fully expanded, others have leading gaps to match
134          */
135         assertTrue(exp.getSequenceAt(4).getSequenceAsString()
136                 .startsWith("abc"));
137         assertTrue(exp.getSequenceAt(3).getSequenceAsString()
138                 .startsWith("--abc"));
139         assertTrue(exp.getSequenceAt(2).getSequenceAsString()
140                 .startsWith("----abc"));
141         assertTrue(exp.getSequenceAt(1).getSequenceAsString()
142                 .startsWith("------abc"));
143         assertTrue(exp.getSequenceAt(0).getSequenceAsString()
144                 .startsWith("--------abc"));
145       }
146     }
147   }
148
149   /**
150    * Test that annotations are correctly adjusted by expandContext
151    */
152   @Test
153   public void testExpandContext_annotation()
154   {
155     AlignmentI al = new Alignment(new Sequence[]
156     {});
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[]
166     { new Annotation(4), 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     // FIXME expandContext adds an unnecessary gap; need tests to cover all
199     // cases for which 'maxOffset' is computed
200     assertEquals("-abcDEFghi", expanded.getSequenceAt(0)
201             .getSequenceAsString());
202
203     /*
204      * Confirm the alignment and sequence have the same SS annotation,
205      * referencing the expanded sequence
206      */
207     ann = expanded.getSequenceAt(0).getAnnotation()[0];
208     assertSame(ann, expanded.getAlignmentAnnotation()[0]);
209     assertSame(expanded.getSequenceAt(0), ann.sequenceRef);
210
211     /*
212      * The annotations array should have null values except for annotated
213      * positions
214      */
215     assertNull(ann.annotations[0]);
216     assertNull(ann.annotations[1]);
217     assertNull(ann.annotations[2]);
218     assertNull(ann.annotations[3]);
219     assertEquals(4, ann.annotations[4].value, 0.001);
220     assertEquals(5, ann.annotations[5].value, 0.001);
221     assertEquals(6, ann.annotations[6].value, 0.001);
222     assertNull(ann.annotations[7]);
223     assertNull(ann.annotations[8]);
224     assertNull(ann.annotations[9]);
225
226     /*
227      * sequence position mappings should be unchanged
228      */
229     assertNull(ann.getAnnotationForPosition(1));
230     assertNull(ann.getAnnotationForPosition(2));
231     assertNull(ann.getAnnotationForPosition(3));
232     assertEquals(4, ann.getAnnotationForPosition(4).value, 0.001);
233     assertEquals(5, ann.getAnnotationForPosition(5).value, 0.001);
234     assertEquals(6, ann.getAnnotationForPosition(6).value, 0.001);
235     assertNull(ann.getAnnotationForPosition(7));
236     assertNull(ann.getAnnotationForPosition(8));
237     assertNull(ann.getAnnotationForPosition(9));
238   }
239
240   /**
241    * Test method that returns a map of lists of sequences by sequence name.
242    * 
243    * @throws IOException
244    */
245   @Test
246   public void testGetSequencesByName() throws IOException
247   {
248     final String data = ">Seq1Name\nKQYL\n" + ">Seq2Name\nRFPW\n"
249             + ">Seq1Name\nABCD\n";
250     AlignmentI al = loadAlignment(data, "FASTA");
251     Map<String, List<SequenceI>> map = AlignmentUtils
252             .getSequencesByName(al);
253     assertEquals(2, map.keySet().size());
254     assertEquals(2, map.get("Seq1Name").size());
255     assertEquals("KQYL", map.get("Seq1Name").get(0).getSequenceAsString());
256     assertEquals("ABCD", map.get("Seq1Name").get(1).getSequenceAsString());
257     assertEquals(1, map.get("Seq2Name").size());
258     assertEquals("RFPW", map.get("Seq2Name").get(0).getSequenceAsString());
259   }
260   /**
261    * Helper method to load an alignment and ensure dataset sequences are set up.
262    * 
263    * @param data
264    * @param format TODO
265    * @return
266    * @throws IOException
267    */
268   protected AlignmentI loadAlignment(final String data, String format) throws IOException
269   {
270     Alignment 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
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(
311             protein.getSequenceAt(0)).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[]
321     { 1, 9 }, mapList.getFromRanges().get(0)));
322     assertEquals(1, mapList.getFromRanges().size());
323     assertTrue(Arrays.equals(new int[]
324     { 1, 3 }, 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
347   public void testAlignSequenceAs_withMapping_noIntrons()
348   {
349     MapList map = new MapList(new int[]
350     { 1, 6 }, new int[]
351     { 1, 2 }, 3, 1);
352
353     /*
354      * No existing gaps in dna:
355      */
356     checkAlignSequenceAs("GGGAAA", "-A-L-", false, false, map,
357             "---GGG---AAA");
358
359     /*
360      * Now introduce gaps in dna but ignore them when realigning.
361      */
362     checkAlignSequenceAs("-G-G-G-A-A-A-", "-A-L-", false, false, map,
363             "---GGG---AAA");
364
365     /*
366      * Now include gaps in dna when realigning. First retaining 'mapped' gaps
367      * only, i.e. those within the exon region.
368      */
369     checkAlignSequenceAs("-G-G--G-A--A-A-", "-A-L-", true, false, map,
370             "---G-G--G---A--A-A");
371
372     /*
373      * Include all gaps in dna when realigning (within and without the exon
374      * region). The leading gap, and the gaps between codons, are subsumed by
375      * the protein alignment gap.
376      */
377     checkAlignSequenceAs("-G-GG--AA-A-", "-A-L-", true, true, map,
378             "---G-GG---AA-A-");
379
380     /*
381      * Include only unmapped gaps in dna when realigning (outside the exon
382      * region). The leading gap, and the gaps between codons, are subsumed by
383      * the protein alignment gap.
384      */
385     checkAlignSequenceAs("-G-GG--AA-A-", "-A-L-", false, true, map,
386             "---GGG---AAA-");
387   }
388
389   /**
390    * Test for the alignSequenceAs method that takes two sequences and a mapping.
391    */
392   @Test
393   public void testAlignSequenceAs_withMapping_withIntrons()
394   {
395     /*
396      * Exons at codon 2 (AAA) and 4 (TTT)
397      */
398     MapList map = new MapList(new int[]
399     { 4, 6, 10, 12 }, new int[]
400     { 1, 2 }, 3, 1);
401
402     /*
403      * Simple case: no gaps in dna
404      */
405     checkAlignSequenceAs("GGGAAACCCTTTGGG", "--A-L-", false, false, map,
406             "GGG---AAACCCTTTGGG");
407
408     /*
409      * Add gaps to dna - but ignore when realigning.
410      */
411     checkAlignSequenceAs("-G-G-G--A--A---AC-CC-T-TT-GG-G-", "--A-L-",
412             false, false, map, "GGG---AAACCCTTTGGG");
413
414     /*
415      * Add gaps to dna - include within exons only when realigning.
416      */
417     checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
418             true, false, map, "GGG---A--A---ACCCT-TTGGG");
419
420     /*
421      * Include gaps outside exons only when realigning.
422      */
423     checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
424             false, true, map, "-G-G-GAAAC-CCTTT-GG-G-");
425
426     /*
427      * Include gaps following first intron if we are 'preserving mapped gaps'
428      */
429     checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
430             true, true, map, "-G-G-G--A--A---A-C-CC-T-TT-GG-G-");
431
432     /*
433      * Include all gaps in dna when realigning.
434      */
435     checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
436             true, true, map, "-G-G-G--A--A---A-C-CC-T-TT-GG-G-");
437   }
438
439   /**
440    * Test for the case where not all of the protein sequence is mapped to cDNA.
441    */
442   @Test
443   public void testAlignSequenceAs_withMapping_withUnmappedProtein()
444   {
445     
446     /*
447      * Exons at codon 2 (AAA) and 4 (TTT) mapped to A and P
448      */
449     final MapList map = new MapList(new int[]
450     { 4, 6, 10, 12 }, new int[]
451     { 1, 1, 3, 3 }, 3, 1);
452     
453
454     /*
455      * Expect alignment does nothing (aborts realignment). Change this test
456      * first if different behaviour wanted.
457      */
458     checkAlignSequenceAs("GGGAAACCCTTTGGG", "-A-L-P-", false,
459             false, map, "GGGAAACCCTTTGGG");
460   }
461
462   /**
463    * Helper method that performs and verifies the method under test.
464    * 
465    * @param dnaSeq
466    * @param proteinSeq
467    * @param preserveMappedGaps
468    * @param preserveUnmappedGaps
469    * @param map
470    * @param expected
471    */
472   protected void checkAlignSequenceAs(final String dnaSeq,
473           final String proteinSeq, final boolean preserveMappedGaps,
474           final boolean preserveUnmappedGaps, MapList map,
475           final String expected)
476   {
477     SequenceI dna = new Sequence("Seq1", dnaSeq);
478     dna.createDatasetSequence();
479     SequenceI protein = new Sequence("Seq1", proteinSeq);
480     protein.createDatasetSequence();
481     AlignedCodonFrame acf = new AlignedCodonFrame();
482     acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map);
483
484     AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-',
485             preserveMappedGaps, preserveUnmappedGaps);
486     assertEquals(expected, dna.getSequenceAsString());
487   }
488
489   /**
490    * Test for the alignSequenceAs method where we preserve gaps in introns only.
491    */
492   @Test
493   public void testAlignSequenceAs_keepIntronGapsOnly()
494   {
495
496     /*
497      * Intron GGGAAA followed by exon CCCTTT
498      */
499     MapList map = new MapList(new int[]
500     { 7, 12 }, new int[]
501     { 1, 2 }, 3, 1);
502     
503     checkAlignSequenceAs("GG-G-AA-A-C-CC-T-TT", "AL",
504             false, true, map, "GG-G-AA-ACCCTTT");
505   }
506
507   /**
508    * Test for the method that generates an aligned translated sequence from one
509    * mapping.
510    */
511   @Test
512   public void testGetAlignedTranslation_dnaLikeProtein()
513   {
514     // dna alignment will be replaced
515     SequenceI dna = new Sequence("Seq1", "T-G-CC-A--T-TAC-CAG-");
516     dna.createDatasetSequence();
517     // protein alignment will be 'applied' to dna
518     SequenceI protein = new Sequence("Seq1", "-CH-Y--Q-");
519     protein.createDatasetSequence();
520     MapList map = new MapList(new int[]
521     { 1, 12 }, new int[]
522     { 1, 4 }, 3, 1);
523     AlignedCodonFrame acf = new AlignedCodonFrame();
524     acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map);
525
526     final SequenceI aligned = AlignmentUtils
527                 .getAlignedTranslation(protein, '-', acf);
528     assertEquals("---TGCCAT---TAC------CAG---", aligned.getSequenceAsString());
529     assertSame(aligned.getDatasetSequence(), dna.getDatasetSequence());
530   }
531
532   /**
533    * Test the method that realigns protein to match mapped codon alignment.
534    */
535   @Test
536   public void testAlignProteinAsDna()
537   {
538     // seq1 codons are [1,2,3] [4,5,6] [7,8,9] [10,11,12]
539     SequenceI dna1 = new Sequence("Seq1", "TGCCATTACCAG-");
540     // seq2 codons are [1,3,4] [5,6,7] [8,9,10] [11,12,13]
541     SequenceI dna2 = new Sequence("Seq2", "T-GCCATTACCAG");
542     // seq3 codons are [1,2,3] [4,5,7] [8,9,10] [11,12,13]
543     SequenceI dna3 = new Sequence("Seq3", "TGCCA-TTACCAG");
544     AlignmentI dna = new Alignment(new SequenceI[]
545     { dna1, dna2, dna3 });
546     dna.setDataset(null);
547
548     // protein alignment will be realigned like dna
549     SequenceI prot1 = new Sequence("Seq1", "CHYQ");
550     SequenceI prot2 = new Sequence("Seq2", "CHYQ");
551     SequenceI prot3 = new Sequence("Seq3", "CHYQ");
552     AlignmentI protein = new Alignment(new SequenceI[]
553     { prot1, prot2, prot3 });
554     protein.setDataset(null);
555
556     MapList map = new MapList(new int[]
557     { 1, 12 }, new int[]
558     { 1, 4 }, 3, 1);
559     AlignedCodonFrame acf = new AlignedCodonFrame();
560     acf.addMap(dna1.getDatasetSequence(), prot1.getDatasetSequence(), map);
561     acf.addMap(dna2.getDatasetSequence(), prot2.getDatasetSequence(), map);
562     acf.addMap(dna3.getDatasetSequence(), prot3.getDatasetSequence(), map);
563     protein.setCodonFrames(Collections.singleton(acf));
564
565     /*
566      * Translated codon order is [1,2,3] [1,3,4] [4,5,6] [4,5,7] [5,6,7] [7,8,9]
567      * [8,9,10] [10,11,12] [11,12,13]
568      */
569     AlignmentUtils.alignProteinAsDna(protein, dna);
570     assertEquals("C-H--Y-Q-", prot1.getSequenceAsString());
571     assertEquals("-C--H-Y-Q", prot2.getSequenceAsString());
572     assertEquals("C--H--Y-Q", prot3.getSequenceAsString());
573   }
574
575   /**
576    * Test the method that tests whether a CDNA sequence translates to a protein
577    * sequence
578    */
579   @Test
580   public void testTranslatesAs()
581   {
582     assertTrue(AlignmentUtils.translatesAs("tttcccaaaggg".toCharArray(), 0,
583             "FPKG".toCharArray()));
584     // with start codon
585     assertTrue(AlignmentUtils.translatesAs("atgtttcccaaaggg".toCharArray(),
586             3, "FPKG".toCharArray()));
587     // with stop codon1
588     assertTrue(AlignmentUtils.translatesAs("tttcccaaagggtaa".toCharArray(),
589             0, "FPKG".toCharArray()));
590     // with stop codon2
591     assertTrue(AlignmentUtils.translatesAs("tttcccaaagggtag".toCharArray(),
592             0, "FPKG".toCharArray()));
593     // with stop codon3
594     assertTrue(AlignmentUtils.translatesAs("tttcccaaagggtga".toCharArray(),
595             0, "FPKG".toCharArray()));
596     // with start and stop codon1
597     assertTrue(AlignmentUtils.translatesAs(
598             "atgtttcccaaaggtaa".toCharArray(), 3, "FPKG".toCharArray()));
599     // with start and stop codon2
600     assertTrue(AlignmentUtils.translatesAs(
601             "atgtttcccaaaggtag".toCharArray(), 3, "FPKG".toCharArray()));
602     // with start and stop codon3
603     assertTrue(AlignmentUtils.translatesAs(
604             "atgtttcccaaaggtga".toCharArray(), 3, "FPKG".toCharArray()));
605
606     // wrong protein
607     assertFalse(AlignmentUtils.translatesAs("tttcccaaaggg".toCharArray(),
608             0,
609             "FPMG".toCharArray()));
610   }
611
612   /**
613    * Test mapping of protein to cDNA, for cases where the cDNA has start and/or
614    * stop codons in addition to the protein coding sequence.
615    * 
616    * @throws IOException
617    */
618   @Test
619   public void testMapProteinToCdna_withStartAndStopCodons()
620           throws IOException
621   {
622     List<SequenceI> protseqs = new ArrayList<SequenceI>();
623     protseqs.add(new Sequence("UNIPROT|V12345", "EIQ"));
624     protseqs.add(new Sequence("UNIPROT|V12346", "EIQ"));
625     protseqs.add(new Sequence("UNIPROT|V12347", "SAR"));
626     AlignmentI protein = new Alignment(protseqs.toArray(new SequenceI[3]));
627     protein.setDataset(null);
628   
629     List<SequenceI> dnaseqs = new ArrayList<SequenceI>();
630     // start + SAR:
631     dnaseqs.add(new Sequence("EMBL|A11111", "ATGTCAGCACGC"));
632     // = EIQ + stop
633     dnaseqs.add(new Sequence("EMBL|A22222", "GAGATACAATAA"));
634     // = start +EIQ + stop
635     dnaseqs.add(new Sequence("EMBL|A33333", "ATGGAAATCCAGTAG"));
636     dnaseqs.add(new Sequence("EMBL|A44444", "GAAATTCAG"));
637     AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[4]));
638     cdna.setDataset(null);
639   
640     assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna));
641
642     // 3 mappings made, each from 1 to 1 sequence
643     assertEquals(3, protein.getCodonFrames().size());
644     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size());
645     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size());
646     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(2)).size());
647   
648     // V12345 mapped from A22222
649     AlignedCodonFrame acf = protein.getCodonFrame(
650             protein.getSequenceAt(0)).get(0);
651     assertEquals(1, acf.getdnaSeqs().length);
652     assertEquals(cdna.getSequenceAt(1).getDatasetSequence(),
653             acf.getdnaSeqs()[0]);
654     Mapping[] protMappings = acf.getProtMappings();
655     assertEquals(1, protMappings.length);
656     MapList mapList = protMappings[0].getMap();
657     assertEquals(3, mapList.getFromRatio());
658     assertEquals(1, mapList.getToRatio());
659     assertTrue(Arrays.equals(new int[]
660     { 1, 9 }, mapList.getFromRanges().get(0)));
661     assertEquals(1, mapList.getFromRanges().size());
662     assertTrue(Arrays.equals(new int[]
663     { 1, 3 }, mapList.getToRanges().get(0)));
664     assertEquals(1, mapList.getToRanges().size());
665
666     // V12346 mapped from A33333 starting position 4
667     acf = protein.getCodonFrame(protein.getSequenceAt(1)).get(0);
668     assertEquals(1, acf.getdnaSeqs().length);
669     assertEquals(cdna.getSequenceAt(2).getDatasetSequence(),
670             acf.getdnaSeqs()[0]);
671     protMappings = acf.getProtMappings();
672     assertEquals(1, protMappings.length);
673     mapList = protMappings[0].getMap();
674     assertEquals(3, mapList.getFromRatio());
675     assertEquals(1, mapList.getToRatio());
676     assertTrue(Arrays.equals(new int[]
677     { 4, 12 }, mapList.getFromRanges().get(0)));
678     assertEquals(1, mapList.getFromRanges().size());
679     assertTrue(Arrays.equals(new int[]
680     { 1, 3 }, mapList.getToRanges().get(0)));
681     assertEquals(1, mapList.getToRanges().size());
682   
683     // V12347 mapped to A11111 starting position 4
684     acf = protein.getCodonFrame(protein.getSequenceAt(2)).get(0);
685     assertEquals(1, acf.getdnaSeqs().length);
686     assertEquals(cdna.getSequenceAt(0).getDatasetSequence(),
687             acf.getdnaSeqs()[0]);
688     protMappings = acf.getProtMappings();
689     assertEquals(1, protMappings.length);
690     mapList = protMappings[0].getMap();
691     assertEquals(3, mapList.getFromRatio());
692     assertEquals(1, mapList.getToRatio());
693     assertTrue(Arrays.equals(new int[]
694     { 4, 12 }, mapList.getFromRanges().get(0)));
695     assertEquals(1, mapList.getFromRanges().size());
696     assertTrue(Arrays.equals(new int[]
697     { 1, 3 }, mapList.getToRanges().get(0)));
698     assertEquals(1, mapList.getToRanges().size());
699   
700     // no mapping involving the 'extra' A44444
701     assertTrue(protein.getCodonFrame(cdna.getSequenceAt(3)).isEmpty());
702   }
703
704   /**
705    * Test mapping of protein to cDNA, for the case where we have some sequence
706    * cross-references. Verify that 1-to-many mappings are made where
707    * cross-references exist and sequences are mappable.
708    * 
709    * @throws IOException
710    */
711   @Test
712   public void testMapProteinToCdna_withXrefs() throws IOException
713   {
714     List<SequenceI> protseqs = new ArrayList<SequenceI>();
715     protseqs.add(new Sequence("UNIPROT|V12345", "EIQ"));
716     protseqs.add(new Sequence("UNIPROT|V12346", "EIQ"));
717     protseqs.add(new Sequence("UNIPROT|V12347", "SAR"));
718     AlignmentI protein = new Alignment(protseqs.toArray(new SequenceI[3]));
719     protein.setDataset(null);
720   
721     List<SequenceI> dnaseqs = new ArrayList<SequenceI>();
722     dnaseqs.add(new Sequence("EMBL|A11111", "TCAGCACGC")); // = SAR
723     dnaseqs.add(new Sequence("EMBL|A22222", "ATGGAGATACAA")); // = start + EIQ
724     dnaseqs.add(new Sequence("EMBL|A33333", "GAAATCCAG")); // = EIQ
725     dnaseqs.add(new Sequence("EMBL|A44444", "GAAATTCAG")); // = EIQ
726     dnaseqs.add(new Sequence("EMBL|A55555", "GAGATTCAG")); // = EIQ
727     AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[5]));
728     cdna.setDataset(null);
729   
730     // Xref A22222 to V12345 (should get mapped)
731     dnaseqs.get(1).addDBRef(new DBRefEntry("UNIPROT", "1", "V12345"));
732     // Xref V12345 to A44444 (should get mapped)
733     protseqs.get(0).addDBRef(new DBRefEntry("EMBL", "1", "A44444"));
734     // Xref A33333 to V12347 (sequence mismatch - should not get mapped)
735     dnaseqs.get(2).addDBRef(new DBRefEntry("UNIPROT", "1", "V12347"));
736     // as V12345 is mapped to A22222 and A44444, this leaves V12346 unmapped.
737     // it should get paired up with the unmapped A33333
738     // A11111 should be mapped to V12347
739     // A55555 is spare and has no xref so is not mapped
740
741     assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna));
742
743     // 4 protein mappings made for 3 proteins, 2 to V12345, 1 each to V12346/7
744     assertEquals(3, protein.getCodonFrames().size());
745     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size());
746     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size());
747     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(2)).size());
748
749     // one mapping for each of the first 4 cDNA sequences
750     assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(0)).size());
751     assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(1)).size());
752     assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(2)).size());
753     assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(3)).size());
754   
755     // V12345 mapped to A22222 and A44444
756     AlignedCodonFrame acf = protein.getCodonFrame(
757             protein.getSequenceAt(0)).get(0);
758     assertEquals(2, acf.getdnaSeqs().length);
759     assertEquals(cdna.getSequenceAt(1).getDatasetSequence(),
760             acf.getdnaSeqs()[0]);
761     assertEquals(cdna.getSequenceAt(3).getDatasetSequence(),
762             acf.getdnaSeqs()[1]);
763   
764     // V12346 mapped to A33333
765     acf = protein.getCodonFrame(protein.getSequenceAt(1)).get(0);
766     assertEquals(1, acf.getdnaSeqs().length);
767     assertEquals(cdna.getSequenceAt(2).getDatasetSequence(),
768             acf.getdnaSeqs()[0]);
769   
770     // V12347 mapped to A11111
771     acf = protein.getCodonFrame(protein.getSequenceAt(2)).get(0);
772     assertEquals(1, acf.getdnaSeqs().length);
773     assertEquals(cdna.getSequenceAt(0).getDatasetSequence(),
774             acf.getdnaSeqs()[0]);
775   
776     // no mapping involving the 'extra' A55555
777     assertTrue(protein.getCodonFrame(cdna.getSequenceAt(4)).isEmpty());
778   }
779
780   /**
781    * Test mapping of protein to cDNA, for the case where we have some sequence
782    * cross-references. Verify that once we have made an xref mapping we don't
783    * also map un-xrefd sequeces.
784    * 
785    * @throws IOException
786    */
787   @Test
788   public void testMapProteinToCdna_prioritiseXrefs() throws IOException
789   {
790     List<SequenceI> protseqs = new ArrayList<SequenceI>();
791     protseqs.add(new Sequence("UNIPROT|V12345", "EIQ"));
792     protseqs.add(new Sequence("UNIPROT|V12346", "EIQ"));
793     AlignmentI protein = new Alignment(
794             protseqs.toArray(new SequenceI[protseqs.size()]));
795     protein.setDataset(null);
796   
797     List<SequenceI> dnaseqs = new ArrayList<SequenceI>();
798     dnaseqs.add(new Sequence("EMBL|A11111", "GAAATCCAG")); // = EIQ
799     dnaseqs.add(new Sequence("EMBL|A22222", "GAAATTCAG")); // = EIQ
800     AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[dnaseqs
801             .size()]));
802     cdna.setDataset(null);
803   
804     // Xref A22222 to V12345 (should get mapped)
805     // A11111 should then be mapped to the unmapped V12346
806     dnaseqs.get(1).addDBRef(new DBRefEntry("UNIPROT", "1", "V12345"));
807   
808     assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna));
809   
810     // 2 protein mappings made
811     assertEquals(2, protein.getCodonFrames().size());
812     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size());
813     assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size());
814   
815     // one mapping for each of the cDNA sequences
816     assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(0)).size());
817     assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(1)).size());
818   
819     // V12345 mapped to A22222
820     AlignedCodonFrame acf = protein.getCodonFrame(protein.getSequenceAt(0))
821             .get(0);
822     assertEquals(1, acf.getdnaSeqs().length);
823     assertEquals(cdna.getSequenceAt(1).getDatasetSequence(),
824             acf.getdnaSeqs()[0]);
825   
826     // V12346 mapped to A11111
827     acf = protein.getCodonFrame(protein.getSequenceAt(1)).get(0);
828     assertEquals(1, acf.getdnaSeqs().length);
829     assertEquals(cdna.getSequenceAt(0).getDatasetSequence(),
830             acf.getdnaSeqs()[0]);
831   }
832
833   /**
834    * Test the method that shows or hides sequence annotations by type(s) and
835    * selection group.
836    */
837   @Test
838   public void testShowOrHideSequenceAnnotations()
839   {
840     SequenceI seq1 = new Sequence("Seq1", "AAA");
841     SequenceI seq2 = new Sequence("Seq2", "BBB");
842     SequenceI seq3 = new Sequence("Seq3", "CCC");
843     Annotation[] anns = new Annotation[]
844     { 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
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
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
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[]
1018     { 4, 6, 10, 12 }, new int[]
1019     { 1, 2 }, 3, 1);
1020     AlignedCodonFrame acf = new AlignedCodonFrame();
1021     acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map);
1022     mappings.add(acf);
1023     map = new MapList(new int[]
1024     { 1, 3, 7, 9, 13, 15 }, new int[]
1025     { 1, 3 }, 3, 1);
1026     acf = new AlignedCodonFrame();
1027     acf.addMap(dna2.getDatasetSequence(), pep2.getDatasetSequence(), map);
1028     mappings.add(acf);
1029     
1030     AlignmentI exons = AlignmentUtils.makeExonAlignment(new SequenceI[]
1031     { dna1, dna2 }, mappings);
1032     assertEquals(2, exons.getSequences().size());
1033     assertEquals("GGGTTT", exons.getSequenceAt(0).getSequenceAsString());
1034     assertEquals("GGGTTTCCC", exons.getSequenceAt(1).getSequenceAsString());
1035
1036     /*
1037      * Verify updated mappings
1038      */
1039     assertEquals(2, mappings.size());
1040
1041     /*
1042      * Mapping from pep1 to GGGTTT in first new exon sequence
1043      */
1044     List<AlignedCodonFrame> pep1Mapping = MappingUtils
1045             .findMappingsForSequence(pep1, mappings);
1046     assertEquals(1, pep1Mapping.size());
1047     // map G to GGG
1048     SearchResults sr = MappingUtils.buildSearchResults(pep1, 1, mappings);
1049     assertEquals(1, sr.getResults().size());
1050     Match m = sr.getResults().get(0);
1051     assertEquals(exons.getSequenceAt(0).getDatasetSequence(),
1052             m.getSequence());
1053     assertEquals(1, m.getStart());
1054     assertEquals(3, m.getEnd());
1055     // map F to TTT
1056     sr = MappingUtils.buildSearchResults(pep1, 2, mappings);
1057     m = sr.getResults().get(0);
1058     assertEquals(exons.getSequenceAt(0).getDatasetSequence(),
1059             m.getSequence());
1060     assertEquals(4, m.getStart());
1061     assertEquals(6, m.getEnd());
1062
1063     /*
1064      * Mapping from pep2 to GGGTTTCCC in second new exon sequence
1065      */
1066     List<AlignedCodonFrame> pep2Mapping = MappingUtils
1067             .findMappingsForSequence(pep2, mappings);
1068     assertEquals(1, pep2Mapping.size());
1069     // map G to GGG
1070     sr = MappingUtils.buildSearchResults(pep2, 1, mappings);
1071     assertEquals(1, sr.getResults().size());
1072     m = sr.getResults().get(0);
1073     assertEquals(exons.getSequenceAt(1).getDatasetSequence(),
1074             m.getSequence());
1075     assertEquals(1, m.getStart());
1076     assertEquals(3, m.getEnd());
1077     // map F to TTT
1078     sr = MappingUtils.buildSearchResults(pep2, 2, mappings);
1079     m = sr.getResults().get(0);
1080     assertEquals(exons.getSequenceAt(1).getDatasetSequence(),
1081             m.getSequence());
1082     assertEquals(4, m.getStart());
1083     assertEquals(6, m.getEnd());
1084     // map P to CCC
1085     sr = MappingUtils.buildSearchResults(pep2, 3, mappings);
1086     m = sr.getResults().get(0);
1087     assertEquals(exons.getSequenceAt(1).getDatasetSequence(),
1088             m.getSequence());
1089     assertEquals(7, m.getStart());
1090     assertEquals(9, m.getEnd());
1091   }
1092
1093   /**
1094    * Test the method that makes an exon-only sequence from a DNA sequence and
1095    * its product mapping. Test includes the expected case that the DNA sequence
1096    * already has a protein product (Uniprot translation) which in turn has an
1097    * x-ref to the EMBLCDS record.
1098    */
1099   @Test
1100   public void testMakeExonSequences()
1101   {
1102     SequenceI dna1 = new Sequence("dna1", "aaaGGGcccTTTaaa");
1103     SequenceI pep1 = new Sequence("pep1", "GF");
1104     dna1.createDatasetSequence();
1105     pep1.createDatasetSequence();
1106     pep1.getDatasetSequence().addDBRef(
1107             new DBRefEntry("EMBLCDS", "2", "A12345"));
1108
1109     /*
1110      * Make the mapping from dna to protein. The protein sequence has a DBRef to
1111      * EMBLCDS|A12345.
1112      */
1113     Set<AlignedCodonFrame> mappings = new HashSet<AlignedCodonFrame>();
1114     MapList map = new MapList(new int[]
1115     { 4, 6, 10, 12 }, new int[]
1116     { 1, 2 }, 3, 1);
1117     AlignedCodonFrame acf = new AlignedCodonFrame();
1118     acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map);
1119     mappings.add(acf);
1120
1121     AlignedCodonFrame newMapping = new AlignedCodonFrame();
1122     List<SequenceI> exons = AlignmentUtils.makeExonSequences(dna1, acf,
1123             newMapping);
1124     assertEquals(1, exons.size());
1125     SequenceI exon = exons.get(0);
1126
1127     assertEquals("GGGTTT", exon.getSequenceAsString());
1128     assertEquals("dna1|A12345", exon.getName());
1129     assertEquals(1, exon.getDBRef().length);
1130     DBRefEntry cdsRef = exon.getDBRef()[0];
1131     assertEquals("EMBLCDS", cdsRef.getSource());
1132     assertEquals("2", cdsRef.getVersion());
1133     assertEquals("A12345", cdsRef.getAccessionId());
1134   }
1135
1136   /**
1137    * Test the method that makes an exon-only alignment from a DNA sequence and
1138    * its product mappings, for the case where there are multiple exon mappings
1139    * to different protein products.
1140    */
1141   @Test
1142   public void testMakeExonAlignment_multipleProteins()
1143   {
1144     SequenceI dna1 = new Sequence("dna1", "aaaGGGcccTTTaaa");
1145     SequenceI pep1 = new Sequence("pep1", "GF"); // GGGTTT
1146     SequenceI pep2 = new Sequence("pep2", "KP"); // aaaccc
1147     SequenceI pep3 = new Sequence("pep3", "KF"); // aaaTTT
1148     dna1.createDatasetSequence();
1149     pep1.createDatasetSequence();
1150     pep2.createDatasetSequence();
1151     pep3.createDatasetSequence();
1152     pep1.getDatasetSequence().addDBRef(
1153             new DBRefEntry("EMBLCDS", "2", "A12345"));
1154     pep2.getDatasetSequence().addDBRef(
1155             new DBRefEntry("EMBLCDS", "3", "A12346"));
1156     pep3.getDatasetSequence().addDBRef(
1157             new DBRefEntry("EMBLCDS", "4", "A12347"));
1158
1159     /*
1160      * Make the mappings from dna to protein. Using LinkedHashset is a
1161      * convenience so results are in the input order. There is no assertion that
1162      * the generated exon sequences are in any particular order.
1163      */
1164     Set<AlignedCodonFrame> mappings = new LinkedHashSet<AlignedCodonFrame>();
1165     // map ...GGG...TTT to GF
1166     MapList map = new MapList(new int[]
1167     { 4, 6, 10, 12 }, new int[]
1168     { 1, 2 }, 3, 1);
1169     AlignedCodonFrame acf = new AlignedCodonFrame();
1170     acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map);
1171     mappings.add(acf);
1172
1173     // map aaa...ccc to KP
1174     map = new MapList(new int[]
1175     { 1, 3, 7, 9 }, new int[]
1176     { 1, 2 }, 3, 1);
1177     acf = new AlignedCodonFrame();
1178     acf.addMap(dna1.getDatasetSequence(), pep2.getDatasetSequence(), map);
1179     mappings.add(acf);
1180
1181     // map aaa......TTT to KF
1182     map = new MapList(new int[]
1183     { 1, 3, 10, 12 }, new int[]
1184     { 1, 2 }, 3, 1);
1185     acf = new AlignedCodonFrame();
1186     acf.addMap(dna1.getDatasetSequence(), pep3.getDatasetSequence(), map);
1187     mappings.add(acf);
1188
1189     /*
1190      * Create the Exon alignment; also replaces the dna-to-protein mappings with
1191      * exon-to-protein and exon-to-dna mappings
1192      */
1193     AlignmentI exal = AlignmentUtils.makeExonAlignment(new SequenceI[]
1194     { dna1 }, mappings);
1195
1196     /*
1197      * Verify we have 3 exon sequences, mapped to pep1/2/3 respectively
1198      */
1199     List<SequenceI> exons = exal.getSequences();
1200     assertEquals(3, exons.size());
1201
1202     SequenceI exon = exons.get(0);
1203     assertEquals("GGGTTT", exon.getSequenceAsString());
1204     assertEquals("dna1|A12345", exon.getName());
1205     assertEquals(1, exon.getDBRef().length);
1206     DBRefEntry cdsRef = exon.getDBRef()[0];
1207     assertEquals("EMBLCDS", cdsRef.getSource());
1208     assertEquals("2", cdsRef.getVersion());
1209     assertEquals("A12345", cdsRef.getAccessionId());
1210
1211     exon = exons.get(1);
1212     assertEquals("aaaccc", exon.getSequenceAsString());
1213     assertEquals("dna1|A12346", exon.getName());
1214     assertEquals(1, exon.getDBRef().length);
1215     cdsRef = exon.getDBRef()[0];
1216     assertEquals("EMBLCDS", cdsRef.getSource());
1217     assertEquals("3", cdsRef.getVersion());
1218     assertEquals("A12346", cdsRef.getAccessionId());
1219
1220     exon = exons.get(2);
1221     assertEquals("aaaTTT", exon.getSequenceAsString());
1222     assertEquals("dna1|A12347", exon.getName());
1223     assertEquals(1, exon.getDBRef().length);
1224     cdsRef = exon.getDBRef()[0];
1225     assertEquals("EMBLCDS", cdsRef.getSource());
1226     assertEquals("4", cdsRef.getVersion());
1227     assertEquals("A12347", cdsRef.getAccessionId());
1228
1229     /*
1230      * Verify there are mappings from each exon sequence to its protein product
1231      * and also to its dna source
1232      */
1233     Iterator<AlignedCodonFrame> newMappingsIterator = mappings.iterator();
1234
1235     // mappings for dna1 - exon1 - pep1
1236     AlignedCodonFrame exonMapping = newMappingsIterator.next();
1237     List<Mapping> dnaMappings = exonMapping.getMappingsForSequence(dna1);
1238     assertEquals(1, dnaMappings.size());
1239     assertSame(exons.get(0).getDatasetSequence(), dnaMappings.get(0)
1240             .getTo());
1241     assertEquals("G(1) in CDS should map to G(4) in DNA", 4, dnaMappings
1242             .get(0).getMap().getToPosition(1));
1243     List<Mapping> peptideMappings = exonMapping
1244             .getMappingsForSequence(pep1);
1245     assertEquals(1, peptideMappings.size());
1246     assertSame(pep1.getDatasetSequence(), peptideMappings.get(0).getTo());
1247
1248     // mappings for dna1 - exon2 - pep2
1249     exonMapping = newMappingsIterator.next();
1250     dnaMappings = exonMapping.getMappingsForSequence(dna1);
1251     assertEquals(1, dnaMappings.size());
1252     assertSame(exons.get(1).getDatasetSequence(), dnaMappings.get(0)
1253             .getTo());
1254     assertEquals("c(4) in CDS should map to c(7) in DNA", 7, dnaMappings
1255             .get(0).getMap().getToPosition(4));
1256     peptideMappings = exonMapping.getMappingsForSequence(pep2);
1257     assertEquals(1, peptideMappings.size());
1258     assertSame(pep2.getDatasetSequence(), peptideMappings.get(0).getTo());
1259
1260     // mappings for dna1 - exon3 - pep3
1261     exonMapping = newMappingsIterator.next();
1262     dnaMappings = exonMapping.getMappingsForSequence(dna1);
1263     assertEquals(1, dnaMappings.size());
1264     assertSame(exons.get(2).getDatasetSequence(), dnaMappings.get(0)
1265             .getTo());
1266     assertEquals("T(4) in CDS should map to T(10) in DNA", 10, dnaMappings
1267             .get(0).getMap().getToPosition(4));
1268     peptideMappings = exonMapping.getMappingsForSequence(pep3);
1269     assertEquals(1, peptideMappings.size());
1270     assertSame(pep3.getDatasetSequence(), peptideMappings.get(0).getTo());
1271   }
1272 }