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