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